Dissolve capabilities.md into server.md and client.md (#1532)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan Hefner 2026-02-17 17:23:23 -06:00 • committed by GitHub
commit 284a4135f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 336 additions and 161 deletions

View file

@ -127,9 +127,8 @@ Next steps:
## Documentation
- Local SDK docs:
- [docs/server.md](docs/server.md) – building MCP servers, transports, tools/resources/prompts, CORS, DNS rebinding, and deployment patterns.
- [docs/client.md](docs/client.md) – using the high-level client, transports, backwards compatibility, and OAuth helpers.
- [docs/capabilities.md](docs/capabilities.md) – sampling, elicitation (form and URL), and experimental task-based execution.
- [docs/server.md](docs/server.md) – building MCP servers, transports, tools/resources/prompts, sampling, elicitation, tasks, and deployment patterns.
- [docs/client.md](docs/client.md) – using the high-level client, transports, OAuth helpers, handling server‑initiated requests, and tasks.
- [docs/faq.md](docs/faq.md) – environment and troubleshooting FAQs (including Node.js Web Crypto support).
- External references:
- [SDK API documentation](https://modelcontextprotocol.github.io/typescript-sdk/)

View file

@ -1,85 +0,0 @@
---
title: Capabilities
---
## Sampling
MCP servers can request LLM completions from connected clients that support the sampling capability. This lets your tools offload summarisation or generation to the client’s model.
For a runnable server that combines tools, logging and tasks, see:
- [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts)
In practice you will:
- Declare the sampling capability on the client.
- Call `server.server.createMessage(...)` from within a tool handler.
- Return the model’s response as structured content and/or text.
Refer to the MCP spec’s sampling section for full request/response details.
## Elicitation
### Form elicitation
Form elicitation lets a tool ask the user for additional, **non‑sensitive** information via a schema‑driven form. The server sends a schema and message, and the client is responsible for collecting and returning the data.
Runnable example:
- Server: [`elicitationFormExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/elicitationFormExample.ts)
- Client‑side handling: [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleStreamableHttp.ts)
The `simpleStreamableHttp` server also includes a `collect-user-info` tool that demonstrates how to drive elicitation from a tool and handle the response.
### URL elicitation
URL elicitation is designed for sensitive data and secure web‑based flows (e.g., collecting an API key, confirming a payment, or doing third‑party OAuth). Instead of returning form data, the server asks the client to open a URL and the rest of the flow happens in the browser.
Runnable example:
- Server: [`elicitationUrlExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/elicitationUrlExample.ts)
- Client: [`elicitationUrlExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/elicitationUrlExample.ts)
Key points:
- Use `mode: 'url'` when calling `server.server.elicitInput(...)`.
- Implement a client‑side handler for `ElicitRequestSchema` that:
- Shows the full URL and reason to the user.
- Asks for explicit consent.
- Opens the URL in the system browser.
Sensitive information **must not** be collected via form elicitation; always use URL elicitation or out‑of‑band flows for secrets.
## Task-based execution (experimental)
Task-based execution enables “call-now, fetch-later” patterns for long-running operations. Instead of returning a result immediately, a tool creates a task that can be polled or resumed later.
The APIs live under the experimental `.experimental.tasks` namespace and may change without notice.
### Server-side concepts
On the server you will:
- Provide a `TaskStore` implementation that persists task metadata and results.
- Enable the `tasks` capability when constructing the server.
- Register tools with `server.experimental.tasks.registerToolTask(...)`.
For a runnable example that uses the in-memory store shipped with the SDK, see:
- [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts)
- `packages/core/src/experimental/tasks/stores/in-memory.ts`
### Client-side usage
On the client, you use:
- `client.experimental.tasks.callToolStream(...)` to start a tool call that may create a task and emit status updates over time.
- `client.getTask(...)` and `client.getTaskResult(...)` to check status and fetch results after reconnecting.
The interactive client in:
- [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleStreamableHttp.ts)
includes commands to demonstrate calling tools that support tasks and handling their lifecycle.
See the MCP spec’s tasks section and the example server/client above for a full walkthrough of the task status lifecycle and TTL handling.

View file

@ -2,7 +2,7 @@
title: Client
---
## Client overview
# Client overview
This guide covers SDK usage for building MCP clients in TypeScript. For protocol-level details and message formats, see the [MCP specification](https://modelcontextprotocol.io/specification/latest/).
@ -318,7 +318,7 @@ client.setRequestHandler('elicitation/create', async request => {
```
> [!NOTE]
> For a full form‑based elicitation handler with AJV validation, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleStreamableHttp.ts). For URL elicitation mode, see [`elicitationUrlExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/elicitationUrlExample.ts) and the [Capabilities guide](capabilities.md#elicitation).
> For a full form‑based elicitation handler with AJV validation, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleStreamableHttp.ts). For URL elicitation mode (`mode: 'url'`), see [`elicitationUrlExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/elicitationUrlExample.ts).
>
> For protocol details, see [Elicitation](https://modelcontextprotocol.io/specification/latest/client/elicitation) in the MCP specification.
@ -367,6 +367,19 @@ console.log(result);
> [!NOTE]
> 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)
Task-based execution enables "call-now, fetch-later" patterns for long-running operations. 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.
> [!NOTE]
> For a full runnable example, see [`simpleTaskInteractiveClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleTaskInteractiveClient.ts).
> [!WARNING]
> The tasks API is experimental and may change without notice.
## More client features
The sections above cover the essentials. The table below links to additional capabilities.
@ -377,4 +390,3 @@ The sections above cover the essentials. The table below links to additional cap
| SSE disconnect / reconnection | Server‑initiated SSE disconnect with automatic reconnection and event replay | [`ssePollingClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/ssePollingClient.ts) |
| Multiple clients | Independent client lifecycles to the same server | [`multipleClientsParallel.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/multipleClientsParallel.ts) |
| URL elicitation | Handle sensitive data collection via browser | [`elicitationUrlExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/elicitationUrlExample.ts) |
| Tasks (experimental) | Long‑running tool calls with status streaming | [`simpleTaskInteractiveClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleTaskInteractiveClient.ts), [Capabilities guide](capabilities.md#task-based-execution-experimental) |

View file

@ -3,13 +3,11 @@ title: Documents
children:
- ./server.md
- ./client.md
- ./capabilities.md
- ./faq.md
---
# Documents
- [Server](./server.md) – building MCP servers, transports, tools/resources/prompts, and deployment patterns
- [Client](./client.md) – using the high-level client, transports, backwards compatibility, and OAuth helpers
- [Capabilities](./capabilities.md) – sampling, elicitation, and experimental task-based execution
- [Server](./server.md) – building MCP servers, transports, tools/resources/prompts, sampling, elicitation, tasks, and deployment patterns
- [Client](./client.md) – using the high-level client, transports, OAuth helpers, handling server‑initiated requests, and tasks
- [FAQ](./faq.md) – frequently asked questions and troubleshooting

View file

@ -2,7 +2,7 @@
title: Server
---
## Server overview
# Server overview
This guide covers SDK usage for building MCP servers in TypeScript. For protocol-level details and message formats, see the [MCP specification](https://modelcontextprotocol.io/specification/latest/).
@ -92,30 +92,6 @@ const transport = new StdioServerTransport();
await server.connect(transport);
```
## DNS rebinding protection
MCP servers running on localhost are vulnerable to DNS rebinding attacks. Use `createMcpExpressApp()` from `@modelcontextprotocol/express` to create an Express app with DNS rebinding protection enabled by default:
```ts source="../examples/server/src/serverGuide.examples.ts#dnsRebinding_basic"
// Default: DNS rebinding protection auto-enabled (host is 127.0.0.1)
const app = createMcpExpressApp();
// DNS rebinding protection also auto-enabled for localhost
const appLocal = createMcpExpressApp({ host: 'localhost' });
// No automatic protection when binding to all interfaces
const appOpen = createMcpExpressApp({ host: '0.0.0.0' });
```
When binding to `0.0.0.0` / `::`, provide an allow-list of hosts:
```ts source="../examples/server/src/serverGuide.examples.ts#dnsRebinding_allowedHosts"
const app = createMcpExpressApp({
host: '0.0.0.0',
allowedHosts: ['localhost', '127.0.0.1', 'myhost.local']
});
```
## Tools, resources, and prompts
### Tools
@ -185,37 +161,12 @@ server.registerTool(
> [!NOTE]
> For a full runnable example with `ResourceLink` outputs, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts).
#### Logging
#### Tool annotations
Use `ctx.mcpReq.log(level, data)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler to send structured log messages to the client. The server must declare the `logging` capability:
```ts source="../examples/server/src/serverGuide.examples.ts#logging_capability"
const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { logging: {} } });
```
Then log from any tool callback:
```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_logging"
server.registerTool(
'fetch-data',
{
description: 'Fetch data from an API',
inputSchema: z.object({ url: z.string() })
},
async ({ url }, ctx): Promise<CallToolResult> => {
await ctx.mcpReq.log('info', `Fetching ${url}`);
const res = await fetch(url);
await ctx.mcpReq.log('debug', `Response status: ${res.status}`);
const text = await res.text();
return { content: [{ type: 'text', text }] };
}
);
```
Tools can include annotations that hint at their behavior — for example, whether a tool is read‑only, destructive, or idempotent. Annotations help clients present tools appropriately without changing their execution semantics.
> [!NOTE]
> For logging in a full server, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) and [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts).
>
> For protocol details, see [Logging](https://modelcontextprotocol.io/specification/latest/server/utilities/logging) in the MCP specification.
> For tool annotations in a full server, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts).
### Resources
@ -337,11 +288,181 @@ server.registerPrompt(
);
```
For client-side completion usage, see the [Client guide](client.md).
### Logging
Unlike tools, resources, and prompts, logging is not a registered primitive — it is a handler-level API available inside any callback. Use `ctx.mcpReq.log(level, data)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) to send structured log messages to the client. The server must declare the `logging` capability:
```ts source="../examples/server/src/serverGuide.examples.ts#logging_capability"
const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { logging: {} } });
```
Then log from any handler callback:
```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_logging"
server.registerTool(
'fetch-data',
{
description: 'Fetch data from an API',
inputSchema: z.object({ url: z.string() })
},
async ({ url }, ctx): Promise<CallToolResult> => {
await ctx.mcpReq.log('info', `Fetching ${url}`);
const res = await fetch(url);
await ctx.mcpReq.log('debug', `Response status: ${res.status}`);
const text = await res.text();
return { content: [{ type: 'text', text }] };
}
);
```
> [!NOTE]
> For logging in a full server, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) and [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts).
>
> For protocol details, see [Logging](https://modelcontextprotocol.io/specification/latest/server/utilities/logging) in the MCP specification.
## Server‑initiated requests
MCP is bidirectional — servers can also send requests *to* the client during tool execution, as long as the client declares matching capabilities.
### Sampling
Use `ctx.mcpReq.requestSampling(params)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler to request an LLM completion from the connected client:
```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_sampling"
server.registerTool(
'summarize',
{
description: 'Summarize text using the client LLM',
inputSchema: z.object({ text: z.string() })
},
async ({ text }, ctx): Promise<CallToolResult> => {
const response = await ctx.mcpReq.requestSampling({
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Please summarize:\n\n${text}`
}
}
],
maxTokens: 500
});
return {
content: [
{
type: 'text',
text: `Model (${response.model}): ${JSON.stringify(response.content)}`
}
]
};
}
);
```
> [!NOTE]
> For a full runnable example, see [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts).
>
> For protocol details, see [Sampling](https://modelcontextprotocol.io/specification/latest/client/sampling) in the MCP specification.
### Elicitation
Use `ctx.mcpReq.elicitInput(params)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler to request user input. Elicitation supports two modes:
- **Form** (`mode: 'form'`) — collects **non‑sensitive** data via a schema‑driven form.
- **URL** (`mode: 'url'`) — for sensitive data or secure web‑based flows (API keys, payments, OAuth). The client opens a URL in the browser.
> [!IMPORTANT]
> Sensitive information **must not** be collected via form elicitation; always use URL elicitation or out‑of‑band flows for secrets.
```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_elicitation"
server.registerTool(
'collect-feedback',
{
description: 'Collect user feedback via a form',
inputSchema: z.object({})
},
async (_args, ctx): Promise<CallToolResult> => {
const result = await ctx.mcpReq.elicitInput({
mode: 'form',
message: 'Please share your feedback:',
requestedSchema: {
type: 'object',
properties: {
rating: {
type: 'number',
title: 'Rating (1\u20135)',
minimum: 1,
maximum: 5
},
comment: { type: 'string', title: 'Comment' }
},
required: ['rating']
}
});
if (result.action === 'accept') {
return {
content: [
{
type: 'text',
text: `Thanks! ${JSON.stringify(result.content)}`
}
]
};
}
return { content: [{ type: 'text', text: 'Feedback declined.' }] };
}
);
```
> [!NOTE]
> For runnable examples, see [`elicitationFormExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/elicitationFormExample.ts) (form mode) and [`elicitationUrlExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/elicitationUrlExample.ts) (URL mode).
>
> For protocol details, see [Elicitation](https://modelcontextprotocol.io/specification/latest/client/elicitation) in the MCP specification.
## Tasks (experimental)
Task-based execution enables "call-now, fetch-later" patterns for long-running operations. 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(...)}.
> [!NOTE]
> For a full runnable example, see [`simpleTaskInteractive.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleTaskInteractive.ts).
> [!WARNING]
> The tasks API is experimental and may change without notice.
## Deployment
### DNS rebinding protection
MCP servers running on localhost are vulnerable to DNS rebinding attacks. Use `createMcpExpressApp()` from `@modelcontextprotocol/express` to create an Express app with DNS rebinding protection enabled by default:
```ts source="../examples/server/src/serverGuide.examples.ts#dnsRebinding_basic"
// Default: DNS rebinding protection auto-enabled (host is 127.0.0.1)
const app = createMcpExpressApp();
// DNS rebinding protection also auto-enabled for localhost
const appLocal = createMcpExpressApp({ host: 'localhost' });
// No automatic protection when binding to all interfaces
const appOpen = createMcpExpressApp({ host: '0.0.0.0' });
```
When binding to `0.0.0.0` / `::`, provide an allow-list of hosts:
```ts source="../examples/server/src/serverGuide.examples.ts#dnsRebinding_allowedHosts"
const app = createMcpExpressApp({
host: '0.0.0.0',
allowedHosts: ['localhost', '127.0.0.1', 'myhost.local']
});
```
## More server features
The sections above cover the essentials. The SDK supports several additional capabilities — each is demonstrated in the runnable examples and covered in more detail in the linked references.
The sections above cover the essentials. The table below links to additional capabilities demonstrated in the runnable examples.
| Feature | Description | Reference |
|---------|-------------|-----------|
@ -349,8 +470,4 @@ The sections above cover the essentials. The SDK supports several additional cap
| Session management | Per-session transport routing, initialization, and cleanup | [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) |
| Resumability | Replay missed SSE events via an event store | [`inMemoryEventStore.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/inMemoryEventStore.ts) |
| CORS | Expose MCP headers (`mcp-session-id`, etc.) for browser clients | [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) |
| Tool annotations | Hint whether tools are read-only, destructive, etc. | [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) |
| Elicitation | Request user input (forms or URLs) during tool execution | [Capabilities guide](capabilities.md#elicitation), [`elicitationFormExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/elicitationFormExample.ts) |
| Sampling | Request LLM completions from the connected client | [Capabilities guide](capabilities.md#sampling), [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts) |
| Tasks (experimental) | Long-running operations with polling and resumption | [Capabilities guide](capabilities.md#task-based-execution-experimental), [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) |
| Multi‑node deployment | Stateless, persistent‑storage, and distributed routing patterns | [`examples/server/README.md`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/README.md#multi-node-deployment-patterns) |

View file

@ -207,6 +207,88 @@ function registerTool_logging() {
return server;
}
// ---------------------------------------------------------------------------
// Server-initiated requests
// ---------------------------------------------------------------------------
/** Example: Tool that uses sampling to request an LLM completion from the client. */
function registerTool_sampling(server: McpServer) {
//#region registerTool_sampling
server.registerTool(
'summarize',
{
description: 'Summarize text using the client LLM',
inputSchema: z.object({ text: z.string() })
},
async ({ text }, ctx): Promise<CallToolResult> => {
const response = await ctx.mcpReq.requestSampling({
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Please summarize:\n\n${text}`
}
}
],
maxTokens: 500
});
return {
content: [
{
type: 'text',
text: `Model (${response.model}): ${JSON.stringify(response.content)}`
}
]
};
}
);
//#endregion registerTool_sampling
}
/** Example: Tool that uses form elicitation to collect user input. */
function registerTool_elicitation(server: McpServer) {
//#region registerTool_elicitation
server.registerTool(
'collect-feedback',
{
description: 'Collect user feedback via a form',
inputSchema: z.object({})
},
async (_args, ctx): Promise<CallToolResult> => {
const result = await ctx.mcpReq.elicitInput({
mode: 'form',
message: 'Please share your feedback:',
requestedSchema: {
type: 'object',
properties: {
rating: {
type: 'number',
title: 'Rating (1\u20135)',
minimum: 1,
maximum: 5
},
comment: { type: 'string', title: 'Comment' }
},
required: ['rating']
}
});
if (result.action === 'accept') {
return {
content: [
{
type: 'text',
text: `Thanks! ${JSON.stringify(result.content)}`
}
]
};
}
return { content: [{ type: 'text', text: 'Feedback declined.' }] };
}
);
//#endregion registerTool_elicitation
}
// ---------------------------------------------------------------------------
// Transports
// ---------------------------------------------------------------------------
@ -294,6 +376,8 @@ function dnsRebinding_allowedHosts() {
void registerTool_basic;
void registerTool_resourceLink;
void registerTool_logging;
void registerTool_sampling;
void registerTool_elicitation;
void registerResource_static;
void registerResource_template;
void registerPrompt_basic;

View file

@ -31,6 +31,7 @@ export class InMemoryTaskStore implements TaskStore {
return crypto.randomUUID().replaceAll('-', '');
}
/** {@inheritDoc TaskStore.createTask} */
async createTask(taskParams: CreateTaskOptions, requestId: RequestId, request: Request, sessionId?: string): Promise<Task> {
// Generate a unique task ID
const taskId = this.generateTaskId();
@ -96,6 +97,7 @@ export class InMemoryTaskStore implements TaskStore {
return stored ? { ...stored.task } : null;
}
/** {@inheritDoc TaskStore.storeTaskResult} */
async storeTaskResult(taskId: string, status: 'completed' | 'failed', result: Result, sessionId?: string): Promise<void> {
const stored = this.getStoredTask(taskId, sessionId);
if (!stored) {
@ -129,6 +131,7 @@ export class InMemoryTaskStore implements TaskStore {
}
}
/** {@inheritDoc TaskStore.getTaskResult} */
async getTaskResult(taskId: string, sessionId?: string): Promise<Result> {
const stored = this.getStoredTask(taskId, sessionId);
if (!stored) {
@ -142,6 +145,7 @@ export class InMemoryTaskStore implements TaskStore {
return stored.result;
}
/** {@inheritDoc TaskStore.updateTaskStatus} */
async updateTaskStatus(taskId: string, status: Task['status'], statusMessage?: string, sessionId?: string): Promise<void> {
const stored = this.getStoredTask(taskId, sessionId);
if (!stored) {
@ -178,6 +182,7 @@ export class InMemoryTaskStore implements TaskStore {
}
}
/** {@inheritDoc TaskStore.listTasks} */
async listTasks(cursor?: string, sessionId?: string): Promise<{ tasks: Task[]; nextCursor?: string }> {
const PAGE_SIZE = 10;

View file

@ -1,14 +1,17 @@
import type { Result, Task } from '../types/types.js';
/**
* Base message type
* Base message type for the response stream.
*/
export interface BaseResponseMessage {
type: string;
}
/**
* Task status update message
* Task status update message.
*
* Yielded on each poll iteration while the task is active (e.g. while
* `working`). May be emitted multiple times with the same status.
*/
export interface TaskStatusMessage extends BaseResponseMessage {
type: 'taskStatus';
@ -16,7 +19,10 @@ export interface TaskStatusMessage extends BaseResponseMessage {
}
/**
* Task created message (first message for task-augmented requests)
* Task created message.
*
* Yielded once when the server creates a new task for a long-running operation.
* This is always the first message for task-augmented requests.
*/
export interface TaskCreatedMessage extends BaseResponseMessage {
type: 'taskCreated';
@ -24,7 +30,10 @@ export interface TaskCreatedMessage extends BaseResponseMessage {
}
/**
* Final result message (terminal)
* Final result message.
*
* Yielded once when the operation completes successfully. Terminal — no further
* messages will follow.
*/
export interface ResultMessage<T extends Result> extends BaseResponseMessage {
type: 'result';
@ -32,7 +41,9 @@ export interface ResultMessage<T extends Result> extends BaseResponseMessage {
}
/**
* Error message (terminal)
* Error message.
*
* Yielded once if the operation fails. Terminal — no further messages will follow.
*/
export interface ErrorMessage extends BaseResponseMessage {
type: 'error';
@ -40,14 +51,26 @@ export interface ErrorMessage extends BaseResponseMessage {
}
/**
* Union type representing all possible messages that can be yielded during request processing.
* Note: Progress notifications are handled through the existing {@linkcode index.RequestOptions | onprogress} callback mechanism.
* Union of all message types yielded by task-aware streaming APIs such as
* {@linkcode @modelcontextprotocol/client!experimental/tasks/client.ExperimentalClientTasks#callToolStream | callToolStream()},
* {@linkcode @modelcontextprotocol/client!experimental/tasks/client.ExperimentalClientTasks#requestStream | ExperimentalClientTasks.requestStream()}, and
* {@linkcode @modelcontextprotocol/server!experimental/tasks/server.ExperimentalServerTasks#requestStream | ExperimentalServerTasks.requestStream()}.
*
* A typical sequence is:
* 1. `taskCreated` — task is registered (once)
* 2. `taskStatus` — zero or more progress updates
* 3. `result` **or** `error` — terminal message (once)
*
* Progress notifications are handled through the existing {@linkcode index.RequestOptions | onprogress} callback.
* Side-channeled messages (server requests/notifications) are handled through registered handlers.
*/
export type ResponseMessage<T extends Result> = TaskStatusMessage | TaskCreatedMessage | ResultMessage<T> | ErrorMessage;
export type AsyncGeneratorValue<T> = T extends AsyncGenerator<infer U> ? U : never;
/**
* Collects all values from an async generator into an array.
*/
export async function toArrayAsync<T extends AsyncGenerator<unknown>>(it: T): Promise<AsyncGeneratorValue<T>[]> {
const arr: AsyncGeneratorValue<T>[] = [];
for await (const o of it) {
@ -57,6 +80,11 @@ export async function toArrayAsync<T extends AsyncGenerator<unknown>>(it: T): Pr
return arr;
}
/**
* Consumes a {@linkcode ResponseMessage} stream and returns the final result,
* discarding intermediate `taskCreated` and `taskStatus` messages. Throws
* if an `error` message is received or the stream ends without a result.
*/
export async function takeResult<T extends Result, U extends AsyncGenerator<ResponseMessage<T>>>(it: U): Promise<T> {
for await (const o of it) {
if (o.type === 'result') {

View file

@ -41,10 +41,27 @@ export type TaskRequestHandler<ResultT extends Result, Args extends AnySchema |
/**
* Interface for task-based tool handlers.
*
* Task-based tools split a long-running operation into three phases:
* `createTask`, `getTask`, and `getTaskResult`.
*
* @see {@linkcode @modelcontextprotocol/server!experimental/tasks/mcpServer.ExperimentalMcpServerTasks#registerToolTask | registerToolTask} for registration.
* @experimental
*/
export interface ToolTaskHandler<Args extends AnySchema | undefined = undefined> {
/**
* Called on the initial `tools/call` request.
*
* Creates a task via `ctx.task.store.createTask(...)`, starts any
* background work, and returns the task object.
*/
createTask: CreateTaskRequestHandler<CreateTaskResult, Args>;
/**
* Handler for `tasks/get` requests.
*/
getTask: TaskRequestHandler<GetTaskResult, Args>;
/**
* Handler for `tasks/result` requests.
*/
getTaskResult: TaskRequestHandler<CallToolResult, Args>;
}