fix: drop the unreachable root rule, correct the always-false suggestion

Copilot review round 2.

- Remove `non-object-root` entirely. It could never fire: the SDK types
  `inputSchema` with `type: literal("object")`, so a tool with any other
  root fails `ListToolsResultSchema` and `salvageListItems` drops it
  before any client sees it. Verified against a live server advertising
  `inputSchema: {type: "array"}` — `listAllTools()` returns []. A rule
  that cannot fire is worse than none, because the docs then claim a
  check the tool does not perform; the condition is already reported
  through the malformed-items surface. Pinned by an integration test so
  a change in that behavior is a signal rather than a silent gap.

- Fix the bare-`false` suggestion. `properties: {a: false}` forbids the
  property; deleting the entry *permits* it with any value under the
  default `additionalProperties`, so the advice inverted the schema it
  replaced. Now recommends `{"not": {}}` and says why deletion differs.

- Exempt `not`-parented schemas from `untyped-schema`, so the `{"not":
  {}}` above does not trip this module's own rule — under negation an
  always-accepting subschema means always-reject, making "accepts any
  value" the exact opposite of what it does. Scoped to that position,
  not inherited by its subtree.

- Say plainly, in the `true` and untyped suggestions, that they narrow
  the schema deliberately rather than implying an equivalent rewrite.
  `true` has no portable object-form equivalent — `{}` is the very shape
  this lint flags.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UL9retfvAXRgvi6EWk4SY4
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-25 02:02:26 -04:00
commit cac97c142c

View file

@ -174,11 +174,18 @@ Error: tool "info"
```
Severity decides the exit code, not the report: **errors** are constructs a
shipping MCP client refuses outright (a bare `true` in a `properties` map, a
non-object schema root), **warnings** are ones handled unevenly (an array-form
`type`, a remote `$ref`, a schema carrying no validation keyword at all). Only
errors fail the run — a `--strict` that failed on warnings would be unusable as
a CI gate against servers that are in fact fine.
shipping MCP client refuses outright (a bare `true` or `false` where a schema
object belongs), **warnings** are ones handled unevenly (an array-form `type`,
a remote `$ref`, a schema carrying no constraining keyword at all). Only errors
fail the run — a `--strict` that failed on warnings would be unusable as a CI
gate against servers that are in fact fine.
There is deliberately **no "inputSchema must be an object" check**, even though
MCP requires one. The SDK types `inputSchema` with `type: literal("object")`,
so a tool whose input root is anything else fails `ListToolsResultSchema` and
is dropped from the list before the lint could see it — it is reported through
the malformed-items path instead. A rule that cannot fire would only make this
documentation claim a check the CLI does not perform.
The report goes to stderr, so the result on stdout stays parseable. Under
`--format json` the findings are folded into the same envelope instead