Skip to content

fix: detect plain JSON Schema objects in tool() overload resolution#2010

Open
aayushbaluni wants to merge 2 commits intomodelcontextprotocol:mainfrom
aayushbaluni:fix/1585-tool-json-schema-detection
Open

fix: detect plain JSON Schema objects in tool() overload resolution#2010
aayushbaluni wants to merge 2 commits intomodelcontextprotocol:mainfrom
aayushbaluni:fix/1585-tool-json-schema-detection

Conversation

@aayushbaluni
Copy link
Copy Markdown

Problem

The deprecated McpServer.tool() variadic overloads were removed during the v2 migration, but callers still rely on them (see issue #1585). When they passed (name, description, plainJsonSchema, handler), older implementations routed any non-Zod object into the ToolAnnotations branch. Plain JSON Schema objects (with nested properties, etc.) were therefore stored as annotations and inputSchema stayed unset, so tools/list exposed an empty object schema and arguments were not validated or forwarded correctly.

Fix

  • Restore legacy tool() on McpServer with positional overloads equivalent to the common v1 shapes (tool(name, schema, cb) and tool(name, description, schema, cb)).
  • Resolve the schema/annotations slot in order: Standard Schema / Zod raw shape (normalizeRawShapeSchema) → plain JSON Schema (structural heuristic + fromJsonSchema()) → ToolAnnotations-only objects (title, *Hint booleans).
  • Throw a clear TypeError when the object matches none of these shapes (replacing silent data loss).

Note: TypeScript overloads for the rare (name, paramsSchema, annotations, cb) / five-argument forms were omitted because they collide with the description overload; those combinations remain supported at runtime via registerTool.

Testing

  • Coverage in packages/server/test/server/mcp.compat.test.ts: plain JSON Schema preserves directory_id (and required) through standardSchemaToJsonSchema, annotations-only registration yields empty wire input schema and preserved annotations on tools/list, unrecognized objects throw, and tools/call validates JSON Schema tools end-to-end.

Fixes #1585

Made with Cursor

@aayushbaluni aayushbaluni requested a review from a team as a code owner May 1, 2026 18:39
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 1, 2026

🦋 Changeset detected

Latest commit: 1a5fea9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/express Patch
@modelcontextprotocol/fastify Patch
@modelcontextprotocol/hono Patch
@modelcontextprotocol/node Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 1, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2010

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2010

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2010

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2010

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2010

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2010

commit: 1a5fea9

@aayushbaluni aayushbaluni force-pushed the fix/1585-tool-json-schema-detection branch from 2685c73 to d80e40e Compare May 1, 2026 18:47
When a plain JSON Schema object (with type/properties) was passed to
server.tool(), the overload resolver incorrectly treated it as
ToolAnnotations, causing inputSchema to be silently dropped from the
wire protocol.

Fixes modelcontextprotocol#1585
@aayushbaluni aayushbaluni force-pushed the fix/1585-tool-json-schema-detection branch from 7302ec9 to 1a5fea9 Compare May 4, 2026 05:45
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.

server.tool() silently drops inputSchema when passed plain JSON Schema objects instead of Zod schemas

1 participant