Skip to content

Add explicit task_meta parameter to FastMCP.call_tool()#2749

Merged
jlowin merged 2 commits intomainfrom
add-task-meta-parameter
Dec 26, 2025
Merged

Add explicit task_meta parameter to FastMCP.call_tool()#2749
jlowin merged 2 commits intomainfrom
add-task-meta-parameter

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 26, 2025

Adds a task_meta: TaskMeta | None parameter to call_tool() for explicit control over sync vs background task execution. This makes the return type predictable at the call site through overloads:

# Returns ToolResult (type-safe)
result = await server.call_tool("my_tool", {"x": 5})

# Returns CreateTaskResult (type-safe)  
result = await server.call_tool("my_tool", {"x": 5}, task_meta=TaskMeta())

# With custom TTL
result = await server.call_tool("my_tool", {"x": 5}, task_meta=TaskMeta(ttl=30000))

Tools can now call other tools as background tasks with explicit control:

@server.tool
async def outer_tool(x: int) -> str:
    # Execute as background task
    result = await server.call_tool("inner_tool", {"x": x}, task_meta=TaskMeta())
    return f"Created task: {result.task.taskId}"

The TaskMeta dataclass supports:

  • ttl: int | None - TTL in milliseconds (uses server default if None)
  • fn_key: str | None - Docket function key (auto-populated from tool name if None)

Resources and prompts still use contextvar-based task routing for backwards compatibility.

Adds a `task_meta: TaskMeta | None` parameter to `call_tool()` for explicit
control over sync vs background task execution. When `task_meta` is provided,
the tool executes as a background task and returns `CreateTaskResult`; when
`None` (default), it executes synchronously and returns `ToolResult`.

This replaces implicit contextvar-based task determination for tools, making
the return type predictable at the call site. Resources and prompts still use
the contextvar approach for backwards compatibility.
@marvin-context-protocol marvin-context-protocol Bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality. tests labels Dec 26, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 26, 2025

Walkthrough

This change introduces explicit task metadata propagation throughout the tool execution pipeline. A new TaskMeta dataclass with optional ttl and fn_key fields is added to signal background task execution. The task_meta parameter is threaded through multiple layers: the server's call_tool method gains overloads to return either ToolResult or CreateTaskResult, the Tool._run method accepts task_meta, background task routing and submission handlers receive and utilize task_meta, and provider tool wrappers forward it to wrapped servers. The implementation shifts from contextvar-based task metadata handling to explicit parameter passing.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add explicit task_meta parameter to FastMCP.call_tool()' clearly and specifically summarizes the main change—introducing a new parameter for explicit task execution control.
Description check ✅ Passed The description is comprehensive, covering the new parameter, type-safe overloads, usage examples for both synchronous and background task execution, and backwards compatibility notes—addressing all key aspects of the change.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-task-meta-parameter

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f1aa27 and 4a04f44.

⛔ Files ignored due to path filters (5)
  • tests/server/providers/test_local_provider_tools.py is excluded by none and included by none
  • tests/server/tasks/test_task_meta_parameter.py is excluded by none and included by none
  • tests/server/test_dependencies.py is excluded by none and included by none
  • tests/server/test_mount.py is excluded by none and included by none
  • tests/server/test_providers.py is excluded by none and included by none
📒 Files selected for processing (7)
  • src/fastmcp/server/providers/fastmcp_provider.py
  • src/fastmcp/server/server.py
  • src/fastmcp/server/tasks/__init__.py
  • src/fastmcp/server/tasks/config.py
  • src/fastmcp/server/tasks/handlers.py
  • src/fastmcp/server/tasks/routing.py
  • src/fastmcp/tools/tool.py
🧰 Additional context used
📓 Path-based instructions (1)
src/fastmcp/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/fastmcp/**/*.py: Python ≥ 3.10 with full type annotations required
Prioritize readable, understandable code - clarity over cleverness. Avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code implementation
Be intentional about re-exports - don't blindly re-export everything to parent namespaces. Core types defining a module's purpose should be exported. Specialized features can live in submodules. Only re-export to fastmcp.* for most fundamental types
Never use bare except - be specific with exception types

Files:

  • src/fastmcp/server/tasks/__init__.py
  • src/fastmcp/server/tasks/config.py
  • src/fastmcp/server/providers/fastmcp_provider.py
  • src/fastmcp/tools/tool.py
  • src/fastmcp/server/tasks/handlers.py
  • src/fastmcp/server/server.py
  • src/fastmcp/server/tasks/routing.py
🧠 Learnings (2)
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Be intentional about re-exports - don't blindly re-export everything to parent namespaces. Core types defining a module's purpose should be exported. Specialized features can live in submodules. Only re-export to fastmcp.* for most fundamental types

Applied to files:

  • src/fastmcp/server/tasks/__init__.py
  • src/fastmcp/server/providers/fastmcp_provider.py
📚 Learning: 2025-12-25T15:53:07.646Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.646Z
Learning: Applies to src/fastmcp/**/*.py : Python ≥ 3.10 with full type annotations required

