docs: document v1 z.object() vs raw-shape footgun in troubleshooting + migration guide by huzaifafarooqusa-spec · Pull Request #2813 · modelcontextprotocol/typescript-sdk · GitHub
Skip to content

docs: document v1 z.object() vs raw-shape footgun in troubleshooting + migration guide - #2813

Draft
huzaifafarooqusa-spec wants to merge 2 commits into
modelcontextprotocol:mainfrom
huzaifafarooqusa-spec:docs/v1-zod-object-vs-raw-shape
Draft

huzaifafarooqusa-spec wants to merge 2 commits into
modelcontextprotocol:mainfrom
huzaifafarooqusa-spec:docs/v1-zod-object-vs-raw-shape

Conversation

@huzaifafarooqusa-spec

@huzaifafarooqusa-spec huzaifafarooqusa-spec commented Sep 14, 2026

Copy link
Copy Markdown

Summary

On SDK v1, server.tool() expects a raw shape ({ name: z.string() }), not a ZodObject (z.object({...})) — the opposite of v2. Passing the wrong one produces failure modes that never point at the actual cause:

  • v1 ≤1.21: tools/list crashes with Cannot read properties of null (reading '_def')
  • v1 1.22–1.26: registration succeeds silently but publishes an empty {"type":"object"} schema — clients strip all tool arguments with no error anywhere
  • v1 ≥1.28: throws a clear error at registration (already fixed upstream)

Full repro matrix and root cause analysis from #2627 (credit to the reporter — this PR just writes it up).

Changes

  • docs/troubleshooting.md: new entry keyed on both observable symptoms, plus a recap line, following the page's existing convention.
  • docs/migration/upgrade-to-v2.md: short callout next to the registerTool example explaining the reverse mistake on v1.

Testing

  • prettier --check docs/troubleshooting.md docs/migration/upgrade-to-v2.md → clean
  • Full pnpm build:all / typecheck:all / lint:all via pre-push hook → all green

Addresses #2627

…ng one chunk

streamableHttp.test.ts asserted on multiple SSE events after a single
reader.read() call, assuming Node's fetch would coalesce them into one
chunk. That assumption doesn't hold on newer Node versions, where
closely-timed events can arrive as separate chunks across separate
reads, failing the test before the later event is read.

Adds a readUntilContains helper that accumulates decoded text across
reads until the expected content appears (or times out), and uses it
in the two tests that were asserting on multiple SSE events from a
single read.
@changeset-bot

changeset-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown

@huzaifafarooqusa-spec huzaifafarooqusa-spec changed the title Docs/v1 zod object vs raw shape docs: document v1 z.object() vs raw-shape footgun in troubleshooting + migration guide Sep 14, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 14, 2026

Copy link
Copy Markdown

…+ migration guide

On SDK v1, server.tool() expects a raw shape ({ name: z.string() })
rather than a ZodObject, but nothing about the two failure modes
points a reader at the actual mistake:

- v1 <=1.21: tools/list crashes with
  "Cannot read properties of null (reading '_def')"
- v1 1.22-1.26: registration succeeds but publishes an empty
  {"type":"object"} schema, so every client silently strips the
  tool's arguments

v1 >=1.28 already throws a clear error, but the older ranges are
still widely deployed and easy to hit when copying v2-style
examples (which use z.object()) into v1 code. Adds a troubleshooting
entry keyed on both observable symptoms, and a short callout in the
v2 migration guide next to the registerTool example, so both search
paths land on the explanation.

Reported with full repro matrix in modelcontextprotocol#2627.
@huzaifafarooqusa-spec
huzaifafarooqusa-spec force-pushed the docs/v1-zod-object-vs-raw-shape branch from 814ceec to ab9ac4c Compare September 14, 2026 01:27
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.

1 participant