Skip to content

Commit c77cee7

Browse files
authored
Merge branch 'main' into fix/auth-swallow-savetokens-2034
2 parents 676d040 + 5fc42e9 commit c77cee7

78 files changed

Lines changed: 9446 additions & 82 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/add-sdk-http-error.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@modelcontextprotocol/core": minor
3+
"@modelcontextprotocol/client": minor
4+
---
5+
6+
Add `SdkHttpError` subclass with typed `.status` / `.statusText` accessors for HTTP transport failures. `StreamableHTTPClientTransport` now throws `SdkHttpError` (which extends `SdkError`) for non-OK HTTP responses; `SSEClientTransport` throws `SdkHttpError` for 401-after-reauth (circuit breaker).

.changeset/spec-type-schema.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
'@modelcontextprotocol/server': minor
44
---
55

6-
Export `isSpecType` and `specTypeSchemas` records for runtime validation of any MCP spec type by name. `isSpecType.ContentBlock(value)` is a type predicate; `specTypeSchemas.ContentBlock` is a `StandardSchemaV1<ContentBlock>` validator. Guards are standalone functions, so `arr.filter(isSpecType.ContentBlock)` works. Also export the `SpecTypeName` and `SpecTypes` types.
6+
Export `isSpecType` and `specTypeSchemas` records for runtime validation of any MCP spec type by name. `isSpecType.ContentBlock(value)` is a type predicate; `specTypeSchemas.ContentBlock` is a `StandardSchemaV1Sync<ContentBlock>` validator`validate()` returns the result synchronously. Guards are standalone functions, so `arr.filter(isSpecType.ContentBlock)` works. Also export the `SpecTypeName`, `SpecTypes`, and `StandardSchemaV1Sync` types.

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ jobs:
4040
- name: Publish preview packages
4141
run:
4242
pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/client'
43-
'./packages/middleware/express' './packages/middleware/fastify' './packages/middleware/hono' './packages/middleware/node'
43+
'./packages/codemod' './packages/middleware/express' './packages/middleware/fastify' './packages/middleware/hono' './packages/middleware/node'

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ pnpm-lock.yaml
1212
# Ignore generated files
1313
src/spec.types.ts
1414

15+
# Batch test cloned repos and results
16+
packages/codemod/batch-test/repos
17+
packages/codemod/batch-test/results
18+
1519
# Quickstart examples uses 2-space indent to match ecosystem conventions
1620
examples/client-quickstart/
1721
examples/server-quickstart/

README.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
# MCP TypeScript SDK
22

