Skip to content

Commit d9b72f0

Browse files
committed
Add docs for task augmentation
1 parent b028061 commit d9b72f0

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

README.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- [Improving Network Efficiency with Notification Debouncing](#improving-network-efficiency-with-notification-debouncing)
2727
- [Low-Level Server](#low-level-server)
2828
- [Eliciting User Input](#eliciting-user-input)
29+
- [Task-Based Execution](#task-based-execution)
2930
- [Writing MCP Clients](#writing-mcp-clients)
3031
- [Proxy Authorization Requests Upstream](#proxy-authorization-requests-upstream)
3132
- [Backwards Compatibility](#backwards-compatibility)
@@ -1301,6 +1302,169 @@ client.setRequestHandler(ElicitRequestSchema, async request => {
13011302

13021303
**Note**: Elicitation requires client support. Clients must declare the `elicitation` capability during initialization.
13031304

1305+
### Task-Based Execution
1306+
1307+
Task-based execution enables "call-now, fetch-later" patterns for long-running operations. This is useful for tools that take significant time to complete, where clients may want to disconnect and check on progress or retrieve results later.
1308+
1309+
Common use cases include:
1310+
1311+
- Long-running data processing or analysis
1312+
- Code migration or refactoring operations
1313+
- Complex computational tasks
1314+
- Operations that require periodic status updates
1315+
1316+
#### Server-Side: Implementing Task Support
1317+
1318+
To enable task-based execution, configure your server with a `TaskStore` implementation. The SDK doesn't provide a built-in TaskStore—you'll need to implement one backed by your database of choice:
1319+
1320+
```typescript
1321+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
1322+
import { TaskStore } from '@modelcontextprotocol/sdk/shared/task.js';
1323+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
1324+
1325+
// Implement TaskStore backed by your database (e.g., PostgreSQL, Redis, etc.)
1326+
class MyTaskStore implements TaskStore {
1327+
async createTask(metadata, requestId, request) {
1328+
// Store task in your database
1329+
}
1330+
1331+
async getTask(taskId) {
1332+
// Retrieve task from your database
1333+
}
1334+
1335+
async updateTaskStatus(taskId, status, errorMessage?) {
1336+
// Update task status in your database
1337+
}
1338+
1339+
async storeTaskResult(taskId, result) {
1340+
// Store task result in your database
1341+
}
1342+
1343+
async getTaskResult(taskId) {
1344+
// Retrieve task result from your database
1345+
}
1346+
}
1347+
1348+
const taskStore = new MyTaskStore();
1349+
1350+
const server = new Server(
1351+
{
1352+
name: 'task-enabled-server',
1353+
version: '1.0.0'
1354+
},
1355+
{
1356+
capabilities: {
1357+
tools: {}
1358+
},
1359+
taskStore // Enable task support
1360+
}
1361+
);
1362+
1363+
// Set up a long-running tool handler as usual
1364+
server.setRequestHandler(CallToolRequestSchema, async request => {
1365+
if (request.params.name === 'analyze-data') {
1366+
// Simulate long-running analysis
1367+
await new Promise(resolve => setTimeout(resolve, 30000));
1368+
1369+
return {
1370+
content: [
1371+
{
1372+
type: 'text',
1373+
text: 'Analysis complete!'
1374+
}
1375+
]
1376+
};
1377+
}
1378+
throw new Error('Unknown tool');
1379+
});
1380+
1381+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
1382+
tools: [
1383+
{
1384+
name: 'analyze-data',
1385+
description: 'Perform data analysis (long-running)',
1386+
inputSchema: {
1387+
type: 'object',
1388+
properties: {
1389+
dataset: { type: 'string' }
1390+
}
1391+
}
1392+
}
1393+
]
1394+
}));
1395+
```
1396+
1397+
**Note**: See `src/examples/shared/inMemoryTaskStore.ts` in the SDK source for a reference implementation suitable for development and testing.
1398+
1399+
#### Client-Side: Using Task-Based Execution
1400+
1401+
Clients use `beginCallTool()` to initiate task-based operations. The returned `PendingRequest` object provides automatic polling and status tracking:
1402+
1403+
```typescript
1404+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
1405+
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
1406+
1407+
const client = new Client({
1408+
name: 'task-client',
1409+
version: '1.0.0'
1410+
});
1411+
1412+
// ... connect to server ...
1413+
1414+
// Initiate a task-based tool call
1415+
const taskId = 'analysis-task-123';
1416+
const pendingRequest = client.beginCallTool(
1417+
{
1418+
name: 'analyze-data',
1419+
arguments: { dataset: 'user-data.csv' }
1420+
},
1421+
CallToolResultSchema,
1422+
{
1423+
task: {
1424+
taskId,
1425+
keepAlive: 300000 // Keep results for 5 minutes after completion
1426+
}
1427+
}
1428+
);
1429+
1430+
// Option 1: Wait for completion with status callbacks
1431+
const result = await pendingRequest.result({
1432+
onTaskCreated: () => {
1433+
console.log('Task created successfully');
1434+
},
1435+
onTaskStatus: task => {
1436+
console.log(`Task status: ${task.status}`);
1437+
// Status can be: 'submitted', 'working', 'input_required', 'completed', 'failed', or 'cancelled'
1438+
}
1439+
});
1440+
console.log('Task completed:', result);
1441+
1442+
// Option 2: Fire and forget - disconnect and reconnect later
1443+
// (useful when you don't want to wait for long-running tasks)
1444+
// Later, after disconnecting and reconnecting to the server:
1445+
const taskStatus = await client.getTask({ taskId });
1446+
console.log('Task status:', taskStatus.status);
1447+
1448+
if (taskStatus.status === 'completed') {
1449+
const taskResult = await client.getTaskResult({ taskId }, CallToolResultSchema);
1450+
console.log('Retrieved result after reconnect:', taskResult);
1451+
}
1452+
```
1453+
1454+
#### Task Status Lifecycle
1455+
1456+
Tasks transition through the following states:
1457+
1458+
- **submitted**: Task has been created and queued
1459+
- **working**: Task is actively being processed
1460+
- **input_required**: Task is waiting for additional input (e.g., from elicitation)
1461+
- **completed**: Task finished successfully
1462+
- **failed**: Task encountered an error
1463+
- **cancelled**: Task was cancelled by the client
1464+
- **unknown**: Task status could not be determined (terminal state, rarely occurs)
1465+
1466+
The `keepAlive` parameter determines how long the server retains task results after completion. This allows clients to retrieve results even after disconnecting and reconnecting.
1467+
13041468
### Writing MCP Clients
13051469

13061470
The SDK provides a high-level client interface:

0 commit comments

Comments
 (0)