* Support CallArgument and Depends bindings from uncalled-for 0.4.0 uncalled-for 0.4.0 adds explicit argument references: CallArgument() lets a dependency factory read an argument of the function it serves, and Depends(factory, **bindings) supplies factory arguments at the declaration site (https://github.com/chrisguidry/uncalled-for/pull/12). FastMCP's resolver now opens a frame_scope() around dependency resolution, with the sanitized user arguments as the frame's provided values. A CallArgument can reference a tool call's public parameters, but a caller-supplied value for a dependency parameter name is still stripped before resolution. CallArgument and CycleError are re-exported from fastmcp.dependencies, and the dependency-injection docs cover both features. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Raise the pydocket floor to 0.24.0 outside Windows pydocket 0.24.0 resolves TaskArgument and CallArgument through uncalled-for 0.4.0's call-scoped frames. Windows keeps the 0.20.0 floor: the burner-redis<0.1.7 pin there transitively caps pydocket to <0.20.2, and burner-redis has shipped no fixed release yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Bump the pydocket floor to 0.24.1 for reliable worker shutdown docket 0.24.1 fixes a lost cancellation in worker shutdown on Python 3.10 and 3.11 (chrisguidry/docket#456): asyncio.wait_for swallowed a cancellation delivered in the same event-loop tick that its inner future completed, so cancelling run_forever during our lifespan teardown left the worker running and hung the test session. That is what timed out the Python 3.10 and lowest-direct jobs here. The floor stays platform-split; Windows keeps >=0.20.0 under the burner-redis pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Drop the Windows burner-redis pin and unify the pydocket floor at 0.24.1 The pin blamed the wrong package. The Windows "interpreter crash" that motivated it (#4618) was pydocket 0.23.1 losing an external cancellation during worker teardown; pytest-timeout's hard kill of the hung xdist worker discarded its stdout and looked like a native fault. Capping burner-redis also dragged pydocket below 0.20.2, so the two variables were never separated. The repro matrix on prefectlabs/burner-redis#7 shows the July environment failing as resolved, passing with only pydocket rolled back, and passing with pydocket 0.24.1 alongside burner-redis 0.1.7 on Windows. pydocket 0.24.1 carries the fix (chrisguidry/docket#456), so every platform now shares one floor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: nate nowack <thrast36@gmail.com> |
||
|---|---|---|
| .. | ||
| fastmcp_tasks | ||
| pyproject.toml | ||
| README.md | ||
fastmcp-tasks
A complete implementation of background tasks for the Model Context Protocol — the io.modelcontextprotocol/tasks extension defined in SEP-2663.
The MCP tasks extension is a Final SEP, but as of this writing it ships in the ecosystem as a schema and a prose specification — no language SDK provides a working runtime for it. fastmcp-tasks is, to our knowledge, the first: a full server-side implementation of the protocol, built on the durable execution engine (docket) that FastMCP has run in production since v3. If you want to actually run MCP background tasks today, this is the implementation.
What background tasks are
Most tool calls are synchronous: the client sends tools/call and holds the request open until the tool returns. That breaks down for work that takes minutes or hours — a long analysis, a batch job, a slow external API. The tasks extension lets a server answer such a call immediately with a durable task handle, then run the work in the background while the client polls for completion on its own schedule.
The model is poll-based and stateless by construction, which is what makes it survive disconnects, server restarts, and load balancers:
- A client that supports tasks issues a normal
tools/callwith a per-request opt-in. - The server decides whether to run it as a task. If it does, it returns a
CreateTaskResultcarrying a server-generated task id — right away, before the work starts. - The client polls
tasks/getuntil the task reaches a terminal state, then reads the result inlined in the response. tasks/cancelrequests cancellation;tasks/updateanswers any input the task asks for mid-run.
The server owns the task's durable state, so the client can poll across independent requests — from any process, after a crash, through any replica — with no session affinity required.
Usage
Install it as the tasks extra on FastMCP:
uv pip install "fastmcp[tasks]"
Register the extension on your server and mark the tools that may run as tasks. The extension is where the backend is configured — point it at Redis for a distributed deployment, or leave it on the in-memory default for a single process:
from fastmcp import FastMCP
from fastmcp_tasks import TasksExtension
mcp = FastMCP("Analytics")
mcp.add_extension(TasksExtension(url="redis://localhost:6379/0"))
@mcp.tool(task=True)
async def analyze(dataset: str) -> str:
# Long-running work. The client gets a task handle immediately and
# polls for the result; this runs in a background worker.
...
task=True is a declaration of intent — this tool may run as a task — while the server, per the spec, decides per call whether to actually task it. Use TaskConfig for finer control:
from fastmcp.utilities.tasks import TaskConfig
@mcp.tool(task=TaskConfig(mode="required"))
async def must_run_async(n: int) -> int:
# Always runs as a task; a client that has not opted in is told so.
...
Registering TasksExtension is required to serve task=True tools — the tool declares intent, the extension provides the engine. A task=True tool on a server with no tasks extension registered fails loudly at startup rather than silently running inline.
Running out-of-process workers
For distributed deployments backed by Redis, run dedicated worker processes alongside your server:
python -m fastmcp_tasks.worker_cli worker server.py
Workers and servers that share a backend URL and queue name share a task queue, so you can scale execution independently of your request-serving frontends.
Configuration
The backend is configured on the extension. Every option also has a FASTMCP_DOCKET_* environment variable, so an env-configured deployment can construct TasksExtension() with no arguments:
| Option | Env var | Default | Description |
|---|---|---|---|
url |
FASTMCP_DOCKET_URL |
memory:// |
Backend URL. memory:// for single-process; redis://host:port/db for distributed workers. |
name |
FASTMCP_DOCKET_NAME |
fastmcp |
Queue name. Servers and workers sharing a name and URL share a queue. |
concurrency |
FASTMCP_DOCKET_CONCURRENCY |
10 |
Maximum concurrent tasks per worker. |
See the FastMCP task documentation for the full reference.
Status
The tasks extension is an experimental MCP extension, and fastmcp-tasks tracks its draft schema. The protocol's shape is settled — SEP-2663 is Final — but field-level details may still move; this package versions independently so it can follow the schema without waiting on a FastMCP release.