3-
> [!IMPORTANT] **This is the `main` branch which contains v2 of the SDK (currently in development, pre-alpha).**
3+
<!-- prettier-ignore -->
4+
> [!IMPORTANT]
5+
> **This is the `main` branch which contains v2 of the SDK (currently in development, pre-alpha).**
46
>
57
> We anticipate a stable v2 release in Q1 2026. Until then, **v1.x remains the recommended version** for production use. v1.x will continue to receive bug fixes and security updates for at least 6 months after v2 ships to give people time to upgrade.
68
>
79
> For v1 documentation, see the [V1 API docs](https://ts.sdk.modelcontextprotocol.io/). For v2 API docs, see [`/v2/`](https://ts.sdk.modelcontextprotocol.io/v2/).
810
9-
![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fserver) ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fclient) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fserver)
11+
[![NPM Version - Server](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fserver?label=%40modelcontextprotocol%2Fserver)](https://www.npmjs.com/package/@modelcontextprotocol/server)
12+
[![NPM Version - Client](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fclient?label=%40modelcontextprotocol%2Fclient)](https://www.npmjs.com/package/@modelcontextprotocol/client) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fserver)
1013

1114
<details>
1215
<summary>Table of Contents</summary>
@@ -102,19 +105,19 @@ import * as z from 'zod/v4';
102105
const server = new McpServer({ name: 'greeting-server', version: '1.0.0' });
103106

104107
server.registerTool(
105-
'greet',
106-
{
107-
description: 'Greet someone by name',
108-
inputSchema: z.object({ name: z.string() }),
109-
},
110-
async ({ name }) => ({
111-
content: [{ type: 'text', text: `Hello, ${name}!` }],
112-
}),
108+
'greet',
109+
{
110+
description: 'Greet someone by name',
111+
inputSchema: z.object({ name: z.string() })
112+
},
113+
async ({ name }) => ({
114+
content: [{ type: 'text', text: `Hello, ${name}!` }]
115+
})
113116
);
114117

115118
async function main() {
116-
const transport = new StdioServerTransport();
117-
await server.connect(transport);
119+
const transport = new StdioServerTransport();
120+
await server.connect(transport);
118121
}
119122

120123
main();
@@ -125,7 +128,8 @@ Ready to build something real? Follow the step-by-step quickstart tutorials:
125128
- [Build a weather server](docs/server-quickstart.md) — server quickstart
126129
- [Build an LLM-powered chatbot](docs/client-quickstart.md) — client quickstart
127130

128-
The complete code for each tutorial is in [`examples/server-quickstart/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/server-quickstart/) and [`examples/client-quickstart/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/client-quickstart/). For more advanced runnable examples, see:
131+
The complete code for each tutorial is in [`examples/server-quickstart/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/server-quickstart/) and
132+
[`examples/client-quickstart/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/client-quickstart/). For more advanced runnable examples, see:
129133

130134
- [`examples/server/README.md`](examples/server/README.md) — server examples index
131135
- [`examples/client/README.md`](examples/client/README.md) — client examples index

docs/migration-SKILL.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,19 @@ Notes:
9595
| `ErrorCode` | `ProtocolErrorCode` |
9696
| `ErrorCode.RequestTimeout` | `SdkErrorCode.RequestTimeout` |
9797
| `ErrorCode.ConnectionClosed` | `SdkErrorCode.ConnectionClosed` |
98-
| `StreamableHTTPError` | REMOVED (use `SdkError` with `SdkErrorCode.ClientHttp*`) |
98+
| `StreamableHTTPError` | REMOVED (use `SdkHttpError` with `SdkErrorCode.ClientHttp*`) |
9999
| `WebSocketClientTransport` | REMOVED (use `StreamableHTTPClientTransport` or `StdioClientTransport`) |
100100

101101
All other **type** symbols from `@modelcontextprotocol/sdk/types.js` retain their original names. **Zod schemas** (e.g., `CallToolResultSchema`, `ListToolsResultSchema`) are no longer part of the public API — they are internal to the SDK. For runtime validation, use
102-
`isSpecType.TypeName(value)` (e.g., `isSpecType.CallToolResult(v)`) or `specTypeSchemas.TypeName` for the `StandardSchemaV1` validator object. The keys are typed as `SpecTypeName`, a literal union of all spec type names.
102+
`isSpecType.TypeName(value)` (e.g., `isSpecType.CallToolResult(v)`) or `specTypeSchemas.TypeName` for the `StandardSchemaV1Sync` validator object. The keys are typed as `SpecTypeName`, a literal union of all spec type names.
103103

104104
### Error class changes
105105

106-
Two error classes now exist:
106+
Three error classes now exist:
107107

108108
- **`ProtocolError`** (renamed from `McpError`): Protocol errors that cross the wire as JSON-RPC responses
109109
- **`SdkError`** (new): Local SDK errors that never cross the wire
110+
- **`SdkHttpError`** (extends `SdkError`): HTTP transport errors with typed `.status` and `.statusText` accessors
110111

111112
| Error scenario | v1 type | v2 type |
112113
| --------------------------------- | -------------------------------------------- | ----------------------------------------------------------------- |
@@ -115,12 +116,12 @@ Two error classes now exist:
115116
| Capability not supported | `new Error(...)` | `SdkError` with `SdkErrorCode.CapabilityNotSupported` |
116117
| Not connected | `new Error('Not connected')` | `SdkError` with `SdkErrorCode.NotConnected` |
117118
| Invalid params (server response) | `McpError` with `ErrorCode.InvalidParams` | `ProtocolError` with `ProtocolErrorCode.InvalidParams` |
118-
| HTTP transport error | `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttp*` |
119-
| Failed to open SSE stream | `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttpFailedToOpenStream` |
120-
| 401 after re-auth (circuit break) | `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttpAuthentication` |
121-
| 403 after upscoping | `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttpForbidden` |
119+
| HTTP transport error | `StreamableHTTPError` | `SdkHttpError` with `SdkErrorCode.ClientHttp*` |
120+
| Failed to open SSE stream | `StreamableHTTPError` | `SdkHttpError` with `SdkErrorCode.ClientHttpFailedToOpenStream` |
121+
| 401 after re-auth (circuit break) | `StreamableHTTPError` | `SdkHttpError` with `SdkErrorCode.ClientHttpAuthentication` |
122+
| 403 after upscoping | `StreamableHTTPError` | `SdkHttpError` with `SdkErrorCode.ClientHttpForbidden` |
122123
| Unexpected content type | `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttpUnexpectedContent` |
123-
| Session termination failed | `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttpFailedToTerminateSession` |
124+
| Session termination failed | `StreamableHTTPError` | `SdkHttpError` with `SdkErrorCode.ClientHttpFailedToTerminateSession` |
124125
| Response result fails schema | `ZodError` (raw) | `SdkError` with `SdkErrorCode.InvalidResult` |
125126

126127
New `SdkErrorCode` enum values:
@@ -161,9 +162,17 @@ if (error instanceof StreamableHTTPError) {
161162
}
162163

163164
// v2
164-
import { SdkError, SdkErrorCode } from '@modelcontextprotocol/client';
165-
if (error instanceof SdkError && error.code === SdkErrorCode.ClientHttpFailedToOpenStream) {
166-
const status = (error.data as { status?: number })?.status;
165+
import { SdkHttpError, SdkErrorCode } from '@modelcontextprotocol/client';
166+
if (error instanceof SdkHttpError) {
167+
console.log('HTTP status:', error.status); // number — typed accessor
168+
console.log('Status text:', error.statusText); // string | undefined
169+
switch (error.code) {
170+
case SdkErrorCode.ClientHttpAuthentication: // 401 after re-auth
171+
case SdkErrorCode.ClientHttpForbidden: // 403 after upscoping
172+
case SdkErrorCode.ClientHttpFailedToOpenStream:
173+
case SdkErrorCode.ClientHttpNotImplemented:
174+
break;
175+
}
167176
}
168177
```
169178

@@ -468,8 +477,8 @@ If a `*Schema` constant was used for **runtime validation** (not just as a `requ
468477
| -------------------------------------------------- | -------------------------------------------------------------------------------------- |
469478
| `CallToolResultSchema.safeParse(value).success` | `isSpecType.CallToolResult(value)` |
470479
| `<TypeName>Schema.safeParse(value).success` | `isSpecType.<TypeName>(value)` |
471-
| `<TypeName>Schema.parse(value)` | `await specTypeSchemas.<TypeName>['~standard'].validate(value)` (returns a `Result`, not the value) |
472-
| Passing `<TypeName>Schema` as a validator argument | `specTypeSchemas.<TypeName>` (a `StandardSchemaV1<In, Out>`) |
480+
| `<TypeName>Schema.parse(value)` | `specTypeSchemas.<TypeName>['~standard'].validate(value)` (returns a `Result` synchronously, not the value) |
481+
| Passing `<TypeName>Schema` as a validator argument | `specTypeSchemas.<TypeName>` (a `StandardSchemaV1Sync<In, Out>`) |
473482

474483
`isCallToolResult(value)` still works, but `isSpecType` covers every spec type by name.
475484

docs/migration.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -505,12 +505,12 @@ if (isSpecType.CallToolResult(value)) {
505505
}
506506
const blocks = mixed.filter(isSpecType.ContentBlock);
507507

508-
// v2: or get the StandardSchemaV1 validator object directly
508+
// v2: or get the StandardSchemaV1Sync validator object directly
509509
import { specTypeSchemas } from '@modelcontextprotocol/client';
510-
const result = await specTypeSchemas.CallToolResult['~standard'].validate(value);
510+
const result = specTypeSchemas.CallToolResult['~standard'].validate(value);
511511
```
512512

513-
`isSpecType` and `specTypeSchemas` are keyed by `SpecTypeName` — a literal union of every named type in the MCP spec — so you get autocomplete and a compile error on typos. `specTypeSchemas.X` is a `StandardSchemaV1<In, Out>`, which composes with any Standard-Schema-aware library. The pre-existing `isCallToolResult(value)` guard still works.
513+
`isSpecType` and `specTypeSchemas` are keyed by `SpecTypeName` — a literal union of every named type in the MCP spec — so you get autocomplete and a compile error on typos. `specTypeSchemas.X` is a `StandardSchemaV1Sync<In, Out>``validate()` returns the result synchronously, so you can access `.issues` / `.value` without `await`. It composes with any Standard-Schema-aware library. The pre-existing `isCallToolResult(value)` guard still works.
514514

515515
### Client list methods return empty results for missing capabilities
516516

@@ -652,10 +652,11 @@ These replace the pattern of calling `server.sendLoggingMessage()`, `server.crea
652652

653653
### Error hierarchy refactoring
654654

655-
The SDK now distinguishes between two types of errors:
655+
The SDK now distinguishes between three types of errors:
656656

657657
1. **`ProtocolError`** (renamed from `McpError`): Protocol errors that cross the wire as JSON-RPC error responses
658658
2. **`SdkError`**: Local SDK errors that never cross the wire (timeouts, connection issues, capability checks)
659+
3. **`SdkHttpError`** (extends `SdkError`): HTTP transport errors with typed `.status` and `.statusText` accessors
659660

660661
#### Renamed exports
661662

@@ -725,7 +726,7 @@ The new `SdkErrorCode` enum contains string-valued codes for local SDK errors:
725726

726727
#### `StreamableHTTPError` removed
727728

728-
The `StreamableHTTPError` class has been removed. HTTP transport errors are now thrown as `SdkError` with specific `SdkErrorCode` values that provide more granular error information:
729+
The `StreamableHTTPError` class has been removed. HTTP transport errors are now thrown as `SdkHttpError` (a subclass of `SdkError` with typed `.status` and `.statusText` accessors) with specific `SdkErrorCode` values that provide more granular error information:
729730

730731
**Before (v1):**
731732

@@ -744,12 +745,14 @@ try {
744745
**After (v2):**
745746

746747
```typescript
747-
import { SdkError, SdkErrorCode } from '@modelcontextprotocol/client';
748+
import { SdkHttpError, SdkErrorCode } from '@modelcontextprotocol/client';
748749

749750
try {
750751
await transport.send(message);
751752
} catch (error) {
752-
if (error instanceof SdkError) {
753+
if (error instanceof SdkHttpError) {
754+
console.log('HTTP status:', error.status); // number — no cast needed
755+
console.log('Status text:', error.statusText); // string | undefined
753756
switch (error.code) {
754757
case SdkErrorCode.ClientHttpAuthentication:
755758
console.log('Auth failed — server rejected token after re-auth');
@@ -764,8 +767,6 @@ try {
764767
console.log('HTTP request failed');
765768
break;
766769
}
767-
// Access HTTP status code from error.data if needed
768-
const httpStatus = (error.data as { status?: number })?.status;
769770
}
770771
}
771772
```

packages/client/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
The MCP (Model Context Protocol) TypeScript client SDK. Build MCP clients that connect to MCP servers.
44

5-
> [!WARNING] **This is an alpha release.** Expect breaking changes until v2 stabilizes. We're publishing early to gather feedback — please try it and open issues — but we can't guarantee API stability yet. We'll aim to minimize disruption between alphas.
5+
<!-- prettier-ignore -->
6+
> [!WARNING]
7+
> **This is an alpha release.** Expect breaking changes until v2 stabilizes. We're publishing early to gather feedback — please try it and open issues — but we can't guarantee API stability yet. We'll aim to minimize disruption between alphas.
68
7-
> [!NOTE] This is **v2** of the MCP TypeScript SDK. It replaces the monolithic `@modelcontextprotocol/sdk` package from v1. See the **[migration guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration.md)** if you're coming from v1.
9+
<!-- prettier-ignore -->
10+
> [!NOTE]
11+
> This is **v2** of the MCP TypeScript SDK. It replaces the monolithic `@modelcontextprotocol/sdk` package from v1. See the **[migration guide](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration.md)** if you're coming from v1.
812
913
## Install
1014

packages/client/src/client/sse.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import type { FetchLike, JSONRPCMessage, Transport } from '@modelcontextprotocol/core';
2-
import { createFetchWithInit, JSONRPCMessageSchema, normalizeHeaders, SdkError, SdkErrorCode } from '@modelcontextprotocol/core';
2+
import {
3+
createFetchWithInit,
4+
JSONRPCMessageSchema,
5+
normalizeHeaders,
6+
SdkError,
7+
SdkErrorCode,
8+
SdkHttpError
9+
} from '@modelcontextprotocol/core';
310
import type { ErrorEvent, EventSourceInit } from 'eventsource';
411
import { EventSource } from 'eventsource';
512

@@ -286,8 +293,9 @@ export class SSEClientTransport implements Transport {
286293
}
287294
await response.text?.().catch(() => {});
288295
if (isAuthRetry) {
289-
throw new SdkError(SdkErrorCode.ClientHttpAuthentication, 'Server returned 401 after re-authentication', {
290-
status: 401
296+
throw new SdkHttpError(SdkErrorCode.ClientHttpAuthentication, 'Server returned 401 after re-authentication', {
297+
status: 401,
298+
statusText: response.statusText
291299
});
292300
}
293301
throw new UnauthorizedError();

0 commit comments

Comments
 (0)