{"record":{"id":"8fa17c236f5f81a5","repo":"PrefectHQ/fastmcp","slug":"task-enabled-tools-names-require-the-tasks-ext","errorCode":null,"errorMessage":"Task-enabled tools ({names}) require the tasks extension, but no extension with identifier {TASKS_EXTENSION_ID!r} is registered. Install it with `pip install 'fastmcp[tasks]'` and register it via `mcp.add_extension(TasksExtension(...))`.","messagePattern":"Task-enabled tools \\((.+?)\\) require the tasks extension, but no extension with identifier (.+?) is registered\\. Install it with `pip install 'fastmcp\\[tasks\\]'` and register it via `mcp\\.add_extension\\(TasksExtension\\(\\.\\.\\.\\)\\)`\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/mixins/lifespan.py","lineNumber":142,"sourceCode":"        # fail a child that legitimately relies on the root's registration.\n        if _lifespan_root_active.get():\n            return\n\n        if TASKS_EXTENSION_ID in self._extensions:\n            return\n\n        candidates = list(await self.get_tasks())\n\n        # ``get_tasks()`` applies server-level transforms, which can inject\n        # non-task tools (e.g. ResourcesAsTools' synthetic list/read tools) into\n        # the result, so re-filter by the actual task config here — mirroring the\n        # guard the old per-component docket registration applied.\n        task_components = [c for c in candidates if c.task_config.supports_tasks()]\n        if not task_components:\n            return\n\n        names = \", \".join(sorted(c.name for c in task_components))\n        raise RuntimeError(\n            f\"Task-enabled tools ({names}) require the tasks extension, but no \"\n            f\"extension with identifier {TASKS_EXTENSION_ID!r} is registered. \"\n            \"Install it with `pip install 'fastmcp[tasks]'` and register it via \"\n            \"`mcp.add_extension(TasksExtension(...))`.\"\n        )\n\n    def _capture_shared_context(self: FastMCP) -> None:\n        \"\"\"Snapshot the live ``SharedContext`` ContextVar values.\n\n        The SDK v2 dispatcher runs each request handler in the *message\n        sender's* contextvars (via ``ContextReceiveStream.last_context``), not\n        the server-lifespan context. App-scoped ``Shared()`` dependencies rely\n        on ``uncalled_for.SharedContext`` ContextVars set during the lifespan,\n        which are therefore invisible to handlers. We capture those values here\n        so ``FastMCPServerMiddleware`` can re-apply them per request.\n        \"\"\"\n        try:\n            self._shared_context_snapshot = {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/mixins/lifespan.py#L124-L160","documentation":"A RuntimeError raised during server startup (_validate_task_extension_registered, invoked by the lifespan manager) when task-enabled tools are registered but the tasks extension (identifier TASKS_EXTENSION_ID) is absent. Docket-backed task execution requires that extension; the error lists the offending tool names and the exact install/registration steps.","triggerScenarios":"Decorating tools with task_config that supports_tasks() (e.g. @mcp.tool(task=True) or a task_config with mode other than forbid) while never calling mcp.add_extension(TasksExtension(...)); happens at lifespan startup, not at decoration time.","commonSituations":"Upgrading FastMCP and adopting the tasks extension without adding the dependency or registration; deploying with the base package where `fastmcp[tasks]` extra was not installed; copying task-enabled example code into an existing server.","solutions":["Install the extra: pip install 'fastmcp[tasks]'.","Register the extension at startup: mcp.add_extension(TasksExtension(...)).","If background execution is not intended, remove the task_config/task=True settings from the listed tools so they run inline."],"exampleFix":"// before\nmcp = FastMCP(\"server\")\n\n@mcp.tool(task=True)\ndef report(): ...\n# RuntimeError at startup\n\n// after\nfrom fastmcp.server.extensions.tasks import TasksExtension\nmcp = FastMCP(\"server\")\nmcp.add_extension(TasksExtension())\n\n@mcp.tool(task=True)\ndef report(): ...","handlingStrategy":"validation","validationCode":"from fastmcp.server.extensions import TASKS_EXTENSION_ID\n\nregistered = {getattr(ext, \"identifier\", None) for ext in mcp.extensions}\ntask_tools = [t for t in mcp._tool_manager if getattr(t, \"task_config\", None) and t.task_config.supports_tasks()]\nif task_tools and TASKS_EXTENSION_ID not in registered:\n    raise RuntimeError(f\"install fastmcp[tasks] and add_extension(TasksExtension()) for: {[t.name for t in task_tools]}\")","typeGuard":null,"tryCatchPattern":"try:\n    mcp.run()\nexcept RuntimeError as e:\n    if \"require the tasks extension\" in str(e):\n        # surface install/registration instructions at deploy time\n        raise SystemExit(f\"deployment misconfiguration: {e}\")\n    raise","preventionTips":["Install the tasks extra in every environment: pip install 'fastmcp[tasks]'.","Always pair task-enabled tools with mcp.add_extension(TasksExtension(...)) in the same bootstrap module.","Add a startup smoke test that constructs the server to catch missing extensions before deploy.","Keep task_config usage and TasksExtension registration in the same PR/config review."],"tags":["tasks","extensions","startup","configuration"],"backgroundTag":"missing-required-extension","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}