You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Lead with concepts instead of code
- Explain MCP background tasks vs general Python concurrency
- Document Docket's Prefect origins and battle-tested infrastructure
- Add sections on graceful degradation and embedded workers
- Fix version badge to 2.14.0
- Link to SEP-1686 spec
The [MCP task protocol](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) lets you request operations to run asynchronously. This returns a Task object immediately, letting you track progress, cancel operations, or await results.
14
14
15
15
See [Server Background Tasks](/servers/tasks) for how to enable this on the server side.
16
16
17
17
## Requesting Background Execution
18
18
19
-
Pass `task=True` to run an operation as a background task:
19
+
Pass `task=True` to run an operation as a background task. The call returns immediately with a Task object while the work executes on the server.
20
20
21
21
```python
22
22
from fastmcp import Client
@@ -33,71 +33,54 @@ async with Client(server) as client:
All task types share a common interface for retrieving results, checking status, and receiving updates.
52
47
53
-
### Getting Results
48
+
To get the result, call `await task.result()` or simply `await task`. This blocks until the task completes and returns the result. You can also check status without blocking using `await task.status()`, which returns the current state (`"working"`, `"completed"`, `"failed"`, or `"cancelled"`) along with any progress message from the server.
You can always pass `task=True` regardless of whether the server supports background tasks. Per the [MCP specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks), servers that don't support tasks will execute the operation immediately and return the result inline. Your code works either way:
92
+
You can always pass `task=True` regardless of whether the server supports background tasks. Per the MCP specification, servers without task support execute the operation immediately and return the result inline. The Task API provides a consistent interface either way.
This means you can write task-aware client code without worrying about server capabilities—the Task API provides a consistent interface whether the operation runs in the background or completes immediately.
106
+
This means you can write task-aware client code without worrying about server capabilities.
Background tasks allow tools, resources, and prompts to execute asynchronously, returning immediately while work continues in the background. Clients can track progress, cancel operations, and retrieve results when ready.
13
+
FastMCP implements the MCP background task protocol ([SEP-1686](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks)), giving your servers a production-ready distributed task scheduler with a single decorator change.
14
14
15
-
This implements the [MCP task protocol](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) from the MCP specification, powered by [Docket](https://github.com/chrisguidry/docket) for task queue management.
15
+
<Tip>
16
+
**What is Docket?** FastMCP's task system is powered by [Docket](https://github.com/chrisguidry/docket), originally built by [Prefect](https://prefect.io) to power [Prefect Cloud](https://www.prefect.io/prefect/cloud)'s managed task scheduling and execution service. Docket is the beating heart of Prefect's enterprise task infrastructure, processing millions of tasks daily across their multi-tenant SaaS platform. It's now open-sourced for the community.
17
+
</Tip>
18
+
19
+
<Note>
20
+
Background tasks are disabled by default in v2.14.0. Enable them with `FASTMCP_ENABLE_TASKS=true` or by passing `tasks=True` to the FastMCP constructor. This default will change in a future release.
21
+
</Note>
22
+
23
+
## What Are MCP Background Tasks?
24
+
25
+
In MCP, all component interactions are blocking by default. When a client calls a tool, reads a resource, or fetches a prompt, it sends a request and waits for the response. For operations that take seconds or minutes, this creates a poor user experience.
26
+
27
+
The MCP background task protocol solves this by letting clients:
28
+
1.**Start** an operation and receive a task ID immediately
29
+
2.**Track** progress as the operation runs
30
+
3.**Retrieve** the result when ready
31
+
32
+
FastMCP handles all of this for you. Add `task=True` to your decorator, and your function gains full background execution with progress reporting, distributed processing, and horizontal scaling.
16
33
17
-
##Requirements
34
+
### MCP Background Tasks vs Python Concurrency
18
35
19
-
For **single-process** deployments, everything works out of the box using an in-memory backend.
36
+
You can always use Python's concurrency primitives (asyncio, threads, multiprocessing) or external task queues in your FastMCP servers. FastMCP is just Python—run code however you like.
20
37
21
-
For **multi-process** deployments (multiple workers, distributed systems), you'll need Redis or Valkey. See the [Docket documentation](https://chrisguidry.github.io/docket/) for backend configuration details.
38
+
MCP background tasks are different: they're **protocol-native**. This means MCP clients that support the task protocol can start operations, receive progress updates, and retrieve results through the standard MCP interface. The coordination happens at the protocol level, not inside your application code.
22
39
23
40
## Enabling Background Tasks
24
41
25
-
Add `task=True` to any tool, resource, or prompt decorator:
42
+
Add `task=True` to any tool, resource, resource template, or prompt decorator. This marks the component as capable of background execution.
"""A long-running operation with progress tracking."""
37
-
await progress.set_total(duration)
38
-
51
+
asyncdefslow_computation(duration: int) -> str:
52
+
"""A long-running operation."""
39
53
for i inrange(duration):
40
54
await asyncio.sleep(1)
41
-
await progress.increment()
42
-
await progress.set_message(f"Step {i +1} of {duration}")
43
-
44
55
returnf"Completed in {duration} seconds"
45
56
```
46
57
47
-
<Note>
48
-
Background tasks require async functions. Sync functions will log a warning and execute immediately instead.
49
-
</Note>
58
+
When a client requests background execution, the call returns immediately with a task ID. The work executes in a background worker, and the client can poll for status or wait for the result.
50
59
51
-
## Configuration
60
+
<Warning>
61
+
Background tasks require async functions. Attempting to use `task=True` with a sync function raises a `ValueError` at registration time.
62
+
</Warning>
52
63
53
-
Background tasks require explicit opt-in:
64
+
### Server-Wide Default
65
+
66
+
To enable background task support for all components by default, pass `tasks=True` to the constructor. Individual decorators can still override this with `task=False`.
67
+
68
+
```python
69
+
mcp = FastMCP("MyServer", tasks=True)
70
+
```
71
+
72
+
<Warning>
73
+
If your server defines any synchronous tools, resources, or prompts, you will need to explicitly set `task=False` on their decorators to avoid an error.
74
+
</Warning>
75
+
76
+
### Graceful Degradation
77
+
78
+
When a client requests background execution (`task=True` in the request) but the component doesn't support it (`task=False` on the decorator), FastMCP executes synchronously and returns the result inline. This follows the SEP-1686 specification for graceful degradation—clients can always request background execution without worrying about server capabilities.
79
+
80
+
### Configuration
81
+
82
+
Background tasks require explicit opt-in via environment variable:
54
83
55
84
| Environment Variable | Default | Description |
56
85
|---------------------|---------|-------------|
57
86
|`FASTMCP_ENABLE_TASKS`|`false`| Enable the MCP task protocol |
58
87
|`FASTMCP_DOCKET_URL`|`memory://`| Backend URL (`memory://` or `redis://host:port/db`) |
59
88
60
-
You can also set a server-wide default in the constructor:
89
+
## Backends
61
90
62
-
```python
63
-
mcp = FastMCP("MyServer", tasks=True)
91
+
FastMCP supports two backends for task execution, each with different tradeoffs.
92
+
93
+
### In-Memory Backend (Default)
94
+
95
+
The in-memory backend (`memory://`) requires zero configuration and works out of the box.
96
+
97
+
**Advantages:**
98
+
- No external dependencies
99
+
- Simple single-process deployment
100
+
101
+
**Disadvantages:**
102
+
-**Ephemeral**: If the server restarts, all pending tasks are lost
103
+
-**Higher latency**: ~250ms task pickup time vs single-digit milliseconds with Redis
104
+
-**No horizontal scaling**: Single process only—you cannot add additional workers
105
+
106
+
### Redis Backend
107
+
108
+
For production deployments, use Redis (or Valkey) as your backend by setting `FASTMCP_DOCKET_URL=redis://localhost:6379`.
-**Scalable**: Add workers to distribute load across processes or machines
114
+
115
+
## Workers
116
+
117
+
Every FastMCP server with task-enabled components automatically starts an **embedded worker**. You do not need to start a separate worker process for tasks to execute.
118
+
119
+
To scale horizontally, add more workers using the CLI:
120
+
121
+
```bash
122
+
fastmcp tasks worker server.py
64
123
```
65
124
125
+
Each additional worker pulls tasks from the same queue, distributing load across processes. Configure worker concurrency via environment:
126
+
127
+
```bash
128
+
export FASTMCP_DOCKET_CONCURRENCY=20
129
+
fastmcp tasks worker server.py
130
+
```
131
+
132
+
<Note>
133
+
Additional workers only work with Redis/Valkey backends. The in-memory backend is single-process only.
134
+
</Note>
135
+
66
136
## Progress Reporting
67
137
68
-
The `Progress` dependency lets you report progress back to clients:
138
+
The `Progress` dependency lets you report progress back to clients. Inject it as a parameter with a default value, and FastMCP will provide the active progress reporter.
-`await progress.set_message(text)`— Update the status message
89
162
90
-
Progress works in both immediate and background execution modes.
163
+
Progress works in both immediate and background execution modes—you can use the same code regardless of how the client invokes your function.
91
164
92
-
## Additional Dependencies
165
+
## Docket Dependencies
93
166
94
-
FastMCP provides several Docket-style dependencies you can inject into your functions:
167
+
FastMCP exposes Docket's full dependency injection system within your task-enabled functions. Beyond `Progress`, you can access the Docket instance, worker information, and use advanced features like retries and timeouts.
95
168
96
169
```python
170
+
from docket import Docket, Worker
171
+
from fastmcp import FastMCP
97
172
from fastmcp.dependencies import Progress, CurrentDocket, CurrentWorker
98
173
174
+
mcp = FastMCP("MyServer")
175
+
99
176
@mcp.tool(task=True)
100
177
asyncdefmy_task(
101
178
progress: Progress = Progress(),
102
-
#docket: Docket = CurrentDocket(), # Access the Docket instance
103
-
#worker: Worker = CurrentWorker(), # Access worker info
179
+
docket: Docket = CurrentDocket(),
180
+
worker: Worker = CurrentWorker(),
104
181
) -> str:
105
-
...
106
-
```
182
+
# Schedule additional background work
183
+
await docket.add(another_task, arg1, arg2)
107
184
108
-
By injecting `CurrentDocket()`, you gain access to the full Docket API. This lets you schedule additional background tasks from within your tool, chain tasks together, or use any of Docket's advanced features like task priorities and retries. See the [Docket documentation](https://chrisguidry.github.io/docket/) for the complete API.
185
+
# Access worker metadata
186
+
worker_name = worker.name
109
187
110
-
## Running Additional Workers
111
-
112
-
For distributed task processing, start additional workers:
113
-
114
-
```bash
115
-
fastmcp tasks worker server.py
188
+
return"Done"
116
189
```
117
190
118
-
Configure worker concurrency via environment:
119
-
120
-
```bash
121
-
export FASTMCP_DOCKET_CONCURRENCY=20
122
-
fastmcp tasks worker server.py
123
-
```
124
-
125
-
<Tip>
126
-
Workers only work with Redis/Valkey backends. The `memory://` backend is single-process only.
127
-
</Tip>
191
+
With `CurrentDocket()`, you can schedule additional background tasks, chain work together, and coordinate complex workflows. See the [Docket documentation](https://chrisguidry.github.io/docket/) for the complete API, including retry policies, timeouts, and custom dependencies.
0 commit comments