Skip to content

fix(tools): schema-safe coercion of MCP tool arguments - #13416

Open
iborakove wants to merge 1 commit into
continuedev:mainfrom
iborakove:fix/mcp-tool-arg-types-13350-upstream
Open

iborakove wants to merge 1 commit into
continuedev:mainfrom
iborakove:fix/mcp-tool-arg-types-13350-upstream

Conversation

@iborakove

@iborakove iborakove commented Oct 24, 2026 •

Copy link
Copy Markdown

Description

Fixes #13350. Related: #13386 addresses the same issue.

Some models send non-string MCP tool arguments as JSON text, for example {"paths": "[\"a.txt\", \"b.txt\"]"} for an array parameter or "false" for a boolean. coerceArgsToSchema only handled the opposite case (re-stringifying objects for string parameters), so these values reached MCP servers as strings and failed input validation with errors such as expected array, received string.

coerceArgsToSchema (core/tools/parseArgs.ts) now decodes a string argument only when its property schema rejects strings and accepts the decoded JSON type. All other values pass through unchanged, and the MCP server remains responsible for validation.

  • Decoding: arrays, objects, booleans, numbers and null are decoded with JSON.parse; "True"/"FALSE" are accepted as getBooleanArg accepts them. Integers beyond Number.MAX_SAFE_INTEGER and non-finite numbers stay strings, so no precision is lost.
  • Schema support: type (including type arrays), enum, const, anyOf/oneOf (union), allOf (intersection), boolean schemas and local $refs. Keywords that can only narrow a schema (not, if/then/else, formats, ranges) are ignored, so a type the schema accepts is never excluded.
  • String preservation: if the schema accepts strings at all ("string", ["string", "array"], a string branch in anyOf, a string enum/const, or no type constraint), the value is left as is, e.g. "[1]" for a string | array parameter.
  • Bounded traversal: analysing one argument's schema visits at most 1,000 schema nodes, expands each $ref once and detects recursive references. Schemas that exceed the budget or recurse leave the argument unchanged, so pathological schemas take bounded time instead of exponential time or a stack overflow.
  • Reference safety: only local JSON-pointer references (#, #/…) are followed, through own properties only. References into or across a nested schema resource ($id, or id in draft 4), $dynamicRef/$recursiveRef, and remote or unresolvable references leave the argument unchanged.

Scope: argument normalization in the core tool-call path used by the IDE extensions. The CLI's MCP implementation in extensions/cli is unchanged.

Checklist

  • I've read the contributing guide
  • The relevant docs, if any, have been updated or created (no user-facing documentation is affected)
  • The relevant tests, if any, have been updated or created

Screen recording or screenshot

Not applicable: no UI changes.

Tests

  • core/tools/parseArgs.vitest.ts: decoding of each JSON type; valid strings preserved for type, anyOf/oneOf, enum/const and untyped schemas; malformed or mismatched values left unchanged; numeric strings never truncated or rounded; $ref, anyOf, oneOf and allOf resolution; adversarial schemas: an exponential $ref diamond, budget exhaustion (deep nesting, long $ref chains, wide anyOf), recursive references, nested $id resources, $dynamicRef and malformed references.
  • core/tools/callTool.vitest.ts (new): the MCP client receives native types (an edits array and a dryRun boolean) across repeated edit_file calls.
  • Full core suite: 1,694 tests passed, 0 failed. tsc --noEmit for core and extensions/vscode, ESLint and Prettier pass.
  • Manual end-to-end check (not part of this PR) through Continue's MCP client against a strict, schema-validating test MCP server: 30/30 tool calls received correctly typed arguments.

@iborakove
iborakove requested a review from a team as a code owner October 24, 2026 13:33
@iborakove
iborakove requested review from sestinj and removed request for a team October 24, 2026 13:33
@iborakove
iborakove force-pushed the fix/mcp-tool-arg-types-13350-upstream branch from 647763e to 9da7044 Compare October 24, 2026 14:22

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

filesystem read_multiple_files and edit_file Input validation error: Invalid arguments: expected array, received string

1 participant