modelcontextprotocol / modelcontextprotocol/typescript-sdk
Docs: passing z.object() as inputSchema on v1 fails silently (empty schema) or cryptically (tools/list crash) — not covered in troubleshooting or migration guide
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 SDK v1 (still latest on npm as of this writing — 1.30.0), passing a ZodObject (z.object({...})) where the API expects a Zod raw shape ({ field: z.string() }) produces failure modes that are either silent or cryptic, depending on the version and the API used. None of them point the developer at the actual mistake, and neither the troubleshooting page nor the v1→v2 migration guide mentions these symptoms.
I hit this while building scorm-mcp-server and lost the better part of a day on it: the server started fine, tools/list looked plausible, but every client silently stripped my tool arguments. I've since tested the behavior across v1 versions — full matrix below.
Reproduction matrix
Minimal repro: register one tool with inputSchema: z.object({ name: z.string() }) (instead of the raw shape { name: z.string() }), connect an in-memory client, call tools/list.
| SDK version | registerTool + z.object() |
server.tool() (positional) + z.object() |
|---|---|---|
| 1.12.0 → 1.21.0 | ❌ tools/list crashes: MCP error -32603: Cannot read properties of null (reading '_def') |
⚠️ Silent: publishes an empty schema ({"type":"object"}, no properties) — clients strip all arguments; handler receives no args |
| 1.22.0 → 1.26.0 | ✅ normalized, works | ⚠️ Silent empty schema (same as above) |
| 1.28.0 → 1.30.0 | ✅ normalized, works | ✅ throws a clear error at registration |
Repro script (run with any of the versions above)
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import { z } from 'zod';
const server = new McpServer({ name: 't', version: '1.0.0' });
// WRONG on v1 (correct on v2): ZodObject instead of raw shape
server.tool('greet', 'desc', z.object({ name: z.string() }),
async (args) => ({ content: [{ type: 'text', text: JSON.stringify(args) }] }));
const [ct, st] = InMemoryTransport.createLinkedPair();
const client = new Client({ name: 'c', version: '1.0.0' });
await Promise.all([server.connect(st), client.connect(ct)]);
const { tools } = await client.listTools();
console.log(JSON.stringify(tools[0].inputSchema));
// 1.12.0–1.26.0 → {"type":"object"} (empty — no properties, no error anywhere)
Why this still matters even though recent versions behave better
- Older 1.x versions are massively deployed. Plenty of published servers pin
^1.xranges resolved months ago; every tutorial written before mid-v1 shows raw shapes, while the v2 docs and README now showz.object()— so developers moving between examples get bitten in both directions. - The failure modes never mention the cause. An empty
{"type":"object"}schema looks like a client bug ("my client strips arguments"), andCannot read properties of null (reading '_def')looks like an SDK internals bug. Neither error string is searchable back to "you passed a ZodObject where a raw shape was expected". - The normalization added for
z.object()still doesn't cover other Zod types — see #1643 (z.discriminatedUnion()silently dropped), so the general class of "wrong schema kind fails silently" is still live onlatest.
Suggested fix (happy to PR either)
- Troubleshooting page: add an entry keyed on the two observable symptoms — "my tool's
inputSchemais empty / clients don't send arguments" and "tools/listfails withreading '_def'" — explaining the raw-shape vsZodObjectdistinction on v1 and which versions normalize it. - Migration guide: the v1→v2 guide already documents the API shift (raw shape → Standard Schema), but a one-line warning that doing it backwards on v1 ≤1.21 crashes
tools/list, and ≤1.26 silently empties the schema viaserver.tool(), would save people real debugging time.
I'm happy to open a PR with the docs wording if maintainers agree this belongs in the docs.
Environment used for the matrix: Node v22, zod 3.23.8, in-memory transport, each SDK version installed clean from npm.
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 with the linked troubleshooting page and v1→v2 migration guide, then compare their existing schema guidance with the reported v1 behavior and reproduction matrix. Add troubleshooting entries for empty schemas and the tools/list crash, plus a concise migration warning covering raw shapes versus ZodObject and affected versions; done when both symptoms and the correct v1 usage are discoverable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 85/100