Skip to content
Draft
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
6 changes: 6 additions & 0 deletions .changeset/stdio-subpath-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@modelcontextprotocol/client': minor
'@modelcontextprotocol/server': minor
---

Move stdio transports to a `./stdio` subpath export. Import `StdioClientTransport`, `getDefaultEnvironment`, `DEFAULT_INHERITED_ENV_VARS`, and `StdioServerParameters` from `@modelcontextprotocol/client/stdio`, and `StdioServerTransport` from `@modelcontextprotocol/server/stdio`. The package root entries no longer pull in `node:child_process`, `node:stream`, or `cross-spawn`, fixing bundling for browser and Cloudflare Workers targets. Node.js, Bun, and Deno consumers update the import path; runtime behavior is unchanged.
3 changes: 2 additions & 1 deletion docs/client-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ First, let's set up our imports and create the basic client class in `src/index.

```ts source="../examples/client-quickstart/src/index.ts#prelude"
import Anthropic from '@anthropic-ai/sdk';
import { Client, StdioClientTransport } from '@modelcontextprotocol/client';
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
import readline from 'readline/promises';

const ANTHROPIC_MODEL = 'claude-sonnet-4-5';
Expand Down
2 changes: 1 addition & 1 deletion docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import {
SdkError,
SdkErrorCode,
SSEClientTransport,
StdioClientTransport,
StreamableHTTPClientTransport
} from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
```

## Connecting to a server
Expand Down
6 changes: 3 additions & 3 deletions docs/migration-SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table.
| `@modelcontextprotocol/sdk/client/auth.js` | `@modelcontextprotocol/client` |
| `@modelcontextprotocol/sdk/client/streamableHttp.js` | `@modelcontextprotocol/client` |
| `@modelcontextprotocol/sdk/client/sse.js` | `@modelcontextprotocol/client` |
| `@modelcontextprotocol/sdk/client/stdio.js` | `@modelcontextprotocol/client` |
| `@modelcontextprotocol/sdk/client/stdio.js` | `@modelcontextprotocol/client/stdio` |
| `@modelcontextprotocol/sdk/client/websocket.js` | REMOVED (use Streamable HTTP or stdio; implement `Transport` for custom needs) |

### Server imports
Expand All @@ -51,7 +51,7 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table.
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `@modelcontextprotocol/sdk/server/mcp.js` | `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/server/index.js` | `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server/stdio` |
| `@modelcontextprotocol/sdk/server/streamableHttp.js` | `@modelcontextprotocol/node` (class renamed to `NodeStreamableHTTPServerTransport`) OR `@modelcontextprotocol/server` (web-standard `WebStandardStreamableHTTPServerTransport` for Cloudflare Workers, Deno, etc.) |
| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP) |
| `@modelcontextprotocol/sdk/server/auth/*` | REMOVED (use external auth library) |
Expand All @@ -66,7 +66,7 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table.
| `@modelcontextprotocol/sdk/shared/transport.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/shared/uriTemplate.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/shared/auth.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/shared/stdio.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/shared/stdio.js` | `@modelcontextprotocol/client/stdio` or `@modelcontextprotocol/server/stdio` |

Notes:

Expand Down
6 changes: 4 additions & 2 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
**After (v2):**

