LearnNewsExamplesServices
Frontmatter
id16334
titleCensus: which MCP handler signatures silently degrade, read from the handlers not the contracts
stateClosed
labels
bugai
assigneesneo-kimi-iris
createdAtAug 2, 2026, 11:31 AM
updatedAtAug 2, 2026, 2:11 PM
githubUrlhttps://github.com/neomjs/neo/issues/16334
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[ ] 16343 Fail the unit suite when an MCP handler signature silently degrades
closedAtAug 2, 2026, 2:11 PM

Census: which MCP handler signatures silently degrade, read from the handlers not the contracts

neo-opus-grace
neo-opus-grace commented on Aug 2, 2026, 11:31 AM

Context

Split out of #16250 after an external contributor (@novice-22) corrected the scope I was about to give this — issuecomment-5156710968. The correction is the reason this ticket exists in this shape rather than the wrong one.

The Problem

ai/mcp/ToolService.mjs:107-111 dispatches one of two ways:

if (tool.passAsObject) {
    return tool.handler(validatedArgs);
}
const handlerArgs = tool.argNames.map(name => validatedArgs[name]);
return tool.handler(...handlerArgs);

Without x-pass-as-object, arguments arrive positionally, in contract order. Whether that is correct depends entirely on the handler signature, which the contract never references. #16250 proved two operations where it is wrong and fails silently — get_ingestion_progress destructures a bare Number (every key undefined, = {} suppresses the throw, defaults win) and get_pull_request_diff binds one parameter out of four declared.

Nobody knows how many others there are, and the obvious way to count them is wrong.

The Architectural Reality — two questions, two different sources of truth

This is the distinction the correction turns on, and getting it backwards produces a census of the wrong population:

question source of truth count
which operations are unannotated and take arguments? the OpenAPI contracts alone 9 (contributor's table)
which handler signatures silently degrade under positional dispatch? the handler signatures themselves unknown — this ticket

"My nine include seven that are genuinely positional and correct, so the overlap is smaller than it looks." — @novice-22

So the annotation table is a starting set, not the defect set. A contract-only sweep cannot answer this: the contract does not know whether the handler destructures an object, declares one parameter, or accepts N positional ones in the declared order. Only reading the handler answers it.

The three-way split #16250 established:

  1. handler takes positional params in contract order → correct without the annotation
  2. handler destructures a single object → silently degrades (defaults win, no throw)
  3. handler declares fewer params than the contract → silently truncates (extras dropped)

Classes 2 and 3 are the defect. Class 1 is the majority and must not be "fixed".

The Fix

(Prescription — the census is the deliverable; repairs are downstream.)

For every operation across all MCP servers' openapi.yaml, resolve its handler through the server's toolService.mjs binding table and classify it 1/2/3. Report the class-2 and class-3 members. Annotating them is separate work, and each one is a behavior change on a live tool surface.

Worth considering as an outcome rather than assuming it: if the classification is mechanically derivable, this belongs in a lint rather than a one-time sweep — a new operation whose handler destructures an object and carries no annotation is the same bug re-filed. OpenApiValidatorCompliance.spec.mjs is the existing home for contract-vs-implementation checks.

Acceptance Criteria

  • Every operation across all MCP servers is classified 1 / 2 / 3 by reading its handler signature, not its contract.
  • The report distinguishes the starting set (unannotated + takes arguments) from the defect set (classes 2 and 3), with both counts stated — they are different numbers and conflating them is the failure this ticket exists to avoid.
  • Class-1 operations are explicitly listed as correct-as-is, so a later pass does not "fix" them into breakage.
  • The two already-known members (get_ingestion_progress, get_pull_request_diff) appear in the defect set — a census that misses its own known positives has not been validated.
  • A verdict on whether the classification is mechanically checkable; if yes, a follow-up ticket for the lint, with the reason recorded if not.

Out of Scope

  • Repairing the members found — each is a live tool-surface behavior change and needs its own ticket and review.
  • The two in #16250, fixed in PR #16330.
  • The getPullRequestDiff legacy bare-number tolerance — every in-repo call site passes an object and the only bare-number producer is the dispatcher itself, so it becomes dead once #16330 lands. Its removal is a named decision, not part of a census.

Avoided Traps

  • Censusing the annotation table. Nine unannotated-with-arguments operations, seven of them correct. Starting there means starting from a population that mostly is not the bug — the exact scope error this ticket was corrected out of before it was filed.
  • Answering from the contracts. The contract cannot see the handler signature, and the handler signature is what decides. A contract-only sweep would produce a confident, complete-looking, wrong answer.
  • Treating "takes arguments" as "takes an object". Class 1 is the majority: positional handlers matching contract order work correctly with no annotation and must be left alone.

Related

  • #16250 / PR #16330 — the two proven members and their fix
  • #16231 — established that a missing annotation can break a call; this establishes that the handler signature decides whether it does

Live latest-open sweep at 2026-08-02T09:33Z; no equivalent found.

Origin Session ID: 713db0da-2239-44ea-ba5b-931be90d34fc

Retrieval Hint: query_raw_memories("MCP positional dispatch handler signature census x-pass-as-object three-way split")

tobiu referenced in commit 3981aea - "feat(ai): the MCP handler-signature census — handlers, not contracts (#16334) (#16345) on Aug 2, 2026, 2:11 PM
tobiu closed this issue on Aug 2, 2026, 2:11 PM