Skip to content

Add task_meta to prompts and centralize fn_key enrichment#2751

Merged
jlowin merged 7 commits intomainfrom
refactor/fn-key-enrichment-to-server
Dec 26, 2025
Merged

Add task_meta to prompts and centralize fn_key enrichment#2751
jlowin merged 7 commits intomainfrom
refactor/fn-key-enrichment-to-server

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 26, 2025

Completes the task_meta parameter work started in #2749 (tools) and #2750 (resources) by extending it to prompts, then consolidates the fn_key enrichment logic.

task_meta for prompts:

from fastmcp.server.tasks import TaskMeta

# Prompts now support explicit background task control
result = await server.render_prompt("my_prompt", {"arg": "value"}, task_meta=TaskMeta(ttl=300))

fn_key centralization: Moved fn_key enrichment from 9 locations (component _run/_read/_render methods and provider wrappers) to 3 server methods (call_tool, read_resource, render_prompt). The server now sets fn_key immediately after finding the component, before invoking its method.

Also removes dead code: unused _task_metadata and _docket_fn_key context variables, get_task_metadata() function, and deprecated key parameter in check_background_task().

Extends task_meta parameter pattern to prompts (matching tools and resources)
and moves fn_key enrichment from 9 component/wrapper methods to 3 server
methods (call_tool, read_resource, render_prompt).

Also removes dead code: unused _task_metadata and _docket_fn_key context
variables, get_task_metadata() function, and deprecated key parameter in
check_background_task().
@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. labels Dec 26, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 26, 2025

Warning

Rate limit exceeded

@jlowin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between e22f9df and f429b01.

⛔ Files ignored due to path filters (3)
  • v3-notes/prompt-internal-types.md is excluded by none and included by none
  • v3-notes/provider-architecture.md is excluded by none and included by none
  • v3-notes/task-meta-parameter.md is excluded by none and included by none
📒 Files selected for processing (1)
  • src/fastmcp/server/server.py

Walkthrough

This PR makes task metadata explicit across FastMCP by adding TaskMeta-aware overloads to Prompt._render and by propagating task_meta through server/provider methods. It removes implicit fn_key enrichment in Resource._read, ResourceTemplate._read, Tool._run, and provider delegation, deletes the get_task_metadata public export, and updates check_background_task to drop the separate key parameter and rely on task_meta.fn_key or the component key. render_prompt, call_tool, and read_resource paths were updated to accept and forward TaskMeta and to return either PromptResult or CreateTaskResult depending on task_meta.

Possibly related PRs

  • #2663: Refactors background-task routing and directly modifies Prompt._render, provider delegation, and check_background_task to use explicit TaskMeta propagation.
  • #2750: Changes task_meta handling across resources, templates, and providers, removing automatic fn_key enrichment in read/_read call paths.
  • #2749: Adds TaskMeta propagation and overloads in call_tool and provider/tool delegation paths, aligning provider prompt/resource behavior with explicit task_meta routing.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.91% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the two main changes: adding task_meta parameter to prompts and centralizing fn_key enrichment logic into the server.
Description check ✅ Passed The description provides comprehensive context, concrete code examples, and explains both the task_meta extension and fn_key centralization, though the checklist items are unchecked.

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/fastmcp/server/server.py (2)

1597-1601: Misleading comment about fn_key enrichment location.

The comment states "The component (or provider wrapper) will enrich fn_key with self.key" but this is inaccurate. Based on the implementation:

  1. MCP handlers extract task_meta without fn_key ✓
  2. Server methods (call_tool at lines 1208-1210) enrich fn_key before calling component methods ✓
  3. Component methods receive task_meta with fn_key already set ✓

The comment should clarify that the server's call_tool method enriches fn_key, not the component itself.

🔎 Proposed fix
         # Extract SEP-1686 task metadata from request context.
-        # NOTE: fn_key is NOT set here. The component (or provider wrapper) will
-        # enrich fn_key with self.key. For mounted servers, the provider wrapper
-        # sets fn_key to the parent's namespaced key, ensuring Docket finds the
-        # correctly registered function.
+        # NOTE: fn_key is NOT set here. The server's call_tool() method will
+        # enrich fn_key with tool.key before calling the tool's _run() method.
+        # For mounted servers, the parent server sets fn_key to the namespaced
+        # key, ensuring Docket finds the correctly registered function.

1641-1647: Comment should clarify that server enriches fn_key, not component.

Similar to the issue in _call_tool_mcp, this comment states "The component (or provider wrapper) will enrich fn_key" but the actual implementation shows the server methods (read_resource at lines 1302-1304 and 1326-1328) perform the enrichment.

🔎 Proposed fix
         # Extract SEP-1686 task metadata from request context.
-        # NOTE: fn_key is NOT set here for resources because we don't know yet
-        # if the URI will be handled by a direct resource or a template. Templates
-        # need their pattern-based key for Docket lookup, not the concrete URI.
-        # The component (or provider wrapper) will enrich fn_key with self.key.
-        # For mounted servers, the provider wrapper sets fn_key to the parent's
-        # namespaced key, ensuring Docket finds the correctly registered function.
+        # NOTE: fn_key is NOT set here because we don't know yet if the URI will
+        # be handled by a direct resource or a template. The server's read_resource()
+        # method will enrich fn_key with resource.key or template.key after
+        # determining which component handles the URI. For mounted servers, the
+        # parent server sets fn_key to the namespaced key, ensuring Docket finds
+        # the correctly registered function.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc19f1f and 9bcaec0.

⛔ Files ignored due to path filters (6)
  • tests/server/providers/test_local_provider_prompts.py is excluded by none and included by none
  • tests/server/tasks/test_resource_task_meta_parameter.py is excluded by none and included by none
  • tests/server/tasks/test_task_mount.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 (8)
  • src/fastmcp/prompts/prompt.py
  • src/fastmcp/resources/resource.py
  • src/fastmcp/resources/template.py
  • src/fastmcp/server/dependencies.py
  • src/fastmcp/server/providers/fastmcp_provider.py
  • src/fastmcp/server/server.py
  • src/fastmcp/server/tasks/routing.py
  • src/fastmcp/tools/tool.py
💤 Files with no reviewable changes (3)
  • src/fastmcp/resources/template.py
  • src/fastmcp/server/dependencies.py
  • src/fastmcp/resources/resource.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/tools/tool.py
  • src/fastmcp/server/tasks/routing.py
  • src/fastmcp/server/server.py
  • src/fastmcp/prompts/prompt.py
  • src/fastmcp/server/providers/fastmcp_provider.py
🧠 Learnings (3)
📚 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/tasks/routing.py
  • src/fastmcp/server/server.py
  • src/fastmcp/prompts/prompt.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 : Never use bare except - be specific with exception types

Applied to files:

  • src/fastmcp/server/tasks/routing.py
  • src/fastmcp/prompts/prompt.py
📚 Learning: 2025-11-26T21:51:44.174Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions

Applied to files:

  • src/fastmcp/server/server.py
🧬 Code graph analysis (3)
src/fastmcp/server/tasks/routing.py (3)
src/fastmcp/resources/resource.py (1)
  • key (368-370)
src/fastmcp/resources/template.py (1)
  • key (274-276)
src/fastmcp/utilities/components.py (1)
  • key (94-102)
src/fastmcp/server/server.py (2)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/server/context.py (1)
  • get_prompt (294-311)
src/fastmcp/server/providers/fastmcp_provider.py (4)
src/fastmcp/prompts/prompt.py (6)
  • _render (306-310)
  • _render (313-317)
  • _render (319-357)
  • PromptResult (109-188)
  • render (256-267)
  • render (546-575)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/server/server.py (3)
  • render_prompt (1349-1356)
  • render_prompt (1359-1366)
  • render_prompt (1368-1439)
src/fastmcp/server/providers/proxy.py (1)
  • render (407-420)
⏰ 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.10 on windows-latest
  • GitHub Check: Run tests with lowest-direct dependencies
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest

Comment thread src/fastmcp/server/server.py Outdated
Comment thread src/fastmcp/server/tasks/routing.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/fastmcp/server/server.py (2)

1180-1181: Outdated comment contradicts the actual implementation.

This comment states fn_key enrichment happens in Tool._run(), but lines 1208-1210 clearly show enrichment happens here in call_tool(). The comment appears to be stale after the refactor that centralized fn_key enrichment to the server.

🔎 Proposed fix
-        # Note: fn_key enrichment happens in Tool._run(), not here,
-        # so that provider wrappers can enrich with their namespaced key.
+        # Note: fn_key enrichment happens here after finding the tool.
+        # For mounted servers, the parent's provider sets fn_key to the
+        # namespaced key before delegating, ensuring correct Docket routing.

1273-1276: Outdated comment contradicts the actual implementation.

This comment states fn_key enrichment happens in each component's _read() method, but lines 1302-1304 and 1326-1328 show enrichment happens here in read_resource(). The comment is stale after the centralization refactor.

🔎 Proposed fix
-        # Note: fn_key enrichment happens in each component's _read() method,
-        # not here, because resources and templates use different key formats:
-        # - Resources use Resource.make_key(uri) for the concrete URI
-        # - Templates use self.key which is the template pattern
+        # Note: fn_key enrichment happens here after finding the resource/template.
+        # Resources and templates use different key formats:
+        # - Resources use resource.key (Resource.make_key(uri) for concrete URI)
+        # - Templates use template.key (the template pattern)
+        # For mounted servers, the parent's provider sets fn_key to the
+        # namespaced key before delegating, ensuring correct Docket routing.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9bcaec0 and e22f9df.

⛔ Files ignored due to path filters (2)
  • v3-notes/provider-test-pattern.md is excluded by none and included by none
  • v3-notes/task-meta-parameter.md is excluded by none and included by none
📒 Files selected for processing (2)
  • src/fastmcp/server/server.py
  • src/fastmcp/server/tasks/routing.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/routing.py
  • src/fastmcp/server/server.py
🧠 Learnings (3)
📚 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/tasks/routing.py
  • src/fastmcp/server/server.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 : Never use bare except - be specific with exception types

Applied to files:

  • src/fastmcp/server/tasks/routing.py
📚 Learning: 2025-11-26T21:51:44.174Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: .cursor/rules/core-mcp-objects.mdc:0-0
Timestamp: 2025-11-26T21:51:44.174Z
Learning: Review and update related Manager classes (ToolManager, ResourceManager, PromptManager) when modifying MCP object definitions

Applied to files:

  • src/fastmcp/server/server.py
🧬 Code graph analysis (2)
src/fastmcp/server/tasks/routing.py (3)
src/fastmcp/resources/resource.py (1)
  • key (368-370)
src/fastmcp/resources/template.py (1)
  • key (274-276)
src/fastmcp/utilities/components.py (1)
  • key (94-102)
src/fastmcp/server/server.py (7)
src/fastmcp/resources/resource.py (1)
  • key (368-370)
src/fastmcp/resources/template.py (1)
  • key (274-276)
src/fastmcp/utilities/components.py (1)
  • key (94-102)
src/fastmcp/prompts/prompt.py (5)
  • PromptResult (109-188)
  • _render (306-310)
  • _render (313-317)
  • _render (319-357)
  • to_mcp_prompt_result (181-188)
src/fastmcp/server/tasks/config.py (1)
  • TaskMeta (25-37)
src/fastmcp/server/middleware/tool_injection.py (1)
  • get_prompt (64-72)