```typescript
import { Client, StreamableHTTPClientTransport, StdioClientTransport } from '@modelcontextprotocol/client';
import { McpServer, StdioServerTransport, WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
import { McpServer, WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';

// Node.js HTTP server transport is in the @modelcontextprotocol/node package
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
Expand Down
3 changes: 2 additions & 1 deletion docs/server-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ Now let's dive into building your server.
Add these to the top of your `src/index.ts`:

```ts source="../examples/server-quickstart/src/index.ts#prelude"
import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const NWS_API_BASE = 'https://api.weather.gov';
Expand Down
3 changes: 2 additions & 1 deletion docs/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import { randomUUID } from 'node:crypto';
import { createMcpExpressApp } from '@modelcontextprotocol/express';
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
import type { CallToolResult, ResourceLink } from '@modelcontextprotocol/server';
import { completable, McpServer, ResourceTemplate, StdioServerTransport } from '@modelcontextprotocol/server';
import { completable, McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
```

Expand Down
3 changes: 2 additions & 1 deletion examples/client-quickstart/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//#region prelude
import Anthropic from '@anthropic-ai/sdk';
import { Client, StdioClientTransport } from '@modelcontextprotocol/client';
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
import readline from 'readline/promises';

const ANTHROPIC_MODEL = 'claude-sonnet-4-5';
Expand Down
2 changes: 1 addition & 1 deletion examples/client/src/clientGuide.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import {
SdkError,
SdkErrorCode,
SSEClientTransport,
StdioClientTransport,
StreamableHTTPClientTransport
} from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
//#endregion imports

// ---------------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion examples/server-quickstart/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//#region prelude
import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const NWS_API_BASE = 'https://api.weather.gov';
Expand Down
3 changes: 2 additions & 1 deletion examples/server/src/arktypeExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* ArkType implements the Standard Schema spec with built-in JSON Schema conversion.
*/

import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import { type } from 'arktype';

const server = new McpServer({
Expand Down
3 changes: 2 additions & 1 deletion examples/server/src/mcpServerOutputSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* This demonstrates how to easily create tools with structured output
*/

import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const server = new McpServer({
Expand Down
3 changes: 2 additions & 1 deletion examples/server/src/serverGuide.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { randomUUID } from 'node:crypto';
import { createMcpExpressApp } from '@modelcontextprotocol/express';
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
import type { CallToolResult, ResourceLink } from '@modelcontextprotocol/server';
import { completable, McpServer, ResourceTemplate, StdioServerTransport } from '@modelcontextprotocol/server';
import { completable, McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
//#endregion imports

Expand Down
3 changes: 2 additions & 1 deletion examples/server/src/toolWithSampleServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Run with: pnpm tsx src/toolWithSampleServer.ts

import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const mcpServer = new McpServer({
Expand Down
3 changes: 2 additions & 1 deletion examples/server/src/valibotExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* StandardJSONSchemaV1-compliant schemas.
*/

import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import { toStandardJsonSchema } from '@valibot/to-json-schema';
import * as v from 'valibot';

Expand Down
4 changes: 4 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
},
"./stdio": {
"types": "./dist/stdio.d.mts",
"import": "./dist/stdio.mjs"
},
"./validators/cf-worker": {
"types": "./dist/validators/cfWorker.d.mts",
"import": "./dist/validators/cfWorker.mjs"
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ export type { LoggingOptions, Middleware, RequestLogger } from './client/middlew
export { applyMiddlewares, createMiddleware, withLogging, withOAuth } from './client/middleware.js';
export type { SSEClientTransportOptions } from './client/sse.js';
export { SSEClientTransport, SseError } from './client/sse.js';
export type { StdioServerParameters } from './client/stdio.js';
export { DEFAULT_INHERITED_ENV_VARS, getDefaultEnvironment, StdioClientTransport } from './client/stdio.js';
// StdioClientTransport, getDefaultEnvironment, DEFAULT_INHERITED_ENV_VARS, StdioServerParameters are exported from
// the './stdio' subpath to keep the root entry free of process-spawning runtime dependencies (child_process, cross-spawn).
export type {
ReconnectionScheduler,
StartSSEOptions,
Expand Down
8 changes: 8 additions & 0 deletions packages/client/src/stdio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Subpath entry for the stdio client transport.
//
// Exported separately from the root entry so that bundling `@modelcontextprotocol/client` for browser or
// Cloudflare Workers targets does not pull in `node:child_process`, `node:stream`, or `cross-spawn`. Import
// from `@modelcontextprotocol/client/stdio` only in process-spawning runtimes (Node.js, Bun, Deno).

export type { StdioServerParameters } from './client/stdio.js';
export { DEFAULT_INHERITED_ENV_VARS, getDefaultEnvironment, StdioClientTransport } from './client/stdio.js';
36 changes: 36 additions & 0 deletions packages/client/test/client/barrelClean.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

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

const distDir = join(dirname(fileURLToPath(import.meta.url)), '../../dist');
const NODE_ONLY = /\b(child_process|cross-spawn|node:stream|node:child_process)\b/;

function chunkImportsOf(entryPath: string): string[] {
const src = readFileSync(entryPath, 'utf8');
return [...src.matchAll(/from\s+["']\.\/(.+?\.mjs)["']/g)].map(m => join(distDir, m[1]!));
}

describe('@modelcontextprotocol/client root entry is browser-safe', () => {
test('dist/index.mjs contains no process-spawning runtime imports', () => {
const entry = join(distDir, 'index.mjs');
expect(readFileSync(entry, 'utf8')).not.toMatch(NODE_ONLY);
});

test('chunks transitively imported by dist/index.mjs contain no process-spawning runtime imports', () => {
const entry = join(distDir, 'index.mjs');
for (const chunk of chunkImportsOf(entry)) {
expect({ chunk, content: readFileSync(chunk, 'utf8') }).not.toEqual(
expect.objectContaining({ content: expect.stringMatching(NODE_ONLY) })
);
}
});

test('dist/stdio.mjs exists and exports StdioClientTransport', () => {
const stdio = readFileSync(join(distDir, 'stdio.mjs'), 'utf8');
expect(stdio).toMatch(/\bStdioClientTransport\b/);
expect(stdio).toMatch(/\bgetDefaultEnvironment\b/);
expect(stdio).toMatch(/\bDEFAULT_INHERITED_ENV_VARS\b/);
});
});
2 changes: 1 addition & 1 deletion packages/client/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default defineConfig({
failOnWarn: 'ci-only',
// 1. Entry Points
// Directly matches package.json include/exclude globs
entry: ['src/index.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/shimsBrowser.ts', 'src/validators/cfWorker.ts'],
entry: ['src/index.ts', 'src/stdio.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/shimsBrowser.ts', 'src/validators/cfWorker.ts'],

// 2. Output Configuration
format: ['esm'],
Expand Down
4 changes: 4 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
},
"./stdio": {
"types": "./dist/stdio.d.mts",
"import": "./dist/stdio.mjs"
},
"./validators/cf-worker": {
"types": "./dist/validators/cfWorker.d.mts",
"import": "./dist/validators/cfWorker.mjs"
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export type { HostHeaderValidationResult } from './server/middleware/hostHeaderV
export { hostHeaderValidationResponse, localhostAllowedHostnames, validateHostHeader } from './server/middleware/hostHeaderValidation.js';
export type { ServerOptions } from './server/server.js';
export { Server } from './server/server.js';
export { StdioServerTransport } from './server/stdio.js';
// StdioServerTransport is exported from the './stdio' subpath to keep the root entry free of process-stdio
// runtime dependencies (node:stream).
export type {
EventId,
EventStore,
Expand Down
7 changes: 7 additions & 0 deletions packages/server/src/stdio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Subpath entry for the stdio server transport.
//
// Exported separately from the root entry so that bundling `@modelcontextprotocol/server` for browser or
// Cloudflare Workers targets does not pull in `node:stream`. Import from `@modelcontextprotocol/server/stdio`
// only in process-stdio runtimes (Node.js, Bun, Deno).

export { StdioServerTransport } from './server/stdio.js';
34 changes: 34 additions & 0 deletions packages/server/test/server/barrelClean.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

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

const distDir = join(dirname(fileURLToPath(import.meta.url)), '../../dist');
const NODE_ONLY = /\b(node:stream|node:child_process)\b/;

function chunkImportsOf(entryPath: string): string[] {
const src = readFileSync(entryPath, 'utf8');
return [...src.matchAll(/from\s+["']\.\/(.+?\.mjs)["']/g)].map(m => join(distDir, m[1]!));
}

describe('@modelcontextprotocol/server root entry is browser-safe', () => {
test('dist/index.mjs contains no process-stdio runtime imports', () => {
const entry = join(distDir, 'index.mjs');
expect(readFileSync(entry, 'utf8')).not.toMatch(NODE_ONLY);
});

test('chunks transitively imported by dist/index.mjs contain no process-stdio runtime imports', () => {
const entry = join(distDir, 'index.mjs');
for (const chunk of chunkImportsOf(entry)) {
expect({ chunk, content: readFileSync(chunk, 'utf8') }).not.toEqual(
expect.objectContaining({ content: expect.stringMatching(NODE_ONLY) })
);
}
});

test('dist/stdio.mjs exists and exports StdioServerTransport', () => {
const stdio = readFileSync(join(distDir, 'stdio.mjs'), 'utf8');
expect(stdio).toMatch(/\bStdioServerTransport\b/);
});
});
2 changes: 1 addition & 1 deletion packages/server/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default defineConfig({
failOnWarn: 'ci-only',
// 1. Entry Points
// Directly matches package.json include/exclude globs
entry: ['src/index.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/validators/cfWorker.ts'],
entry: ['src/index.ts', 'src/stdio.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/validators/cfWorker.ts'],

// 2. Output Configuration
format: ['esm'],
Expand Down
3 changes: 2 additions & 1 deletion test/integration/test/__fixtures__/serverThatHangs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import process from 'node:process';
import { setInterval } from 'node:timers';

import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';

const transport = new StdioServerTransport();

Expand Down
3 changes: 2 additions & 1 deletion test/integration/test/__fixtures__/testServer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';

const transport = new StdioServerTransport();

Expand Down
6 changes: 4 additions & 2 deletions test/integration/test/processCleanup.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import path from 'node:path';
import { Readable, Writable } from 'node:stream';

import { Client, StdioClientTransport } from '@modelcontextprotocol/client';
import { Server, StdioServerTransport } from '@modelcontextprotocol/server';
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
import { Server } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';

// Use the local fixtures directory alongside this test file
const FIXTURES_DIR = path.resolve(__dirname, './__fixtures__');
Expand Down
Loading