Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MCP SDK: eager schema conversion re-runs on every stateless request

Minimal reproduction for @modelcontextprotocol/server@2.0.0. No framework, only the SDK, Express and zod.

McpServer.registerTool converts the zod inputSchema to JSON Schema eagerly and stores the result on the server instance. In the stateless pattern (createMcpHandler(() => buildServer()), a fresh McpServer per request) that cache never hits, so every request, including initialize, pays the full conversion cost for every tool. zod does not memoize ~standard.jsonSchema.input(), so hoisting the z.object does not help either.

Run

pnpm install
pnpm bench                     # registration cost in isolation
pnpm server & pnpm measure     # request latency over HTTP

Results

Node 24.16, zod 4.6.5, Apple Silicon. 53 tools, 25 fields each, some nested arrays of objects.

Registration of all 53 tools on a fresh McpServer, median of 60 runs after warm-up:

variant median
raw shape (SDK builds z.object) 19.2 ms
hoisted z.object, same instance every time 18.3 ms
hoisted + memoized ~standard.jsonSchema.input() 0.8 ms
no inputSchema 0.02 ms

Request latency over HTTP with createMcpHandler(() => buildServer()), median of 50 requests after warm-up:

request median
GET /status (plain Express route) 1.7 ms
initialize 30.8 ms
tools/list 55.5 ms

Where the time goes

_createRegisteredTool in @modelcontextprotocol/server calls standardSchemaToJsonSchema(inputSchema, "input") at registration and stores it in this._toolInputSchemaJson[name]. That map lives on the McpServer instance. zod's createStandardJSONSchemaMethod rebuilds the conversion context and walks the schema on every call, so input(opts) === input(opts) is false.

The third bench row patches ~standard.jsonSchema.input on a hoisted schema with a one-line memo. That is the shape of a fix inside the SDK: cache the converted JSON Schema per schema instance (and per conversion target) in a module-level WeakMap, so a hoisted schema converts once per process regardless of how many McpServer instances are created.

About

Repro: @modelcontextprotocol/server registerTool re-converts zod schemas on every stateless request

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages