Skip to content
Draft
19 changes: 6 additions & 13 deletions docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';

### Streamable HTTP

For remote HTTP servers, use {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport}:
For remote HTTP servers, use {@linkcode @modelcontextprotocol/client!client/modernStreamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport}:

```ts source="../examples/client/src/clientGuide.examples.ts#connect_streamableHttp"
const client = new Client({ name: 'my-client', version: '1.0.0' });
Expand All @@ -49,7 +49,7 @@ For a full interactive client over Streamable HTTP, see [`simpleStreamableHttp.t

### stdio

For local, process-spawned servers (Claude Desktop, CLI tools), use {@linkcode @modelcontextprotocol/client!client/stdio.StdioClientTransport | StdioClientTransport}. The transport spawns the server process and communicates over stdin/stdout:
For local, process-spawned servers (Claude Desktop, CLI tools), use {@linkcode @modelcontextprotocol/client!client/modernStdio.StdioClientTransport | StdioClientTransport}. The transport spawns the server process and communicates over stdin/stdout:

```ts source="../examples/client/src/clientGuide.examples.ts#connect_stdio"
const client = new Client({ name: 'my-client', version: '1.0.0' });
Expand All @@ -64,7 +64,7 @@ await client.connect(transport);

### SSE fallback for legacy servers

To support both modern Streamable HTTP and legacy SSE servers, try {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport} first and fall back to {@linkcode @modelcontextprotocol/client!client/sse.SSEClientTransport | SSEClientTransport} on failure:
To support both modern Streamable HTTP and legacy SSE servers, try {@linkcode @modelcontextprotocol/client!client/modernStreamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport} first and fall back to {@linkcode @modelcontextprotocol/client!client/sse.SSEClientTransport | SSEClientTransport} on failure:

```ts source="../examples/client/src/clientGuide.examples.ts#connect_sseFallback"
const baseUrl = new URL(url);
Expand Down Expand Up @@ -113,7 +113,7 @@ console.log(systemPrompt);

## Authentication

MCP servers can require authentication before accepting client connections (see [Authorization](https://modelcontextprotocol.io/specification/latest/basic/authorization) in the MCP specification). Pass an {@linkcode @modelcontextprotocol/client!client/auth.AuthProvider | AuthProvider} to {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport}. The transport calls `token()` before every request and `onUnauthorized()` (if provided) on 401, then retries once.
MCP servers can require authentication before accepting client connections (see [Authorization](https://modelcontextprotocol.io/specification/latest/basic/authorization) in the MCP specification). Pass an {@linkcode @modelcontextprotocol/client!client/auth.AuthProvider | AuthProvider} to {@linkcode @modelcontextprotocol/client!client/modernStreamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport}. The transport calls `token()` before every request and `onUnauthorized()` (if provided) on 401, then retries once.

### Bearer tokens

Expand Down Expand Up @@ -162,7 +162,7 @@ For a runnable example supporting both auth methods via environment variables, s

### Full OAuth with user authorization

For user-facing applications, implement the {@linkcode @modelcontextprotocol/client!client/auth.OAuthClientProvider | OAuthClientProvider} interface to handle the full authorization code flow (redirects, code verifiers, token storage, dynamic client registration). The {@linkcode @modelcontextprotocol/client!client/client.Client#connect | connect()} call will throw {@linkcode @modelcontextprotocol/client!client/auth.UnauthorizedError | UnauthorizedError} when authorization is needed — catch it, complete the browser flow, call {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport#finishAuth | transport.finishAuth(code)}, and reconnect.
For user-facing applications, implement the {@linkcode @modelcontextprotocol/client!client/auth.OAuthClientProvider | OAuthClientProvider} interface to handle the full authorization code flow (redirects, code verifiers, token storage, dynamic client registration). The {@linkcode @modelcontextprotocol/client!client/client.Client#connect | connect()} call will throw {@linkcode @modelcontextprotocol/client!client/auth.UnauthorizedError | UnauthorizedError} when authorization is needed — catch it, complete the browser flow, call {@linkcode @modelcontextprotocol/client!client/modernStreamableHttp.StreamableHTTPClientTransport#finishAuth | transport.finishAuth(code)}, and reconnect.

For a complete working OAuth flow, see [`simpleOAuthClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleOAuthClient.ts) and [`simpleOAuthClientProvider.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleOAuthClientProvider.ts).

Expand Down Expand Up @@ -599,14 +599,7 @@ For an end-to-end example of server-initiated SSE disconnection and automatic cl
## 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).
> The tasks API has been removed from this version of the SDK. See the [Migration guide](./migration.md) for details.

## See also

Expand Down
12 changes: 2 additions & 10 deletions docs/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ For a complete server with sessions, logging, and CORS mounted on Express, see [

### stdio

For local, process-spawned integrations, use {@linkcode @modelcontextprotocol/server!server/stdio.StdioServerTransport | StdioServerTransport}:
For local, process-spawned integrations, use {@linkcode @modelcontextprotocol/server!server/modernStdio.StdioServerTransport | StdioServerTransport}:

```ts source="../examples/server/src/serverGuide.examples.ts#stdio_basic"
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
Expand Down Expand Up @@ -498,15 +498,7 @@ 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).
> The tasks API has been removed from this version of the SDK. See the [Migration guide](./migration.md) for details.

## Shutdown

Expand Down
1 change: 0 additions & 1 deletion examples/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Most clients expect a server to be running. Start one from [`../server/README.md
| 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) |

## URL elicitation example (server + client)

Expand Down
95 changes: 2 additions & 93 deletions examples/client/src/simpleOAuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createServer } from 'node:http';
import { createInterface } from 'node:readline';
import { URL } from 'node:url';

import type { CallToolResult, ListToolsRequest, OAuthClientMetadata } from '@modelcontextprotocol/client';
import type { ListToolsRequest, OAuthClientMetadata } from '@modelcontextprotocol/client';
import { Client, StreamableHTTPClientTransport, UnauthorizedError } from '@modelcontextprotocol/client';
import open from 'open';

Expand Down Expand Up @@ -209,7 +209,6 @@ class InteractiveOAuthClient {
console.log('Commands:');
console.log(' list - List available tools');
console.log(' call <tool_name> [args] - Call a tool');
console.log(' stream <tool_name> [args] - Call a tool with streaming (shows task status)');
console.log(' quit - Exit the client');
console.log();

Expand All @@ -229,10 +228,8 @@ class InteractiveOAuthClient {
await this.listTools();
} else if (command.startsWith('call ')) {
await this.handleCallTool(command);
} else if (command.startsWith('stream ')) {
await this.handleStreamTool(command);
} else {
console.log("❌ Unknown command. Try 'list', 'call <tool_name>', 'stream <tool_name>', or 'quit'");
console.log("❌ Unknown command. Try 'list', 'call <tool_name>', or 'quit'");
}
} catch (error) {
if (error instanceof Error && error.message === 'SIGINT') {
Expand Down Expand Up @@ -328,94 +325,6 @@ class InteractiveOAuthClient {
}
}

private async handleStreamTool(command: string): Promise<void> {
const parts = command.split(/\s+/);
const toolName = parts[1];

if (!toolName) {
console.log('❌ Please specify a tool name');
return;
}

// Parse arguments (simple JSON-like format)
let toolArgs: Record<string, unknown> = {};
if (parts.length > 2) {
const argsString = parts.slice(2).join(' ');
try {
toolArgs = JSON.parse(argsString);
} catch {
console.log('❌ Invalid arguments format (expected JSON)');
return;
}
}

await this.streamTool(toolName, toolArgs);
}

private async streamTool(toolName: string, toolArgs: Record<string, unknown>): Promise<void> {
if (!this.client) {
console.log('❌ Not connected to server');
return;
}

try {
// Using the experimental tasks API - WARNING: may change without notice
console.log(`\n🔧 Streaming tool '${toolName}'...`);

const stream = this.client.experimental.tasks.callToolStream(
{
name: toolName,
arguments: toolArgs
},
{
task: {
taskId: `task-${Date.now()}`,
ttl: 60_000
}
}
);

// Iterate through all messages yielded by the generator
for await (const message of stream) {
switch (message.type) {
case 'taskCreated': {
console.log(`✓ Task created: ${message.task.taskId}`);
break;
}

case 'taskStatus': {
console.log(`⟳ Status: ${message.task.status}`);
if (message.task.statusMessage) {
console.log(` ${message.task.statusMessage}`);
}
break;
}

case 'result': {
console.log('✓ Completed!');
const toolResult = message.result as CallToolResult;
for (const content of toolResult.content) {
if (content.type === 'text') {
console.log(content.text);
} else {
console.log(content);
}
}
break;
}

case 'error': {
console.log('✗ Error:');
console.log(` ${message.error.message}`);
break;
}
}
}
} catch (error) {
console.error(`❌ Failed to stream tool '${toolName}':`, error);
}
}

close(): void {
this.rl.close();
if (this.client) {
Expand Down
Loading
Loading