Applied to files:

  • src/fastmcp/server/providers/fastmcp_provider.py
  • src/fastmcp/tools/tool.py
  • src/fastmcp/server/tasks/handlers.py
  • src/fastmcp/server/tasks/routing.py
🧬 Code graph analysis (5)
src/fastmcp/server/tasks/__init__.py (1)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/server/providers/fastmcp_provider.py (2)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/server/server.py (4)
  • call_tool (1130-1137)
  • call_tool (1140-1147)
  • call_tool (1149-1222)
  • run (586-605)
src/fastmcp/tools/tool.py (3)
src/fastmcp/server/context.py (1)
  • fastmcp (169-174)
src/fastmcp/server/tasks/config.py (2)
  • TaskConfig (41-120)
  • TaskMeta (25-37)
src/fastmcp/server/tasks/routing.py (1)
  • check_background_task (27-93)
src/fastmcp/server/tasks/handlers.py (3)
src/fastmcp/server/context.py (1)
  • fastmcp (169-174)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/server/dependencies.py (1)
  • get_context (271-277)
src/fastmcp/server/tasks/routing.py (5)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/resources/resource.py (1)
  • key (353-355)
src/fastmcp/resources/template.py (1)
  • key (258-260)
src/fastmcp/server/dependencies.py (1)
  • get_task_metadata (280-287)
src/fastmcp/server/tasks/handlers.py (1)
  • submit_to_docket (31-153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.10 on windows-latest
  • GitHub Check: Run tests with lowest-direct dependencies
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest
🔇 Additional comments (11)
src/fastmcp/server/tasks/__init__.py (1)

7-7: LGTM!

The export of TaskMeta in __all__ is appropriate—it's a core type that defines the module's purpose for task-augmented execution, aligning with the coding guidelines on intentional re-exports.

Also applies to: 16-16

src/fastmcp/server/tasks/config.py (1)

24-37: LGTM!

The TaskMeta dataclass is well-designed with clear documentation. Using @dataclass for this simple metadata container is appropriate, and the optional fields with None defaults allow callers to override only what they need.

src/fastmcp/tools/tool.py (1)

280-311: LGTM!

The _run method signature extension is well-implemented:

  • Type annotations are complete and correct
  • Docstring clearly documents the task_meta parameter's effect on sync vs background execution
  • The task_meta is properly forwarded to check_background_task
src/fastmcp/server/providers/fastmcp_provider.py (1)

88-113: LGTM!

The _run method correctly delegates to the child server's call_tool() with task_meta forwarding, ensuring the child's middleware chain runs appropriately. The run() method raising NotImplementedError is intentional—this wrapper tool should always go through _run() to leverage the child server's middleware and task handling.

src/fastmcp/server/tasks/routing.py (2)

27-61: LGTM!

The backwards compatibility handling is well-designed:

  • Falls back to contextvar when task_meta is not provided explicitly
  • The TODO comment documents the deprecation path for the key parameter
  • Proper migration path for resources/prompts that haven't been updated yet

90-93: Clear fallback chain for fn_key resolution.

The priority order (task_meta.fn_keykeycomponent.key) provides a sensible hierarchy: explicit caller-provided key takes precedence, then deprecated parameter, then component default.

src/fastmcp/server/tasks/handlers.py (2)

64-69: Verify session_id fallback behavior.

The fallback to "internal" handles programmatic calls (e.g., tool-to-tool invocations via call_tool(task_meta=TaskMeta())). However, note that get_context() on line 65 will raise RuntimeError if no context exists at all—the try/except only catches errors from ctx.session_id access.

This should be fine since submit_to_docket is called from within component execution paths that always have an active context, but ensure this assumption holds for all call sites.


83-88: LGTM!

TTL override logic is correct:

  • task_meta.ttl takes precedence when explicitly provided
  • Falls back to docket.execution_ttl otherwise
  • Buffer added to ttl_seconds for Redis storage to prevent premature expiry
src/fastmcp/server/server.py (3)

1129-1147: Excellent use of overloads for type-safe return types.

The overload pattern provides clear type guarantees at call sites:

  • task_meta=NoneToolResult (synchronous execution)
  • task_meta=TaskMetaCreateTaskResult (background task)

This eliminates the need for type assertions by callers.


1179-1181: LGTM!

Enriching task_meta with a derived fn_key when not explicitly provided ensures proper Docket routing. Creating a new TaskMeta instance preserves immutability of the caller's object.


1540-1557: LGTM!

The MCP handler correctly extracts task metadata from the request context and constructs a TaskMeta instance. The fn_key is properly set using Tool.make_key(key) to ensure consistent Docket registration lookup.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jlowin jlowin added this to the 3.0 milestone Dec 26, 2025
@jlowin jlowin merged commit bf1c222 into main Dec 26, 2025
17 of 18 checks passed
@jlowin jlowin deleted the add-task-meta-parameter branch December 26, 2025 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant