Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "modelcontextprotocol/typescript-sdk" }],
"commit": false,
"fixed": [],
"fixed": [
[
"@modelcontextprotocol/core",
"@modelcontextprotocol/client",
"@modelcontextprotocol/server",
"@modelcontextprotocol/server-legacy"
]
],
"linked": [],
"access": "public",
"baseBranch": "main",
Expand Down
8 changes: 8 additions & 0 deletions .changeset/schemas-source-home.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@modelcontextprotocol/core': minor
'@modelcontextprotocol/client': minor
'@modelcontextprotocol/server': minor
'@modelcontextprotocol/server-legacy': minor
---

Move the schema source modules (spec schemas, OAuth schemas, protocol constants) into `@modelcontextprotocol/core` and resolve them from there as a regular runtime dependency instead of bundling a private copy into each package. An application importing more than one of the packages now evaluates a single shared schema graph with shared object identity. `@modelcontextprotocol/core` gains a `./internal` subpath (SDK-internal contract; may change in any release) and the four packages now version together.
19 changes: 10 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Include what changed, why, and how to migrate. Search for related sections and g

### JSDoc `@example` Code Snippets

JSDoc `@example` tags should pull type-checked code from companion `.examples.ts` files (e.g., `client.ts` → `client.examples.ts`). Use `` ```ts source="./file.examples.ts#regionName" `` fences referencing `//#region regionName` blocks; region names follow `exportedName_variant` or `ClassName_methodName_variant` pattern (e.g., `applyMiddlewares_basicUsage`, `Client_connect_basicUsage`). For whole-file inclusion (any file type), omit the `#regionName`.
JSDoc `@example` tags should pull type-checked code from companion `.examples.ts` files (e.g., `client.ts` → `client.examples.ts`). Use ` ```ts source="./file.examples.ts#regionName" ` fences referencing `//#region regionName` blocks; region names follow `exportedName_variant` or `ClassName_methodName_variant` pattern (e.g., `applyMiddlewares_basicUsage`, `Client_connect_basicUsage`). For whole-file inclusion (any file type), omit the `#regionName`.

Run `pnpm sync:snippets` to sync example content into JSDoc comments and markdown files.

Expand All @@ -70,9 +70,10 @@ The SDK separates internal code from the public API surface:
- **`@modelcontextprotocol/core-internal`** (main entry, `packages/core-internal/src/index.ts`) — Internal barrel. Exports everything (including Zod schemas, Protocol class, stdio utils). Only consumed by sibling packages within the monorepo (`private: true`).
- **`@modelcontextprotocol/core-internal/public`** (`packages/core-internal/src/exports/public/index.ts`) — Curated public API. Exports only TypeScript types, error classes, constants, and guards. Re-exported by client and server packages.
- **`@modelcontextprotocol/client`** and **`@modelcontextprotocol/server`** (`packages/*/src/index.ts`) — Final public surface. Package-specific exports (named explicitly) plus re-exports from `core-internal/public`.
- **`@modelcontextprotocol/core`** (`packages/core/src/index.ts`) — Public Zod-schema package. Re-exports **only** the `*Schema` Zod constants (MCP spec + OAuth/OpenID), bundled from `core-internal` at build time. The published home for raw runtime validation (`CallToolResultSchema.parse(...)`); runtime-neutral (`zod` is its only dependency). Not consumed by the sibling packages`client`/`server` keep their own bundled schema copies and stay Zod-free in their public surface.
- **`@modelcontextprotocol/core`** (`packages/core/src/index.ts`) — Public Zod-schema package and the canonical home of the schema source modules (`src/schemas.ts`, `src/auth.ts`, `src/constants.ts`). The root entry re-exports **only** the `*Schema` Zod constants (MCP spec + OAuth/OpenID) — the published home for raw runtime validation (`CallToolResultSchema.parse(...)`); runtime-neutral (`zod` is its only dependency). The `./internal` subpath re-exports the schema modules wholesale for the sibling packages: `core-internal` re-exports them at the old module paths, and the `client`/`server`/`server-legacy` bundles resolve `@modelcontextprotocol/core/internal` as a real external dependency instead of carrying their own schema copies (their public surfaces stay Zod-free).

When modifying exports:

- Use explicit named exports, not `export *`, in package `index.ts` files and `core-internal/public`.
- Adding a symbol to a package `index.ts` makes it public API — do so intentionally.
- Internal helpers should stay in the core internal barrel and not be added to `core-internal/public` or package index files.
Expand Down Expand Up @@ -197,14 +198,14 @@ The `ctx` parameter in handlers provides a structured context:

- `sessionId?`: Transport session identifier
- `mcpReq`: Request-level concerns
- `id`: JSON-RPC message ID
- `method`: Request method string (e.g., 'tools/call')
- `_meta?`: Request metadata
- `signal`: AbortSignal for cancellation
- `send(request, schema, options?)`: Send related request (for bidirectional flows)
- `notify(notification)`: Send related notification back
- `id`: JSON-RPC message ID
- `method`: Request method string (e.g., 'tools/call')
- `_meta?`: Request metadata
- `signal`: AbortSignal for cancellation
- `send(request, schema, options?)`: Send related request (for bidirectional flows)
- `notify(notification)`: Send related notification back
- `http?`: HTTP transport info (undefined for stdio)
- `authInfo?`: Validated auth token info
- `authInfo?`: Validated auth token info

**`ServerContext`** extends `BaseContext.mcpReq` and `BaseContext.http?` via type intersection:

Expand Down
3 changes: 2 additions & 1 deletion docs/advanced/wire-schemas.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
shape: how-to
---

# Wire schemas

`@modelcontextprotocol/core` exports the **wire schemas** — the exact Zod constants the SDK validates protocol and OAuth payloads against — for code that holds raw JSON instead of SDK objects.
Expand Down Expand Up @@ -56,7 +57,7 @@ These are the `*Schema` constants v1 exported from `@modelcontextprotocol/sdk/ty

If you build with `McpServer` or `Client`, skip this package: [tools](../servers/tools.md) arrive in your handler already validated, and [tool calls](../clients/calling.md) come back as typed results. Reach for `@modelcontextprotocol/core` when nothing stands between you and the JSON — gateways, proxies, test harnesses, [worker fleets](./gateway.md).

Install it separately (`npm install @modelcontextprotocol/core`) — `@modelcontextprotocol/server` and `@modelcontextprotocol/client` keep a Zod-free public surface and never depend on it. The package is runtime-neutral; `zod` is its only dependency.
`@modelcontextprotocol/server` and `@modelcontextprotocol/client` keep a Zod-free public surface, but they resolve their shared schema graph from this package at runtime, so it already arrives transitively in your tree. Add it to your own `dependencies` (`npm install @modelcontextprotocol/core`) when you import from it directly. The package is runtime-neutral; `zod` is its only dependency.

## Pick the schema for the message you hold

Expand Down
2 changes: 1 addition & 1 deletion docs/get-started/packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@

## Reach for `core` only to validate raw wire JSON

`@modelcontextprotocol/core` exports the Zod schema constants the SDK validates protocol payloads against, for code that handles raw JSON-RPC payloads itself — gateways, proxies, log pipelines. Neither `server` nor `client` exports a Zod schema, and the matching TypeScript types ship with both, so if you only call `registerTool` and `callTool` you never install it. [Wire schemas](../advanced/wire-schemas.md) is the how-to.
`@modelcontextprotocol/core` exports the Zod schema constants the SDK validates protocol payloads against, for code that handles raw JSON-RPC payloads itself — gateways, proxies, log pipelines. Neither `server` nor `client` exports a Zod schema, and the matching TypeScript types ship with both, so if you only call `registerTool` and `callTool` you never import it directly — it arrives transitively, since `server` and `client` resolve their shared schema graph from it at runtime. [Wire schemas](../advanced/wire-schemas.md) is the how-to.

Check warning on line 69 in docs/get-started/packages.md

View check run for this annotation

Claude / Claude Code Review

Migration guide still claims core is not required by client/server (third stale doc instance missed by 68688c9)

A third instance of the stale core-topology claim survives in `docs/migration/upgrade-to-v2.md:1328`: it still says `@modelcontextprotocol/core` "is **not** required by `client` / `server` — install it only if you import the raw schemas directly", which is false after this PR makes core an exact-pinned runtime dependency. Commit 68688c9 fixed the same sentence in `packages.md` and `wire-schemas.md` but missed this one — reword it to match the corrected "arrives transitively as the shared runtime

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 A third instance of the stale core-topology claim survives in docs/migration/upgrade-to-v2.md:1328: it still says @modelcontextprotocol/core "is not required by client / server — install it only if you import the raw schemas directly", which is false after this PR makes core an exact-pinned runtime dependency. Commit 68688c9 fixed the same sentence in packages.md and wire-schemas.md but missed this one — reword it to match the corrected "arrives transitively as the shared runtime schema graph" language.

Extended reasoning...

What's stale. This PR makes @modelcontextprotocol/core an exact-pinned runtime dependency of client, server, and server-legacy (each package.json now declares "@modelcontextprotocol/core": "workspace:*"), and the built bundles resolve @modelcontextprotocol/core/internal as a live external import — pinned by the new coreBoundary.test.ts. The earlier review round flagged two docs asserting the old topology, and commit 68688c9 fixed both (docs/get-started/packages.md:69 and docs/advanced/wire-schemas.md:60 now say core "arrives transitively" as the shared runtime schema graph). But a third instance survived: docs/migration/upgrade-to-v2.md:1326-1329 still reads "It is runtime-neutral (its only dependency is zod) and is not required by client / server — install it only if you import the raw schemas directly."\n\nStep-by-step proof that the sentence is false post-merge. (1) A v1 user upgrading per this guide runs npm install @modelcontextprotocol/client (or server). (2) npm reads the client's dependencies, resolves @modelcontextprotocol/core transitively, and it lands in node_modules on every install — directly contradicting "not required by client / server". (3) At runtime the installed client's dist chunks contain live import { … } from '@modelcontextprotocol/core/internal' statements, so the dependency is real at module-resolution level, not just in the manifest. A grep over docs/ and package READMEs confirms this is the only surviving occurrence of the old "not required / never depend / never install" framing after 68688c9.\n\nWhy it matters beyond mere staleness. The "install it only if…" framing in the v1→v2 migration guide — the doc most upgrading users actually read — invites users to pin their own core version alongside client/server. Because core/client/server/server-legacy now release as a changesets fixed group with exact pins, a version-skewed self-pinned core resolved in place of the paired one fails at import time with ERR_PACKAGE_PATH_NOT_EXPORTED (the PR description names this failure mode). So the sentence steers readers toward exactly the configuration the fixed group and exact pins were added to prevent.\n\nWhy nothing in the PR catches it. The drift guards added here (coreBoundary.test.ts, schemaShims.test.ts, packageTopologyPins.test.ts) pin build artifacts and manifests, not prose. The doc-topology fix in 68688c9 was scoped to the two files the earlier comment named, and the migration guide was not in the diff.\n\nFix. A one-sentence reword at upgrade-to-v2.md:1328 matching the corrected language in packages.md/wire-schemas.md — e.g.: "It is runtime-neutral (its only dependency is zod). client / server resolve their shared schema graph from it at runtime, so it already arrives transitively in your tree; add it to your own dependencies only when you import the raw schemas directly." The runtime-neutral / zod-only half of the claim remains true and should stay.\n\nSeverity. Nit: doc-only — nothing breaks at runtime if merged as-is, and the "only import it directly if you need raw schemas" advice half remains sound. But it is the same class of stale prose the author already accepted and fixed twice in this PR, so finishing the sweep is cheap and worthwhile.


## Leave `server-legacy` and `codemod` to the migration guide

Expand Down
3 changes: 3 additions & 0 deletions examples/client-quickstart/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"@modelcontextprotocol/core-internal": [
"./node_modules/@modelcontextprotocol/client/node_modules/@modelcontextprotocol/core-internal/src/index.ts"
],
"@modelcontextprotocol/core/internal": [
"./node_modules/@modelcontextprotocol/client/node_modules/@modelcontextprotocol/core/src/internal.ts"
],
"@modelcontextprotocol/core-internal/public": [
"./node_modules/@modelcontextprotocol/client/node_modules/@modelcontextprotocol/core-internal/src/exports/public/index.ts"
]
Expand Down
3 changes: 3 additions & 0 deletions examples/server-quickstart/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"@modelcontextprotocol/core-internal": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core-internal/src/index.ts"
],
"@modelcontextprotocol/core/internal": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/src/internal.ts"
],
"@modelcontextprotocol/core-internal/public": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core-internal/src/exports/public/index.ts"
]
Expand Down
3 changes: 3 additions & 0 deletions examples/shared/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"@modelcontextprotocol/core-internal": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core-internal/src/index.ts"
],
"@modelcontextprotocol/core/internal": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/src/internal.ts"
],
"@modelcontextprotocol/core-internal/public": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core-internal/src/exports/public/index.ts"
],
Expand Down
7 changes: 1 addition & 6 deletions examples/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@
"@modelcontextprotocol/core-internal": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core-internal/src/index.ts"
],
"@modelcontextprotocol/core/internal": ["./node_modules/@modelcontextprotocol/core/src/internal.ts"],
"@modelcontextprotocol/core-internal/public": [
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core-internal/src/exports/public/index.ts"
],
"@modelcontextprotocol/core-internal/schemas": [
"./node_modules/@modelcontextprotocol/core/node_modules/@modelcontextprotocol/core-internal/src/types/schemas.ts"
],
"@modelcontextprotocol/core-internal/auth": [
"./node_modules/@modelcontextprotocol/core/node_modules/@modelcontextprotocol/core-internal/src/shared/auth.ts"
],
"@mcp-examples/shared": ["./node_modules/@mcp-examples/shared/src/index.ts"]
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
"test:watch": "vitest"
},
"dependencies": {
"@modelcontextprotocol/core": "workspace:*",
Comment thread
felixweinberger marked this conversation as resolved.
"cross-spawn": "catalog:runtimeClientOnly",
"eventsource": "catalog:runtimeClientOnly",
"eventsource-parser": "catalog:runtimeClientOnly",
Expand Down
13 changes: 6 additions & 7 deletions packages/client/test/client/barrelClean.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { execFileSync } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

import { beforeAll, describe, expect, test } from 'vitest';

import { ensureBuilt } from '../helpers/ensureBuilt';

const pkgDir = join(dirname(fileURLToPath(import.meta.url)), '../..');
const distDir = join(pkgDir, 'dist');
const requireDist = createRequire(join(pkgDir, 'package.json'));
Expand Down Expand Up @@ -37,11 +38,9 @@ function rootExportBlockOf(content: string): string {
}

describe('@modelcontextprotocol/client root entry is browser-safe', () => {
beforeAll(() => {
if (!existsSync(join(distDir, 'index.mjs')) || !existsSync(join(distDir, 'stdio.mjs'))) {
execFileSync('pnpm', ['build'], { cwd: pkgDir, stdio: 'inherit' });
}
}, 60_000);
beforeAll(async () => {
await ensureBuilt(pkgDir);
}, 180_000);

test('dist/index.mjs contains no process-spawning runtime imports', () => {
const entry = join(distDir, 'index.mjs');
Expand Down
Loading
Loading