A task tool returns a task id and finishes later. The host polls that id instead of holding the HTTP call open.
Set execution: 'task' on the tool. The call returns a task handle at once, and the host polls for the result.
You need an MCP server first. If you do not have one, start with Serve Tools over HTTP. This setup is for one long-lived process, such as a Node server. For a Worker, see Run tasks on a Worker.
import { toolDefinition } from '@tanstack/ai'
import { createMCPServer } from '@tanstack/ai-mcp/server'
const buildReport = toolDefinition({
name: 'build_report',
description: 'Build a long report',
execution: 'task',
}).server(async () => {
const text = await writeReport()
return { text }
})
async function writeReport() {
await new Promise((resolve) => {
setTimeout(resolve, 5000)
})
return 'Quarterly report'
}
const server = createMCPServer({
name: 'reports',
version: '1.0.0',
tools: [buildReport],
})
export function handleMcp(request: Request) {
return server.fetch(request)
}The host calls build_report and gets a task handle at once. After 5 seconds, the status is completed, and the host reads { text: 'Quarterly report' } from structuredContent.
The default task store keeps each task in memory for this process. A TanStack AI host polls until the task ends. See MCP Server Tools.
The task keeps running after the call returns the task handle. As a result, the task cannot use the request that started it:
A Worker can stop when the response is sent. It can also build a new server for each request. Pass two more options:
import { toolDefinition } from '@tanstack/ai'
import { createMCPServer, inMemoryTaskStore } from '@tanstack/ai-mcp/server'
const taskStore = inMemoryTaskStore()
const buildReport = toolDefinition({
name: 'build_report',
description: 'Build a long report',
execution: 'task',
}).server(async () => {
const text = await writeReport()
return { text }
})
async function writeReport() {
await new Promise((resolve) => {
setTimeout(resolve, 5000)
})
return 'Quarterly report'
}
export default {
async fetch(
request: Request,
_env: unknown,
ctx: { waitUntil(promise: Promise<unknown>): void },
) {
const server = createMCPServer({
name: 'reports',
version: '1.0.0',
tools: [buildReport],
taskStore,
waitUntil: (promise) => ctx.waitUntil(promise),
})
return server.fetch(request)
},
}The server calls waitUntil with the tool promise. This promise settles after the store saves the tool result or the tool error.
If more than one instance can receive tasks/get, pass a taskStore that those instances share. A task store has three methods:
@tanstack/ai-mcp/server exports inMemoryTaskStore and the TaskStore type.
Tasks are part of spec 2025-11-25. Spec 2026-07-28 has no tasks yet.
| Spec | What the call returns |
|---|---|
| 2025-11-25 | A task handle. The host polls tasks/get, then reads tasks/result. |
| 2026-07-28 | The tool result. The tool runs inline, and the call waits for it. |
On spec 2025, the server sets execution.taskSupport to required. It also advertises the tasks capability for tools/call.
On spec 2025, the call response is the CreateTaskResult from the spec. The task field is a Task with status working.
{
"content": [{ "type": "text", "text": "task-id" }],
"task": {
"taskId": "task-id",
"status": "working",
"ttl": null,
"createdAt": "2026-09-22T12:00:00.000Z",
"lastUpdatedAt": "2026-09-22T12:00:00.000Z"
}
}tasks/get returns the same Task fields:
| Field | Value |
|---|---|
| taskId | The id from the call. |
| status | working, completed, or failed. |
| ttl | null. |
| createdAt | The time of the first set. |
| lastUpdatedAt | The time of the last set. |
| statusMessage | The error text, when the status is failed. |
| Status | Meaning |
|---|---|
| working | The tool function is still at work. |
| completed | The store holds the tool result. |
| failed | The tool function threw an error. |
If the tool function throws, the task keeps the text of that error in statusMessage. If that error has no text, the task keeps The tool failed.
tasks/result wraps the value as content. When the value is an object, tasks/result also returns structuredContent.
Errors: