modelcontextprotocol / modelcontextprotocol/typescript-sdk
zod listed in both dependencies and peerDependencies causes duplicate installs and TS type-incompatibility
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Describe the bug
@modelcontextprotocol/sdk@1.26.0 declares zod in BOTH dependencies and
peerDependencies. With bun (and pnpm under default hoisting, npm in some
configs) this causes a nested node_modules/@modelcontextprotocol/sdk/node_modules/zod
to be installed alongside the consumer's top-level zod, even when the
consumer's version satisfies the SDK's range.
The result: TypeScript sees two distinct $ZodType class declarations (one per
copy) and treats them as nominally incompatible. Schemas built from the
consumer's zod (e.g. z.string().optional()) fail to satisfy the
AnySchema = z3.ZodTypeAny | z4.$ZodType type that registerTool's
inputSchema parameter expects — even though both copies are the same library
at the same major version.
To Reproduce
Steps to reproduce the behavior:
bun init -ybun add @modelcontextprotocol/sdk@1.26.0 zod@4.4.2 typescript- Create
src/server.ts:import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { z } from 'zod' const server = new McpServer({ name: 'demo', version: '0.0.1' }) server.registerTool( 'echo', { inputSchema: { msg: z.string() } }, async ({ msg }) => ({ content: [{ type: 'text', text: msg }] }), ) bun x tsc --noEmit
Expected behavior
Typecheck passes. The consumer's zod should satisfy the AnySchema slot
because the SDK's compat layer already accepts both z3.ZodTypeAny and
z4.$ZodType, and the consumer's schemas are valid z4.$ZodType values.
Logs
src/server.ts:7:24 - error TS2322: Type 'ZodString' is not assignable to type 'AnySchema'.
Type 'ZodString' is missing the following properties from type
'ZodType<any, any, any>': _type, _parse, _getType, _getOrReturnCtx, and 7 more.
7 { inputSchema: { msg: z.string() } },
~~~~~~~~~~
ls node_modules/@modelcontextprotocol/sdk/node_modules/zod/package.json
shows the nested copy is installed (e.g. 4.3.6) alongside the top-level (4.4.2).
Additional context
Workaround consumers have to apply (this is what bit us):
// package.json
"overrides": { "zod": "" }
followed by deleting bun.lock + reinstalling so the existing resolution is
re-evaluated.
Proposed fix: remove "zod" from dependencies and keep it only in
peerDependencies. The compat shim (dist/esm/server/zod-compat.d.ts) already
handles both v3 and v4 via the consumer-provided copy, so the SDK doesn't
need its own bundled zod.
Repo: https://github.com/modelcontextprotocol/typescript-sdk
SDK version: 1.26.0
Bun version: 1.3.12
TypeScript: 5.9.3
Zod (top): 4.4.2
Zod (nested): 4.3.6
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 package.json dependency declarations and read dist/esm/server/zod-compat.d.ts to understand the existing compatibility layer. Reproduce the issue with the Bun commands and sample src/server.ts, then verify that installation no longer creates a nested zod copy and that bun x tsc --noEmit accepts the consumer schema.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100