Skip to content

feat(compat): add /server/zod-schemas subpath re-exporting *Schema constants#1906

Draft
felixweinberger wants to merge 1 commit intomainfrom
fweinberger/v2-bc-zod-schemas-subpath
Draft

feat(compat): add /server/zod-schemas subpath re-exporting *Schema constants#1906
felixweinberger wants to merge 1 commit intomainfrom
fweinberger/v2-bc-zod-schemas-subpath

Conversation

@felixweinberger
Copy link
Copy Markdown
Contributor

Part of the v2 backwards-compatibility series — see reviewer guide.

v2 stopped exporting Zod schema constants (only TS types). Consumers using them for runtime validation at HTTP boundaries (token endpoints, OIDC discovery, custom transports) lose validators. This re-exports them under a @deprecated subpath.

Motivation and Context

v2 stopped exporting Zod schema constants (only TS types). Consumers using them for runtime validation at HTTP boundaries (token endpoints, OIDC discovery, custom transports) lose validators. This re-exports them under a @deprecated subpath.

v1 vs v2 pattern & evidence

v1 pattern:

`import { OAuthTokensSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'`

v2-native:

Use `specTypeSchema('CallToolRequest')` (see #1887) or import types only

Evidence: Hits any consumer doing runtime validation of HTTP responses or implementing a custom transport.

How Has This Been Tested?

  • Import-compiles test
  • Integration: validated bump-only against 5 OSS repos via the v2-bc-integration validation branch
  • pnpm typecheck:all && pnpm lint:all && pnpm test:all green

Breaking Changes

None — additive @deprecated shim. Removed in v3.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added or updated documentation as needed

Additional context

Stacks on: none

@felixweinberger felixweinberger added the v2-bc v2 backwards-compatibility series label Apr 15, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 15, 2026

🦋 Changeset detected

Latest commit: f7e391b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/express Patch
@modelcontextprotocol/fastify Patch
@modelcontextprotocol/hono Patch
@modelcontextprotocol/node Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@felixweinberger felixweinberger added this to the v2.0.0-bc milestone Apr 15, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 15, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@1906

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@1906

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@1906

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@1906

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@1906

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@1906

commit: f7e391b

@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-zod-schemas-subpath branch 2 times, most recently from 8c9a388 to ab8b2ed Compare April 16, 2026 09:38
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-zod-schemas-subpath branch from ab8b2ed to 54f3063 Compare April 16, 2026 17:48
@felixweinberger
Copy link
Copy Markdown
Contributor Author

@claude review

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beyond the inline nits: docs/migration-SKILL.md:101 still states Zod schemas "are no longer part of the public API" and points users at type guards instead — that prose now contradicts this subpath and should mention /zod-schemas as the v1-compat option. Also worth confirming whether @modelcontextprotocol/client should get the same subpath, since v1's types.js served both sides.

Extended reasoning...

Overview

Adds a new public subpath @modelcontextprotocol/server/zod-schemas that re-exports ~150 Zod schema constants (plus OAuth/auth schemas from core) as a v1→v2 compat shim. Touches core/package.json (new ./schemas export on the private core package), server/package.json exports map, a new zodSchemas.ts entry, tsdown/tsconfig/typedoc plumbing, a compat test, and a changeset.

Security risks

None — pure re-exports of existing internal constants; no new logic, auth, or I/O paths.

Level of scrutiny

Moderate-to-high. Per REVIEW.md, "every new export is intentional" and "burden of proof is on addition" — this is a deliberate public-API-surface expansion (an entire subpath), even if framed as deprecated. A maintainer should sign off on the surface shape, especially given the export * pulls in three non-v1 helper functions (getResultSchema/getRequestSchema/getNotificationSchema) and the changeset only promises *Schema constants.

Other factors

  • docs/migration-SKILL.md:101 and docs/migration.md (around L447–503) currently tell users Zod schemas are gone and to use type guards / specTypeSchema; that guidance now needs a pointer to this compat subpath (REVIEW.md "flag prose that now contradicts the implementation").
  • v1's @modelcontextprotocol/sdk/types.js was consumed by client-side code too (e.g., validating CallToolResult), but only the server package gets the subpath here — may be intentional, but worth an explicit call.
  • Core package is private: true and bundled via noExternal, so the new ./schemas core export doesn't leak externally — that part is fine.
  • Test coverage is minimal (instanceof + one parse) but adequate for a re-export shim.

Comment on lines +1 to +4
/**
* These are Zod schemas; their TS type may change with internal Zod upgrades.
* For runtime validation use them as `Schema.parse(value)`.
*/
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The PR description positions this as a "@deprecated shim. Removed in v3", but the module-level JSDoc only warns about Zod type instability — there's no @deprecated tag, no mention of v3 removal, and no pointer to the v2-native alternative (specTypeSchema() from #1887). Since this file is also excluded from typedoc, the source comment is the only place a consumer can learn this subpath is a temporary compat surface — consider adding that context here.

Extended reasoning...

What's missing

The PR description and changeset both frame /zod-schemas as a deprecated v1-compat shim slated for removal in v3, with specTypeSchema() (#1887) as the v2-native replacement. But the file-level JSDoc at lines 1-4 says only:

These are Zod schemas; their TS type may change with internal Zod upgrades. For runtime validation use them as Schema.parse(value).

There is no @deprecated tag, no mention that this is a v1-compat bridge, no v3 removal notice, and no pointer to the preferred alternative. A developer landing on this file (or hovering the module specifier in their IDE) gets zero signal that they're on a temporary surface.

Step-by-step

  1. A v1 consumer follows the migration guide and switches @modelcontextprotocol/sdk/types.js@modelcontextprotocol/server/zod-schemas.
  2. They cmd-click into the source or read the emitted .d.mts to see what's available.
  3. The header comment tells them "TS type may change with Zod upgrades" — a stability caveat, not a deprecation.
  4. Nothing tells them this subpath will disappear in v3, or that specTypeSchema() is the forward path. They reasonably treat it as a stable public API.
  5. typedoc.json excludes **/zodSchemas.ts, so generated docs won't carry this either — the source comment is the only documentation surface for this subpath.

Addressing the counter-arguments

  • "Module-level @deprecated doesn't propagate to import sites" — correct, and per-export tagging is impractical given export * over ~150 schemas. This nit is not asking for IDE strikethrough on every symbol; it's asking that the one comment that does exist state the deprecation intent and migration path. Prose in the module JSDoc is cheap and is exactly where a reader looks.
  • "typedoc excludes it, so it's intentionally undocumented" — that exclusion is presumably to keep ~150 internal Zod constants out of the API reference. It strengthens rather than weakens the case: with generated docs suppressed, the source comment is the only place left to record "this is a shim, removed in v3, prefer X".
  • "The PR description's '@deprecated' is colloquial, not a JSDoc promise" — fair, but the repo uses @deprecated JSDoc tags elsewhere (e.g. the Server class, allowedHosts/allowedOrigins) for exactly this kind of "works now, going away later" signal, so it's established convention here.
  • "JSONRPCErrorSchema's @deprecated already covers it" — no, that tag (line 23) is about the v1→v2 rename (JSONRPCErrorSchemaJSONRPCErrorResponseSchema) and correctly points at the new name within this module. It says nothing about the subpath itself being a shim.

Suggested fix

/**
 * v1-compat re-export of the internal Zod `*Schema` constants.
 *
 * @deprecated This subpath is a v1 compatibility shim and will be removed in v3.
 * Prefer {@link specTypeSchema} for runtime validation, or import the TS types directly.
 *
 * These are Zod schemas; their TS type may change with internal Zod upgrades.
 * For runtime validation use them as `Schema.parse(value)`.
 */

Documentation/DX only — no runtime impact, hence nit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in f7e391b: added @packageDocumentation block with @deprecated and specTypeSchema() pointer; per series policy we don't promise a specific removal version.

Comment thread packages/server/src/zodSchemas.ts Outdated
OptionalSafeUrlSchema,
SafeUrlSchema
} from '@modelcontextprotocol/core';
export * from '@modelcontextprotocol/core/schemas';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The export * here re-exports more than *Schema constants — it also pulls in the internal lookup helpers getResultSchema, getRequestSchema, and getNotificationSchema (schemas.ts:2186/2222/2230), which were never in v1's types.js and aren't covered by the changeset's "*Schema constants" promise. Per CLAUDE.md §Public API Exports, public entry points should use explicit named exports rather than export * to keep the surface intentional; consider enumerating the schema constants explicitly (or noting in the changeset that the helpers are intentionally included).

Extended reasoning...

What's leaking. packages/core/src/types/schemas.ts exports three helper functions in addition to the *Schema Zod constants:

  • getResultSchema(method) — L2186
  • getRequestSchema(method) — L2222
  • getNotificationSchema(method) — L2230

These are method→schema lookup tables used internally (e.g. packages/server/src/experimental/tasks/server.ts via the private core barrel). They are not Zod schema constants and did not exist in v1's @modelcontextprotocol/sdk/types.js.

How it leaks. packages/server/src/zodSchemas.ts:21 does export * from '@modelcontextprotocol/core/schemas'. A wildcard re-export carries every named binding from the source module, so all three get*Schema functions become part of the public @modelcontextprotocol/server/zod-schemas subpath surface.

Step-by-step proof.

  1. core/src/types/schemas.ts has export function getResultSchema(...) at L2186 (and the other two at L2222/L2230).
  2. core/package.json now maps ./schemas./src/types/schemas.ts, so those exports are reachable as @modelcontextprotocol/core/schemas.
  3. server/src/zodSchemas.ts:21 does export * from '@modelcontextprotocol/core/schemas'.
  4. server/package.json maps ./zod-schemasdist/zodSchemas.mjs.
  5. Therefore a consumer can write import { getResultSchema } from '@modelcontextprotocol/server/zod-schemas' and it will resolve and work at runtime — even though the PR/changeset only promise "*Schema constants" and these helpers were never part of the v1 compat surface being recreated.

Why the existing code doesn't prevent it. The explicit named-export blocks at L5–20 and L22–25 only add specific symbols; they don't constrain the wildcard. Nothing in schemas.ts marks the helpers @internal, and the typedoc.json exclusion only hides the file from docs — it doesn't change what ships in dist/zodSchemas.mjs.

Why it matters / repo guidance. CLAUDE.md §Public API Exports says: "Use explicit named exports, not export *" and "Internal helpers should stay in the core internal barrel and not be added to … package index files." The intent is that every public export is deliberate. Once these helpers are reachable from a published subpath (even a @deprecated one), they become API surface that has to be supported until the v3 removal, and the changeset description ("Re-exports the *Schema Zod constants") no longer matches what actually ships.

Impact. Low — this is API-surface hygiene on an already-deprecated compat shim, not a correctness bug. But it does (a) violate an explicit repo convention, (b) widen the compat surface beyond what v1 actually offered, and (c) create a docs/implementation mismatch with the changeset.

Fix. Replace the export * with an explicit list of the *Schema constants (matching what v1's types.js exposed), or — if the helpers are intentionally being made public — call that out in the changeset and TSDoc so it's a deliberate decision rather than an accidental leak.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented the get*Schema lookup helpers in the changeset rather than excluding them: enumerating ~156 named constants to drop 3 helpers is more churn than the benefit, and the helpers serve the same v1-compat use case (look up the schema for a method string). f7e391b.

@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-zod-schemas-subpath branch from 54f3063 to a860d7e Compare April 16, 2026 19:58
Re-exports the internal *Schema Zod constants (CallToolRequestSchema,
JSONRPCMessageSchema, etc.) from a deprecated /zod-schemas subpath so v1
code that imported schemas from @modelcontextprotocol/sdk/types.js has a
single drop-in target.

- packages/server/src/zodSchemas.ts: re-export barrel (deprecated module)
- packages/server/package.json: ./zod-schemas exports entry
- packages/server/tsdown.config.ts: build entry + dts path mapping
- packages/server/tsconfig.json: path mappings for core/schemas and the
  self-reference subpath
- packages/core/package.json: internal ./schemas subpath (core is private,
  consumed only by sibling packages)
- compat test asserting the import resolves and schemas parse

The schemas remain an internal implementation detail; their Zod major
version is not covered by semver. Subpath will be removed in v3.
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-zod-schemas-subpath branch from a860d7e to f7e391b Compare April 16, 2026 20:35
@felixweinberger
Copy link
Copy Markdown
Contributor Author

Re the review-body points: migration-SKILL.md:101 updated to mention the subpath. A separate @modelcontextprotocol/client/zod-schemas mirror isn't needed: the schemas are protocol-level (not server-specific), and the @modelcontextprotocol/sdk meta-package re-exports this subpath at sdk/types.js for both client and server consumers.

@claude review

@claude
Copy link
Copy Markdown

claude bot commented Apr 16, 2026

Review in progress

  • Gather context from PR
  • Read changed files (current branch state)
  • Review implementation
  • Post findings

View job run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2-bc v2 backwards-compatibility series

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant