PrefectHQ/fastmcp · error · RuntimeError
Progress dependency requires a FastMCP server context.
Error message
Progress dependency requires a FastMCP server context.
What it means
The Progress dependency is request-scoped: entering it looks up the current FastMCP server from a contextvar (_current_server) holding a weak reference. If no server is active (or the referenced server was garbage-collected), it raises this RuntimeError because progress notifications can only be sent within a live server request.
Source
Thrown at fastmcp_slim/fastmcp/server/dependencies.py:1221
class Progress(Dependency["Progress"]):
"""Progress dependency that works in both server and worker contexts.
In a Docket worker, delegates to the execution's Redis-backed progress
(observable across processes). Otherwise, uses in-memory tracking.
The shared default instance acts as a stateless factory — ``__aenter__``
creates a fresh ``Progress`` per invocation so concurrent tasks never
share mutable state.
"""
_impl: ProgressLike | None = None
async def __aenter__(self) -> Progress:
server_ref = _current_server.get()
if server_ref is None or server_ref() is None:
raise RuntimeError("Progress dependency requires a FastMCP server context.")
instance = Progress()
if is_docket_available():
try:
from docket.dependencies import current_execution
instance._impl = current_execution.get().progress
return instance
except LookupError:
pass
instance._impl = InMemoryProgress()
return instance
async def __aexit__(
self,
exc_type: type[BaseException] | None,View on GitHub (pinned to 1f02114297)
Solutions
- Use Progress only inside tool/resource/prompt handlers executed through the FastMCP server request path
- In tests, run handlers via fastmcp's in-memory Client so the server contextvar is set
- For background work, capture and restore the context (FastMCP's background context mechanism) before entering Progress
- Don't store the Progress instance beyond the request scope; create it fresh per request
Example fix
// before
async def worker():
with Progress() as p: # RuntimeError: no server context
p.increment()
// after
async def my_tool(progress: Progress) -> str: # injected inside request
await progress.increment()
return "ok" Defensive patterns
Strategy: type-guard
Validate before calling
from fastmcp.server import dependencies
server = dependencies._current_server.get()
if server is None or server() is None:
raise SkipProgress # handle before entering Progress Type guard
def in_server_context() -> bool:
from fastmcp.server import dependencies
ref = dependencies._current_server.get()
return ref is not None and ref() is not None Try / catch
try:
async with Progress() as progress:
await progress.set_total(10)
except RuntimeError as e:
if 'server context' in str(e):
logger.warning('progress unavailable outside request') Prevention
- Accept Progress as an injected handler parameter instead of constructing it
- Create Progress per request; don't cache instances across requests or tasks
- Use context-propagating task spawning inside handlers
- Run tests via the fastmcp in-memory Client, not direct function calls
When it happens
Trigger: Entering `with Progress() as progress:` outside an MCP request — direct function calls in scripts/tests, background tasks spawned without context propagation, or code run after the server request finished (weakref expired).
Common situations: Unit tests invoking tool functions directly instead of through a client; asyncio.create_task inside a tool losing the contextvar; long-lived workers holding a Progress beyond the request lifetime.
Related errors
- No active context found. This can happen if: - Called outs
- Total must be at least 1
- Amount must be at least 1
- mcp_tool() got unexpected keyword argument(s): {sorted(unkno
- mcp_resource() got unexpected keyword argument(s): {sorted(u
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/d64b5726819fbb8d.
Report an issue: GitHub.