mcptest docs GitHub

Strict input-schema lint

Status: implemented. Tracked as epic WOR-1236 and child WOR-1241.

An under-constrained inputSchema lets malformed input reach the server, which the runtime-fault taxonomy (Real Faults in MCP Software, arXiv:2603.05637) ties to a class of parameter and type-validation faults. The fuzzer finds these at runtime; the schema lint catches them statically, which is cheaper. The two are complementary: a tool that passes the lint is far less likely to crash under a fuzz sweep.

The rules

Each rule inspects one tool's inputSchema and carries a stable id.

RuleSeverityWhat it flags
SCH-001warningthe object declares properties but no required list, so the server cannot rely on any argument being present
SCH-002warningadditionalProperties is not false, so unexpected fields are accepted silently
SCH-003criticala property declares neither type nor enum, so any value is accepted
SCH-004warninga string property has no maxLength, or an array property has no maxItems, so input size is unbounded

The targets

The findings surface through the tool_quality: block as two assertable targets, alongside the existing description-quality targets:

tool_quality:
  - name: tool schemas are well constrained
    server: local
    expect:
      - target: schema_criticals
        matcher: { schema: { maximum: 0 } }
      - target: schema_warnings
        matcher: { schema: { maximum: 3 } }

These do not change the default tool_quality: gate; declare them explicitly to opt in.

The autofix

The lint ships a mechanical fix. Given an under-constrained schema it returns a tightened copy that sets additionalProperties: false and adds a required list of every declared property, applied recursively to nested object schemas. It deliberately does not invent a type, a maxLength, or a maxItems, since the right value is the author's to choose, so SCH-003 and SCH-004 stay findings rather than guesses. The examples/tool-schema-lint directory pins a loose schema and its tightened output together with a byte-for-byte test.

Run the lint and the autofix from the command line over a captured tools/list snapshot:

mcptest schema-lint tools.json                 # report findings, exit 1 if any
mcptest schema-lint tools.json --fix           # print the tightened catalog
mcptest schema-lint tools.json --fix --write   # tighten the snapshot in place

mcptest schema-lint is the standalone surface; the same lint also runs inside a suite's tool_quality: check. See the CLI reference for every flag. (This is distinct from mcptest lint, which scans suites for deprecated MCP features.)

What it does not do

The lint is structural: it checks that a schema constrains its inputs, not that the constraints are semantically right. A maxLength of one million still passes SCH-004. Pair it with the fuzzer, which exercises the actual runtime handling the schema describes.