Reference
The rules
Every tool's inputSchema is read against a client profile, the definitions are costed in tokens, and the tool text is checked for hostile patterns. Each finding carries a stable code, a severity, and a location.
How a target is checked
The pipeline is static and has no rendering step. A saved payload never starts a server; a server command is spoken to over stdio and then closed.
-
Read the target
A saved
tools/listpayload, anmcp.jsonconfig, or a server command. The payload is accepted as an array, a{"tools":[...]}object, or a rawtools/listresponse. -
Pick a profile
The client profile decides which names, root shapes, and keywords are valid.
claudeis the default;openai,gemini,foundry,copilot, andgenericare available. -
Run the rules
Schema rules, description rules, safety rules, and cost rules emit issues, each with a severity and a location such as
read file:inputSchema.properties.path. -
Report
Print text or stable JSON, write SARIF 2.1.0, and exit
0,1,2, or3for CI.
The rule codes
| Code | Severity | What it catches |
|---|---|---|
MCP-001 | error | Invalid or missing tool name; the profile's allowed characters and length limit are enforced. |
MCP-002 | error | Duplicate tool name; clients collapse duplicates and may call the wrong tool. |
MCP-003 | error | Missing or non-object inputSchema; clients require one. |
MCP-004 | error | Root inputSchema is not type: object, so requests fail on every call. |
MCP-005 | error | Root $ref, allOf, anyOf, or oneOf a client will not resolve, so the tool is dropped or errors. |
MCP-006 | error | No properties object at the root; strict clients reject the whole request. |
MCP-007 | warning | Property name characters some clients do not accept, or a name longer than 64 characters. |
MCP-008 | error | A required entry with no matching property; strict clients reject the tool. |
MCP-009 | warning | A property that declares no type, leaving its type ambiguous to the model. |
MCP-010 | warning | The OpenAPI keyword nullable, which is not valid JSON Schema; use a union type. |
MCP-011 | info | A $schema draft that differs from the profile's preferred draft. |
MCP-012 | warning | A missing description, so the model cannot judge when to call the tool. |
MCP-013 | info | A very long description, which is re-sent with every request. |
MCP-020 | warning | Prompt-injection-like tool text; describe capabilities plainly so the server is not treated as hostile. |
MCP-021 | warning | Invisible or bidirectional Unicode characters, a known tool-poisoning technique. |
MCP-022 | warning | An unconstrained shell, file, SQL, or URL sink property reachable from tool input. |
MCP-030 | warning | A server whose tool definitions exceed the token budget. |
MCP-031 | info | A single tool whose definition is oversized on its own. |
MCP-032 | warning | Tool definitions that consume over 20% of a 200k-token context window before the first message. |
Client profiles
A profile describes what one client accepts. The same payload can pass for one profile and fail for another, which is why the profile is explicit in every run.
| Profile | Tool name | Property name | Root type: object |
Root $ref / combinators |
Root properties |
Draft |
|---|---|---|---|---|---|---|
claude | [A-Za-z0-9_-], 1-64 | [A-Za-z0-9_.-], 1-64 | required | rejected | required | 2020-12 |
openai | [A-Za-z0-9_-], 1-64 | [A-Za-z0-9_-], 1-64 | required | rejected | required | any |
gemini | [A-Za-z0-9_-], 1-64 | [A-Za-z0-9_.-], 1-64 | required | rejected | required | any |
foundry | [A-Za-z0-9_-], 1-64 | [A-Za-z0-9_-], 1-64 | required | rejected | required | any |
copilot | [A-Za-z0-9_-], 1-64 | [A-Za-z0-9_.-], 1-64 | required | rejected | required | any |
generic | [A-Za-z0-9_-], 1-128 | any | required | combinators allowed | required | any |
An example schema
A valid tool for the claude profile: an object root, typed properties, a constrained sink, and required that matches properties.
{
"name": "read_file",
"description": "Read a UTF-8 text file under the data directory.",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"maxLength": 256,
"pattern": "^/srv/data/[A-Za-z0-9_./-]+$"
}
},
"required": ["path"]
}
}