Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
bf993ef
Move the neutral schema modules into @modelcontextprotocol/core
felixweinberger Jul 10, 2026
25ba63b
Update drift guards and the workers test for the schema modules' new …
felixweinberger Jul 10, 2026
f906078
Guard the schema-module boundary against skew, drift, and re-inlining
felixweinberger Jul 10, 2026
e55dc27
Add changeset for the schema source move
felixweinberger Jul 10, 2026
edb1ab0
Serialize on-demand dist builds across parallel test workers
felixweinberger Jul 10, 2026
64c83df
Scale the dist-build timeout ladder to observed build times
felixweinberger Jul 10, 2026
68688c9
Address review: node10 types resolution, docs topology, boundary test…
felixweinberger Jul 10, 2026
cbed781
Move the neutral schema modules into @modelcontextprotocol/core
felixweinberger Jul 10, 2026
ea7a0d5
Update drift guards and the workers test for the schema modules' new …
felixweinberger Jul 10, 2026
87d9052
Guard the schema-module boundary against skew, drift, and re-inlining
felixweinberger Jul 10, 2026
d6e90f3
Add changeset for the schema source move
felixweinberger Jul 10, 2026
c40a753
Serialize on-demand dist builds across parallel test workers
felixweinberger Jul 10, 2026
6088664
Scale the dist-build timeout ladder to observed build times
felixweinberger Jul 10, 2026
89b6dc9
Address review: node10 types resolution, docs topology, boundary test…
felixweinberger Jul 10, 2026
999e6f6
Merge branch 'main' into fweinberger/core-schemas-home
felixweinberger Jul 13, 2026
2f47583
Address review: extend wire-layer guards to the core seam, harden ens…
felixweinberger Jul 13, 2026
f5d53b8
Merge branch update
felixweinberger Jul 13, 2026
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 @@ Four adapters exist: `@modelcontextprotocol/node` for Node's built-in `http` ser

## 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.
Comment thread
felixweinberger marked this conversation as resolved.

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

Expand Down
7 changes: 4 additions & 3 deletions docs/migration/upgrade-to-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ hierarchy also exposes the same check as an explicit static guard
TypeScript — use whichever style your codebase prefers; both read the same brand.
Fine print (applies equally to `instanceof` and `isInstance`):

- **Version skew** — matching needs *both* copies at a brand-aware release; against an
- **Version skew** — matching needs _both_ copies at a brand-aware release; against an
older copy, behavior degrades to plain prototype `instanceof` (false across bundles).
During mixed-version rollouts, recognize errors without class identity: match
`error.name` plus the class's discriminant field (`code`, `status`), or reconstruct
Expand Down Expand Up @@ -1325,8 +1325,9 @@ if (CallToolResultSchema.safeParse(value).success) { ... }

`@modelcontextprotocol/core` is the canonical home for the spec's Zod schema constants
(and the OAuth/OpenID metadata schemas). 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.
`zod`) and arrives transitively as the shared runtime schema graph of `client` /
`server` — add it to your own `dependencies` only when you import the raw schemas
directly.

If you would rather keep your project Zod-free, the **`isSpecType` / `specTypeSchemas`**
alternatives are exported from `@modelcontextprotocol/client` and `…/server`:
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