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
5 changes: 1 addition & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ The repo also ships “middleware” packages under `packages/middleware/` (e.g.

### Experimental Features

Located in `packages/*/src/experimental/`:

- **Tasks**: Long-running task support with polling/resumption (`packages/core/src/experimental/tasks/`)
Located in `packages/*/src/experimental/`. Currently empty.

### Zod Schemas

Expand Down Expand Up @@ -201,7 +199,6 @@ The `ctx` parameter in handlers provides a structured context:
- `notify(notification)`: Send related notification back
- `http?`: HTTP transport info (undefined for stdio)
- `authInfo?`: Validated auth token info
- `task?`: Task context (`{ id?, store, requestedTtl? }`) when task storage is configured

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

Expand Down
16 changes: 2 additions & 14 deletions docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ All requests have a 60-second default timeout. Pass a custom `timeout` in the op
```ts source="../examples/client/src/clientGuide.examples.ts#errorHandling_timeout"
try {
const result = await client.callTool(
{ name: 'slow-task', arguments: {} },
{ name: 'slow-operation', arguments: {} },
{ timeout: 120_000 } // 2 minutes instead of the default 60 seconds
);
console.log(result.content);
Expand Down Expand Up @@ -581,7 +581,7 @@ let lastToken: string | undefined;
const result = await client.request(
{
method: 'tools/call',
params: { name: 'long-running-task', arguments: {} }
params: { name: 'long-running-operation', arguments: {} }
},
{
resumptionToken: lastToken,
Expand All @@ -596,18 +596,6 @@ console.log(result);

For an end-to-end example of server-initiated SSE disconnection and automatic client reconnection with event replay, see [`ssePollingClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/ssePollingClient.ts).

## Tasks (experimental)

> [!WARNING]
> The tasks API is experimental and may change without notice.

Task-based execution enables "call-now, fetch-later" patterns for long-running operations (see [Tasks](https://modelcontextprotocol.io/specification/latest/basic/utilities/tasks) in the MCP specification). Instead of returning a result immediately, a tool creates a task that can be polled or resumed later. To use tasks:

- Call {@linkcode @modelcontextprotocol/client!experimental/tasks/client.ExperimentalClientTasks#callToolStream | client.experimental.tasks.callToolStream(...)} to start a tool call that may create a task and emit status updates over time.
- Call {@linkcode @modelcontextprotocol/client!experimental/tasks/client.ExperimentalClientTasks#getTask | client.experimental.tasks.getTask(...)} and {@linkcode @modelcontextprotocol/client!experimental/tasks/client.ExperimentalClientTasks#getTaskResult | getTaskResult(...)} to check status and fetch results after reconnecting.

For a full runnable example, see [`simpleTaskInteractiveClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleTaskInteractiveClient.ts).

## See also

- [`examples/client/`](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples/client) — Full runnable client examples
Expand Down
34 changes: 17 additions & 17 deletions docs/migration-SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,7 @@ Request/notification params remain fully typed. Remove unused schema imports aft
| `extra.requestInfo` | `ctx.http?.req` (standard Web `Request`, only `ServerContext`) |
| `extra.closeSSEStream` | `ctx.http?.closeSSE` (only `ServerContext`) |
| `extra.closeStandaloneSSEStream` | `ctx.http?.closeStandaloneSSE` (only `ServerContext`) |
| `extra.taskStore` | `ctx.task?.store` |
| `extra.taskId` | `ctx.task?.id` |
| `extra.taskRequestedTtl` | `ctx.task?.requestedTtl` |
| `extra.taskStore` / `taskId` / `taskRequestedTtl` | _removed; see §12_ |

`ServerContext` convenience methods (new in v2, no v1 equivalent):

Expand Down Expand Up @@ -473,24 +471,26 @@ If a `*Schema` constant was used for **runtime validation** (not just as a `requ

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

## 12. Experimental: `TaskCreationParams.ttl` no longer accepts `null`
## 12. Experimental tasks interception removed

`TaskCreationParams.ttl` changed from `z.union([z.number(), z.null()]).optional()` to `z.number().optional()`. Per the MCP spec, `null` TTL (unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Omit `ttl` to let the server decide.
The 2025-11 task side-channel through `Protocol` is removed (was always `@experimental`). No mechanical migration; remove usages.

| v1 | v2 |
| ---------------------- | ---------------------------------- |
| `task: { ttl: null }` | `task: {}` (omit ttl) |
| `task: { ttl: 60000 }` | `task: { ttl: 60000 }` (unchanged) |
| Removed | Notes |
| --- | --- |
| `ProtocolOptions.tasks` | drop the option |
| `protocol.taskManager` | gone |
| `RequestOptions.task` / `.relatedTask`, `NotificationOptions.relatedTask` | drop the option |
| `BaseContext.task` (`ctx.task?.*`) | gone |
| `assertTaskCapability` / `assertTaskHandlerCapability` overrides | delete the override |
| `*.experimental.tasks.*` accessors, `Experimental{Client,Server,McpServer}Tasks` | removed |
| `requestStream` / `callToolStream` / `createMessageStream` / `elicitInputStream` | removed; no streaming variant |
| `registerToolTask`, `ToolTaskHandler`, `TaskRequestHandler`, `CreateTaskRequestHandler` | removed |
| `TaskMessageQueue`, `InMemoryTaskMessageQueue`, `Queued*`, `CreateTaskServerContext`, `TaskServerContext`, `TaskToolExecution` | removed |
| `ResponseMessage`, `TaskStatusMessage`, `TaskCreatedMessage`, `ResultMessage`, `takeResult`, `toArrayAsync` | removed |

Type changes in handler context:
`TaskStore` / `InMemoryTaskStore` / `CreateTaskOptions` / `isTerminal` (storage layer) are unchanged.

| Type | v1 | v2 |
| ------------------------------------------- | ----------------------------- | --------------------- |
| `TaskContext.requestedTtl` | `number \| null \| undefined` | `number \| undefined` |
| `CreateTaskServerContext.task.requestedTtl` | `number \| null \| undefined` | `number \| undefined` |
| `TaskServerContext.task.requestedTtl` | `number \| null \| undefined` | `number \| undefined` |

> These task APIs are `@experimental` and may change without notice.
`TaskCreationParams.ttl` also no longer accepts `null` (`number | undefined` only); omit `ttl` to let the server decide.

## 13. Client Behavioral Changes

Expand Down
54 changes: 17 additions & 37 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,7 @@
| `extra.closeSSEStream` | `ctx.http?.closeSSE` (only on `ServerContext`) |
| `extra.closeStandaloneSSEStream` | `ctx.http?.closeStandaloneSSE` (only on `ServerContext`) |
| `extra.sessionId` | `ctx.sessionId` |
| `extra.taskStore` | `ctx.task?.store` |
| `extra.taskId` | `ctx.task?.id` |
| `extra.taskRequestedTtl` | `ctx.task?.requestedTtl` |
| `extra.taskStore` / `taskId` / `taskRequestedTtl` | _removed — see "Experimental tasks interception removed" below_ |

**Before (v1):**

Expand Down Expand Up @@ -853,46 +851,28 @@
}
```

### Experimental: `TaskCreationParams.ttl` no longer accepts `null`
### Experimental tasks interception removed

The `ttl` field in `TaskCreationParams` (used when requesting the server to create a task) no longer accepts `null`. Per the MCP spec, `null` TTL (meaning unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Clients should omit `ttl` to let
the server decide the lifetime.
The 2025-11 experimental tasks side-channel woven through `Protocol` has been removed in preparation for the SEP-2663 Tasks Extension. The following are gone with no in-place replacement:

This also narrows the type of `requestedTtl` in `TaskContext`, `CreateTaskServerContext`, and `TaskServerContext` from `number | null | undefined` to `number | undefined`.
- `ProtocolOptions.tasks` (the `{ taskStore, taskMessageQueue }` constructor option)
- `protocol.taskManager` getter, `Protocol#_bindTaskManager`
- `RequestOptions.task` / `RequestOptions.relatedTask`, `NotificationOptions.relatedTask`
- `BaseContext.task` (`ctx.task?.store` / `ctx.task?.id` / `ctx.task?.requestedTtl`)
- abstract `assertTaskCapability` / `assertTaskHandlerCapability`
- `client.experimental.tasks.*` / `server.experimental.tasks.*` / `mcpServer.experimental.tasks.*` accessors and the `Experimental{Client,Server,McpServer}Tasks` classes
- streaming methods (`requestStream`, `callToolStream`, `createMessageStream`, `elicitInputStream`) and the `ResponseMessage` types they yielded
- `mcpServer.experimental.tasks.registerToolTask(...)`, `ToolTaskHandler`, `TaskRequestHandler`, `CreateTaskRequestHandler`
- `TaskMessageQueue`, `InMemoryTaskMessageQueue`, `Queued*` message types, `CreateTaskServerContext`, `TaskServerContext`, `TaskToolExecution`
- `examples/{client,server}/src/simpleTaskInteractive*.ts`

**Before (v1):**

```typescript
// Requesting unlimited lifetime by passing null
const result = await client.callTool({
name: 'long-task',
arguments: {},
task: { ttl: null }
});
**Unchanged:** the storage layer (`TaskStore`, `InMemoryTaskStore`, `CreateTaskOptions`, `isTerminal`). It will be consumed by the SEP-2663 server-directed plugin in a follow-up.

// Handler context had number | null | undefined
server.setRequestHandler('tools/call', async (request, ctx) => {
const ttl: number | null | undefined = ctx.task?.requestedTtl;
});
```
There is no migration path for the removed surface; it was always `@experimental`. Under SEP-2663, tasks reattach via a `DispatchMiddleware` (`mcp.use(tasksPlugin({ store }))`) and handlers read task context from `ctx.ext.task` instead of `ctx.task`.

**After (v2):**

```typescript
// Omit ttl to let the server decide (server may return null for unlimited)
const result = await client.callTool({
name: 'long-task',
arguments: {},
task: {}
});

// Handler context is now number | undefined
server.setRequestHandler('tools/call', async (request, ctx) => {
const ttl: number | undefined = ctx.task?.requestedTtl;
});
```
#### `TaskCreationParams.ttl` no longer accepts `null`

> **Note:** These task APIs are marked `@experimental` and may change without notice.
`TaskCreationParams.ttl` (the storage-layer creation parameter) is now `number | undefined`; `null` is no longer accepted. Per the MCP spec, `null` TTL (unlimited lifetime) is only valid in server responses (`Task.ttl`), not in creation requests. Omit `ttl` to let the store decide. This is a storage-interface change and is independent of the Protocol-level removals above.

Check failure on line 875 in docs/migration.md

View check run for this annotation

Claude / Claude Code Review

Migration docs claim TaskStore/InMemoryTaskStore/CreateTaskOptions/isTerminal are unchanged but they are deleted

The migration guide tells v1 users that the storage layer (`TaskStore`, `InMemoryTaskStore`, `CreateTaskOptions`, `isTerminal`) is "Unchanged" and will be consumed by a SEP-2663 follow-up — but this same PR deletes all of those symbols (`packages/core/src/experimental/tasks/interfaces.ts`, `stores/inMemory.ts`) and removes their re-exports from `packages/core/src/exports/public/index.ts` and `packages/core/src/index.ts`. The same applies to `docs/migration-SKILL.md:491`, and the adjacent `TaskCr
Comment on lines +869 to +875
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 migration guide tells v1 users that the storage layer (TaskStore, InMemoryTaskStore, CreateTaskOptions, isTerminal) is "Unchanged" and will be consumed by a SEP-2663 follow-up — but this same PR deletes all of those symbols (packages/core/src/experimental/tasks/interfaces.ts, stores/inMemory.ts) and removes their re-exports from packages/core/src/exports/public/index.ts and packages/core/src/index.ts. The same applies to docs/migration-SKILL.md:491, and the adjacent TaskCreationParams.ttl subsection documents a type (TaskCreationParams) that is also deleted here. The docs should say these were removed (and will return with the SEP-2663 plugin), or the storage-layer types should be retained.

Extended reasoning...

What the docs claim vs. what the diff ships

docs/migration.md (around line 869) and docs/migration-SKILL.md (around line 491), both rewritten in this PR, contain:

Unchanged: the storage layer (TaskStore, InMemoryTaskStore, CreateTaskOptions, isTerminal). It will be consumed by the SEP-2663 server-directed plugin in a follow-up.

But the same diff:

  • Deletes packages/core/src/experimental/tasks/interfaces.ts — which defined TaskStore, CreateTaskOptions, and isTerminal.
  • Deletes packages/core/src/experimental/tasks/stores/inMemory.ts — which defined InMemoryTaskStore (and InMemoryTaskMessageQueue).
  • Deletes the experimental barrel packages/core/src/experimental/index.ts.
  • Removes the re-exports from packages/core/src/exports/public/index.ts (the export type { ... TaskStore ... }, export { isTerminal }, and export { InMemoryTaskMessageQueue, InMemoryTaskStore } lines).
  • Removes the export * from './experimental/index.js'; line from packages/core/src/index.ts.

After this PR, packages/core/src/experimental/ does not exist, and a grep for TaskStore|InMemoryTaskStore|CreateTaskOptions|isTerminal across packages/core/src returns nothing. The PR description itself states the grep audit confirms no task references remain in packages/**.

Why this matters

The migration guide is the surface a v1 user reads to learn what is and isn't still importable. "Unchanged" is an explicit, load-bearing claim — it tells the reader they do not need to touch any code that depends on these symbols. A user with import { InMemoryTaskStore } from '@modelcontextprotocol/core' (or /server) who reads this section will conclude their import still resolves, ship the change, and then hit a build failure that contradicts the docs they just followed.

Concrete proof

  1. A v1 user has import { InMemoryTaskStore, isTerminal } from '@modelcontextprotocol/core'; in their server.
  2. They upgrade to v2 and read the migration guide. docs/migration.md line ~869 says these are "Unchanged."
  3. They leave that import alone.
  4. tsc fails: Module '"@modelcontextprotocol/core"' has no exported member 'InMemoryTaskStore'. — because packages/core/src/exports/public/index.ts no longer re-exports it and the source file no longer exists.
  5. The user now believes the docs are wrong (correctly) and has to grep the package to figure out what actually shipped.

Adjacent issue: the TaskCreationParams.ttl subsection

Immediately after the "Unchanged" claim, migration.md keeps a subsection titled "TaskCreationParams.ttl no longer accepts null" that describes a type narrowing as if TaskCreationParams still exists. But TaskCreationParamsSchema is deleted from packages/core/src/types/schemas.ts and TaskCreationParams is removed from packages/core/src/types/types.ts (it now only survives as a MISSING_SDK_TYPES entry in spec.types.test.ts). Documenting a type narrowing for a type that no longer exists compounds the confusion.

How to fix

Either:

  • Update both docs to say the storage-layer types and TaskCreationParams were removed in this PR and will return as part of the SEP-2663 server-directed plugin (a one-line edit in each file), or
  • If the intent really was to keep the storage layer importable for the follow-up, restore interfaces.ts, stores/inMemory.ts, and their re-exports from the public barrels.

Given the PR description ("grep audit confirms no task references remain in packages/**"), the first option matches the implementation; the docs are what needs to change.


## Enhancements

Expand Down
13 changes: 0 additions & 13 deletions docs/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -495,19 +495,6 @@ server.registerTool(
);
```

## Tasks (experimental)

> [!WARNING]
> The tasks API is experimental and may change without notice.

Task-based execution enables "call-now, fetch-later" patterns for long-running operations (see [Tasks](https://modelcontextprotocol.io/specification/latest/basic/utilities/tasks) in the MCP specification). Instead of returning a result immediately, a tool creates a task that can be polled or resumed later. To use tasks:

- Provide a {@linkcode @modelcontextprotocol/server!index.TaskStore | TaskStore} implementation that persists task metadata and results (see {@linkcode @modelcontextprotocol/server!index.InMemoryTaskStore | InMemoryTaskStore} for reference).
- Enable the `tasks` capability when constructing the server.
- Register tools with {@linkcode @modelcontextprotocol/server!experimental/tasks/mcpServer.ExperimentalMcpServerTasks#registerToolTask | server.experimental.tasks.registerToolTask(...)}.

For a full runnable example, see [`simpleTaskInteractive.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleTaskInteractive.ts).

## Shutdown

For stateful multi-session HTTP servers, capture the `http.Server` from `app.listen()` so you can stop accepting connections, then close each session transport:
Expand Down
23 changes: 11 additions & 12 deletions examples/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ Most clients expect a server to be running. Start one from [`../server/README.md

## Example index

| Scenario | Description | File |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Interactive Streamable HTTP client | CLI client that exercises tools/resources/prompts, notifications, elicitation, and tasks. | [`src/simpleStreamableHttp.ts`](src/simpleStreamableHttp.ts) |
| Backwards-compatible client (Streamable HTTP → SSE) | Tries Streamable HTTP first, falls back to legacy SSE on 4xx responses. | [`src/streamableHttpWithSseFallbackClient.ts`](src/streamableHttpWithSseFallbackClient.ts) |
| SSE polling client (legacy) | Polls a legacy HTTP+SSE server and demonstrates notification handling. | [`src/ssePollingClient.ts`](src/ssePollingClient.ts) |
| Parallel tool calls | Runs multiple tool calls in parallel. | [`src/parallelToolCallsClient.ts`](src/parallelToolCallsClient.ts) |
| Multiple clients in parallel | Connects multiple clients concurrently to the same server. | [`src/multipleClientsParallel.ts`](src/multipleClientsParallel.ts) |
| OAuth client (interactive) | OAuth-enabled client (dynamic registration, auth flow). | [`src/simpleOAuthClient.ts`](src/simpleOAuthClient.ts) |
| OAuth provider helper | Demonstrates reusable OAuth providers. | [`src/simpleOAuthClientProvider.ts`](src/simpleOAuthClientProvider.ts) |
| Client credentials (M2M) | Machine-to-machine OAuth client credentials example. | [`src/simpleClientCredentials.ts`](src/simpleClientCredentials.ts) |
| URL elicitation client | Drives URL-mode elicitation flows (sensitive input in a browser). | [`src/elicitationUrlExample.ts`](src/elicitationUrlExample.ts) |
| Task interactive client | Demonstrates task-based execution + interactive server→client requests. | [`src/simpleTaskInteractiveClient.ts`](src/simpleTaskInteractiveClient.ts) |
| Scenario | Description | File |
| --------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Interactive Streamable HTTP client | CLI client that exercises tools/resources/prompts, notifications, and elicitation. | [`src/simpleStreamableHttp.ts`](src/simpleStreamableHttp.ts) |
| Backwards-compatible client (Streamable HTTP → SSE) | Tries Streamable HTTP first, falls back to legacy SSE on 4xx responses. | [`src/streamableHttpWithSseFallbackClient.ts`](src/streamableHttpWithSseFallbackClient.ts) |
| SSE polling client (legacy) | Polls a legacy HTTP+SSE server and demonstrates notification handling. | [`src/ssePollingClient.ts`](src/ssePollingClient.ts) |
| Parallel tool calls | Runs multiple tool calls in parallel. | [`src/parallelToolCallsClient.ts`](src/parallelToolCallsClient.ts) |
| Multiple clients in parallel | Connects multiple clients concurrently to the same server. | [`src/multipleClientsParallel.ts`](src/multipleClientsParallel.ts) |
| OAuth client (interactive) | OAuth-enabled client (dynamic registration, auth flow). | [`src/simpleOAuthClient.ts`](src/simpleOAuthClient.ts) |
| OAuth provider helper | Demonstrates reusable OAuth providers. | [`src/simpleOAuthClientProvider.ts`](src/simpleOAuthClientProvider.ts) |
| Client credentials (M2M) | Machine-to-machine OAuth client credentials example. | [`src/simpleClientCredentials.ts`](src/simpleClientCredentials.ts) |
| URL elicitation client | Drives URL-mode elicitation flows (sensitive input in a browser). | [`src/elicitationUrlExample.ts`](src/elicitationUrlExample.ts) |

## URL elicitation example (server + client)

Expand Down
Loading
Loading