You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
validateToolArguments (src/mcp-server.ts, the single dispatch call site in dispatchTool) is the
only argument validation the MCP server performs. It checks exactly two things: that args is a
non-array object, and that it carries no unknown keys when the tool's inputSchema sets additionalProperties: false.
functionvalidateToolArguments(tool: Row,args: Row){if(args===undefined||args===null)return{};if(typeofargs!=="object"||Array.isArray(args)||(tool.inputSchema?.additionalProperties===false&&Object.keys(args).some((key)=>!Object.hasOwn(tool.inputSchema?.properties??{},key),))){throwtoolError("invalid_params",`Invalid arguments for tool ${tool.name}.`);}returnargs;}
No type check, no enum check, no required check, no numeric bounds. Every z.enum(...), z.int().min(1), .max(200), and required across all 200+ entries in MCP_TOOLS is therefore decorative at runtime — honoured only by clients that validate against the published schema, and
by whatever ad-hoc guard the individual handler happens to have written by hand.
#8804 fixed one instance of the consequence (network reached networkArtifactPath unvalidated and
silently served the testnet registry) by wiring that one argument to the existing optionalEnum
guard. That fix is correct and complete for network, but it is per-handler: the next argument that
skips its guard fails the same way. #8804's Requirement 5 asked for an explicit decision on
dispatch-level enforcement and it was answered "no, not in that PR", with the audit below named
as the reason. This issue is that audit.
Why this is maintainer-only
Turning enforcement on is a behaviour change to a public agent-facing surface, across 206 tools at
once. Handlers are deliberately lenient in places — clampLimit accepts and clamps a limit the
schema forbids; optionalEnum accepts "" where a published enum does not — so schema-driven
validation converts an unknown number of currently-succeeding agent calls into hard invalid_params.
The size of that set is the whole question, and it has to be measured before anything is enforced.
Requirements
Audit first, enforce second — and the audit is the deliverable that gates the rest. Produce a
per-tool table of every argument whose runtime behaviour would change under schema enforcement.
Mechanical way to get it: for each tool, drive its handler with arguments that the published inputSchema rejects but the handler currently accepts (out-of-enum strings, out-of-range
integers, wrong types, omitted required keys) and record which ones return a result today.
Then choose the enforcement scope, with the audit as evidence:
(A) Full schema enforcement — validate against the source Zod schema at dispatch. Cheaper
than re-walking the derived JSON Schema, but MCP_TOOLS carries only the z.toJSONSchema-derived object, so the Zod schemas must be threaded through all 206 entries.
(B) Enum-only enforcement — reject a provided string argument that is not a member of inputSchema.properties[key].enum, continuing to accept absent/"" exactly as optionalEnum
does. Needs no Zod threading (the JSON Schema is already at dispatch) and closes the whole fix(mcp): network arg is never validated at runtime — MCP silently serves the testnet registry as mainnet #8804 bug class, but still changes behaviour for any handler that today tolerates an
out-of-enum value by falling back to a default — which is what the audit must enumerate.
Whichever is chosen, the leniency that survives must be deliberate and named.clampLimit's
clamping and optionalEnum's "" acceptance are load-bearing for real agent callers; if they
stay, say so in a comment at the enforcement site rather than leaving them as accidents.
No silent narrowing. A tool that starts rejecting a value it accepted yesterday must appear in
the PR body's list, with the value and the previous behaviour.
Deliverables
The per-tool behaviour-change audit (Requirement 1), committed as a test or a doc, not just
posted in a PR comment — it is the artifact the next person needs.
The chosen option implemented, with every behaviour change from the audit listed in the PR body.
A regression test that a schema-invalid argument is rejected for at least one tool of each
constraint kind the chosen option covers (enum, bound, type, required).
Patch coverage: every changed line and branch in src/** / workers/** is covered — codecov/patch enforces target: 99%, threshold: 0%. Measure unsharded with npm run test:coverage.
Expected Outcome
Either the MCP dispatch enforces the schemas it publishes — with every tool whose behaviour changed
named explicitly — or it deliberately does not, and a CI gate makes the next unguarded enum argument
fail in CI rather than in production. In both cases the gap between "what the tool listing advertises"
and "what the runtime accepts" stops being invisible.
Links & Resources
src/mcp-server.ts — validateToolArguments and its single dispatchTool call site.
Context
validateToolArguments(src/mcp-server.ts, the single dispatch call site indispatchTool) is theonly argument validation the MCP server performs. It checks exactly two things: that
argsis anon-array object, and that it carries no unknown keys when the tool's
inputSchemasetsadditionalProperties: false.No type check, no enum check, no
requiredcheck, no numeric bounds. Everyz.enum(...),z.int().min(1),.max(200), andrequiredacross all 200+ entries inMCP_TOOLSis thereforedecorative at runtime — honoured only by clients that validate against the published schema, and
by whatever ad-hoc guard the individual handler happens to have written by hand.
#8804 fixed one instance of the consequence (
networkreachednetworkArtifactPathunvalidated andsilently served the testnet registry) by wiring that one argument to the existing
optionalEnumguard. That fix is correct and complete for
network, but it is per-handler: the next argument thatskips its guard fails the same way. #8804's Requirement 5 asked for an explicit decision on
dispatch-level enforcement and it was answered "no, not in that PR", with the audit below named
as the reason. This issue is that audit.
Why this is maintainer-only
Turning enforcement on is a behaviour change to a public agent-facing surface, across 206 tools at
once. Handlers are deliberately lenient in places —
clampLimitaccepts and clamps alimittheschema forbids;
optionalEnumaccepts""where a published enum does not — so schema-drivenvalidation converts an unknown number of currently-succeeding agent calls into hard
invalid_params.The size of that set is the whole question, and it has to be measured before anything is enforced.
Requirements
per-tool table of every argument whose runtime behaviour would change under schema enforcement.
Mechanical way to get it: for each tool, drive its handler with arguments that the published
inputSchemarejects but the handler currently accepts (out-of-enum strings, out-of-rangeintegers, wrong types, omitted
requiredkeys) and record which ones return a result today.than re-walking the derived JSON Schema, but
MCP_TOOLScarries only thez.toJSONSchema-derived object, so the Zod schemas must be threaded through all 206 entries.inputSchema.properties[key].enum, continuing to accept absent/""exactly asoptionalEnumdoes. Needs no Zod threading (the JSON Schema is already at dispatch) and closes the whole
fix(mcp): network arg is never validated at runtime — MCP silently serves the testnet registry as mainnet #8804 bug class, but still changes behaviour for any handler that today tolerates an
out-of-enum value by falling back to a default — which is what the audit must enumerate.
argument whose schema declares an
enumis resolved throughoptionalEnum(or an equivalentguard) in its handler. Prevents the next fix(mcp): network arg is never validated at runtime — MCP silently serves the testnet registry as mainnet #8804 without changing any runtime behaviour.
clampLimit'sclamping and
optionalEnum's""acceptance are load-bearing for real agent callers; if theystay, say so in a comment at the enforcement site rather than leaving them as accidents.
the PR body's list, with the value and the previous behaviour.
Deliverables
posted in a PR comment — it is the artifact the next person needs.
constraint kind the chosen option covers (enum, bound, type, required).
src/**/workers/**is covered —codecov/patchenforcestarget: 99%, threshold: 0%. Measure unsharded withnpm run test:coverage.Expected Outcome
Either the MCP dispatch enforces the schemas it publishes — with every tool whose behaviour changed
named explicitly — or it deliberately does not, and a CI gate makes the next unguarded enum argument
fail in CI rather than in production. In both cases the gap between "what the tool listing advertises"
and "what the runtime accepts" stops being invisible.
Links & Resources
src/mcp-server.ts—validateToolArgumentsand its singledispatchToolcall site.src/mcp-server.ts—optionalEnum, the per-handler guard shape fix(mcp): network arg is never validated at runtime — MCP silently serves the testnet registry as mainnet #8804 wirednetworkto.networkinstance of this gap, and the PR that recorded the "no" this issue follows up.