src/fastmcp/server/context.py (2)
  • get_prompt (294-311)
  • request_context (223-249)
⏰ 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). (1)
  • GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (6)
src/fastmcp/server/tasks/routing.py (1)

74-76: LGTM!

The updated comment accurately reflects the centralized fn_key enrichment design. The server now sets task_meta.fn_key before invoking component methods, and the fallback to component.key properly handles direct calls to check_background_task that bypass the server layer.

src/fastmcp/server/server.py (5)

24-24: LGTM!

Appropriate import for creating modified copies of the TaskMeta dataclass during fn_key enrichment.


1208-1212: LGTM!

The fn_key enrichment logic correctly:

  1. Only modifies when task_meta is provided but fn_key is None
  2. Uses immutable replace() to avoid mutating the original TaskMeta
  3. Sets fn_key to tool.key for proper Docket routing

1302-1332: LGTM!

The fn_key enrichment correctly distinguishes between resources and templates:

  • Resources use resource.key (derived from concrete URI)
  • Templates use template.key (derived from URI template pattern)

This ensures proper Docket routing for both component types.


1348-1427: LGTM!

The render_prompt method correctly:

  1. Provides overloads for type narrowing based on task_meta presence
  2. Propagates task_meta through the middleware chain (line 1415)
  3. Enriches fn_key with prompt.key before delegating to _render() (lines 1423-1425)
  4. Documents the task_meta parameter and return type variations

This completes the task_meta support for prompts, consistent with tools and resources.


1660-1701: LGTM!

The _get_prompt_mcp handler now correctly:

  1. Extracts SEP-1686 task metadata from MCP request context (lines 1681-1691)
  2. Passes task_meta to render_prompt() for centralized fn_key enrichment (line 1693)
  3. Handles both CreateTaskResult and PromptResult return types (lines 1695-1697)

The implementation is consistent with _call_tool_mcp and _read_resource_mcp, completing the task_meta propagation for all three component types.

@jlowin jlowin force-pushed the refactor/fn-key-enrichment-to-server branch from c025f06 to f429b01 Compare December 26, 2025 19:57
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: The integration test timed out after 30 seconds due to a rate limit response from GitHub's Copilot MCP API.

Root Cause: The test is hitting the external GitHub Copilot MCP API (), which returned a 429 rate limit error. The test timeout occurred because the client was waiting for a response that never completed due to the rate limiting. This is not related to the PR changes - it's an environmental issue with the external API.

Suggested Solution:

This is a flaky integration test that depends on external API availability and rate limits. The failure is unrelated to the refactoring of enrichment logic in this PR. Options:

  1. Retry the workflow - The rate limit may have reset, and the test may pass on retry
  2. Merge anyway - Since this is an external API issue and not caused by the PR changes, it's safe to merge if other tests pass
  3. Long-term: Consider adding retry logic with exponential backoff to for 429 responses
Detailed Analysis

The test failed with this sequence:

  1. Test called
  2. Request was made to
  3. GitHub API returned
  4. The client kept waiting (as designed) and hit the 30-second pytest timeout
  5. During teardown, the actual HTTPStatusError was logged:

Key log excerpt showing the rate limit:

httpx.HTTPStatusError: Client error '429 Too Many Requests' for url 'https://api.githubcopilot.com/mcp/'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429

This PR changes internal enrichment logic and adds parameter support for prompts - none of which affects external HTTP requests to GitHub's API.

Related Files
    • The failing test that calls GitHub's Copilot MCP API
  • Test uses environment variable for authentication
  • No changes to client HTTP logic or rate limiting in this PR

@jlowin jlowin merged commit 7b0de84 into main Dec 26, 2025
17 checks passed
@jlowin jlowin deleted the refactor/fn-key-enrichment-to-server branch December 26, 2025 20:04
@coderabbitai coderabbitai Bot mentioned this pull request Jan 19, 2026
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant