Skip to content

The MCP tool bridge never forwards parameters — bridged tools reach MCP clients with no input schema, and the docblock says it does forward one #13271

Description

@os-project-manager

Found while re-closing the tool.json liveness ledger (refs #13042); unrelated to that card's file surface, so filed rather than fixed.

Measured at 74049254

packages/mcp/src/mcp-server-runtime.ts, registerToolFromDefinition — the method that turns each AIToolDefinition into an MCP tool — calls the SDK like this:

this.mcpServer.registerTool(tool.name,{description: tool.description,annotations: {destructiveHint: this.isDestructiveTool(tool.name),readOnlyHint: this.isReadOnlyTool(tool.name),openWorldHint: false,},},async(extra)=>{/* reads extra.arguments */},);

tool.parameters is never read. git grep -nE "\.(outputSchema|objectName)" -- 'packages/mcp/src/**/*.ts' returns nothing, and the only tool. reads in the file are tool.name (5 sites) and tool.description (1 site).

So a tool bridged by bridgeTools reaches an MCP client with a name and a description and no input schema at all — the client has to guess the argument shape. The handler then reads whatever arrived via extra.arguments without validating it against the schema the definition carries.

Why this reads as a defect rather than a design choice

The docblock immediately above the call says the opposite of what the code does:

Convert JSON Schema parameters to Zod-compatible format for MCP SDK. The MCP SDK registerTool with inputSchema expects a Zod raw shape or AnySchema. Since our tools use JSON Schema, we use the low-level .tool() with a raw callback and pass the JSON Schema as annotations metadata.

The annotations object that is actually passed carries only destructiveHint / readOnlyHint / openWorldHint. Nothing hands the JSON Schema anywhere. The comment describes a workaround that is not implemented, which is why this survived: a reader checking "is parameters handled here?" finds a sentence saying yes.

bridgeTools is live wiring, not dead code — packages/mcp/src/plugin.ts, start(), duck-types aiService.toolRegistry and calls it when an AI service is mounted.

Not a liveness re-grade

tool.parameters stays live: the cloud LLM path reads it (cloud packages/service-ai/src/adapters/vercel-adapter.ts, buildVercelOptions, wraps it with jsonSchema and passes it as the Vercel SDK inputSchema). This card is about the framework MCP surface only, where the same key is dropped. The asymmetry is now recorded in the parameters note in packages/spec/liveness/tool.json.

What a fix would need to decide

  1. Whether the MCP SDK version in use accepts a raw JSON Schema for inputSchema, or needs a JSON-Schema-to-Zod conversion at the boundary.
  2. Whether dropping the schema was ever deliberate (in which case the docblock is the thing to correct, and the drop should be stated as a known limitation of the bridge).

Either way the docblock and the code should stop disagreeing.

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions