modelcontextprotocol / modelcontextprotocol/typescript-sdk
Low-level Server API publishes inputSchema in tools/list but does not enforce it on tools/call
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Summary
On @modelcontextprotocol/sdk@1.30.0, a server built with the low-level Server API declares an inputSchema in tools/list, but the SDK does not check tools/call arguments against it before invoking the handler. The call runs and the client receives a well-formed result with no error code and no isError.
McpServer does not have this problem. Its zod-derived schemas are enforced.
The specification's Security Considerations require servers to "Validate all tool inputs", so the low-level path lets a server publish a contract the SDK does not hold it to.
Reproduction
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema }
from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "demo", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "scan_code_imports",
inputSchema: {
type: "object",
properties: { code: { type: "string" } },
required: ["code"],
},
}],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => ({
content: [{
type: "text",
text: `CLEAN (received typeof=${typeof req.params.arguments?.code})`,
}],
}));
await server.connect(new StdioServerTransport());
Call it with {"name":"scan_code_imports","arguments":{"code":12345}}, where the declared schema requires a string:
{"jsonrpc":"2.0","id":3,"result":{"content":[
{"type":"text","text":"CLEAN (received typeof=number)"}]}}
The handler ran with a number. The same tool built on McpServer with an equivalent zod schema rejects the identical call:
MCP error -32602: Input validation error: Invalid arguments for tool
scan_code_imports: Invalid input: expected string, received number at code
Scale
I ran a protocol conformance census over the official registry, installing and executing all 6,106 self-contained, locally installable servers. 129 executed a tool on an argument their own declared schema rejects and returned ordinary output. Attributing each to its SDK from package metadata:
| SDK family | count |
|---|---|
| official TypeScript SDK | 112 |
| no recognized SDK package | 7 |
| metadata unavailable | 10 |
| official Python SDK | 0 |
| FastMCP (Python) | 0 |
Responding denominators are 2,347 for official-ts and 1,020 for the two Python families combined. I have not tested the Python SDKs directly, so I report the zero as an observation rather than claiming a mechanism for it.
109 of the 112 declare a caret range such as ^1.0.0 or ^1.29.0, and none declares an exact version. Because the census installed each server fresh, those resolve to the newest 1.x, so this is current 1.x behaviour observed in the wild rather than a legacy artifact. It also means a fix released on the 1.x line reaches essentially all of them on their next install.
The client-visible effect is that the result is indistinguishable from a correct one. In the census a code-scanning tool asked to scan the integer 12345 answered CLEAN, and a prompt-scoring tool returned a full rubric with per-dimension sub-scores for the same integer.
Suggested fix
Validate tools/call arguments against the tool's declared inputSchema before dispatch on the low-level path, matching what McpServer already does, and report failures as isError consistent with SEP-1303.
On @modelcontextprotocol/server@2.0.0
It exports a JsonSchemaValidator and ships an Ajv implementation at ./validators/ajv. I could not determine from the published types whether a validator is wired in by default. If it is opt-in, the same gap exists there for authors who do not supply one.
Artifacts
Harness, dataset, raw transcripts, and the two probe servers used above: https://github.com/Ahmad-Faraj/mcp-conformance (see sdk_probe/)
I can supply the per-server transcripts behind the 112 figure, or re-run the census against a patched build.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the low-level Server request handling for CallToolRequestSchema and compare it with the schema enforcement already used by McpServer. Inspect the JsonSchemaValidator and Ajv entry points mentioned for version 2.0.0. Done means invalid tools/call arguments are rejected before the handler runs and reported with the expected error or isError response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100