Skip to content

Refactor prompt behavior and add meta support#2610

Merged
jlowin merged 2 commits intomainfrom
revert-2608-revert-2600-prompt-content-canonical
Dec 15, 2025
Merged

Refactor prompt behavior and add meta support#2610
jlowin merged 2 commits intomainfrom
revert-2608-revert-2600-prompt-content-canonical

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 13, 2025

Important: this was already merged in #2598 and reverted in #2609 because I want to hold it for 2.15.0. This is the new PR with the same logic.

Merge this AFTER #2611


Applies the same pattern as the new ResourceContent (#2598) user-facing class and internal refactor, but to prompts. PromptResult wraps messages with description and metadata, providing full control over prompt responses. Internally, prompts are refactored around a new canonical representation.

from fastmcp import FastMCP
from fastmcp.prompts import PromptResult, Message

mcp = FastMCP()

@mcp.prompt
def code_review(code: str) -> PromptResult:
    return PromptResult(
        messages=[Message(f"Review this code:\n{code}")],
        description="Code review prompt",
        meta={"review_type": "security"}
    )

Key changes:

  • PromptResult canonical type with messages, description, and meta fields
  • Public render() accepts list[PromptMessage] | PromptResult (backwards compatible)
  • Private _render() always returns PromptResult with deprecation warning for old return type
  • Meta passthrough to MCP's GetPromptResult._meta field

@jlowin jlowin added the DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. label Dec 13, 2025
@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 13, 2025
@jlowin jlowin added this to the 2.15 milestone Dec 13, 2025
@jlowin jlowin added feature Major new functionality. Reserved for 2-4 significant PRs per release. Not for issues. and removed enhancement Improvement to existing functionality. For issues and smaller PR improvements. labels Dec 13, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 13, 2025

Walkthrough

Introduces a canonical PromptResult model with fields messages, description, and meta, plus constructors (from_value) and conversion (to_mcp_prompt_result). Rendering now funnels through a new _render(...) path that returns PromptResult; FunctionPrompt.render and related callables were updated to accept and/or return the new _PromptFnReturn/_SyncPromptFnReturn types. Many signatures and imports across prompts, prompt manager, proxy, middleware, caching, and server were changed to use PromptResult instead of GetPromptResult or list[PromptMessage]. Deprecation handling was added for legacy list returns.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main changes: refactoring prompt behavior and adding metadata support.
Description check ✅ Passed The pull request description is well-structured, explaining context, key changes, and providing a clear example. However, the Contributors Checklist items are not explicitly marked as completed.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% 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 revert-2608-revert-2600-prompt-content-canonical

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1973a3c and 065be20.

📒 Files selected for processing (3)
  • src/fastmcp/prompts/prompt.py (8 hunks)
  • src/fastmcp/server/proxy.py (4 hunks)
  • src/fastmcp/server/server.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Write Python code with Python ≥3.10 and include full type annotations
Use specific exception types in error handling - never use bare except
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if they're shorter

Files:

  • src/fastmcp/server/server.py
  • src/fastmcp/server/proxy.py
  • src/fastmcp/prompts/prompt.py
🧠 Learnings (1)
📚 Learning: 2025-12-13T19:58:20.851Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-13T19:58:20.851Z
Learning: Applies to tests/**/*.py : Pass FastMCP servers directly to clients using in-memory transport for testing; only use HTTP transport when explicitly testing network features

Applied to files:

  • src/fastmcp/server/proxy.py
🧬 Code graph analysis (3)
src/fastmcp/server/server.py (1)
src/fastmcp/prompts/prompt.py (2)
  • PromptResult (68-110)
  • to_mcp_prompt_result (104-110)
src/fastmcp/server/proxy.py (4)
src/fastmcp/prompts/prompt.py (4)
  • Prompt (113-230)
  • PromptResult (68-110)
  • render (195-205)
  • render (397-460)
src/fastmcp/server/server.py (2)
  • get_prompt (1056-1060)
  • name (367-368)
src/fastmcp/prompts/prompt_manager.py (1)
  • get_prompt (53-58)
src/fastmcp/client/client.py (3)
  • get_prompt (1041-1047)
  • get_prompt (1050-1058)
  • get_prompt (1060-1089)
src/fastmcp/prompts/prompt.py (2)
src/fastmcp/utilities/types.py (1)
  • FastMCPBaseModel (38-41)
src/fastmcp/server/proxy.py (1)
  • render (531-541)
⏰ 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 with lowest-direct dependencies
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (13)
src/fastmcp/server/server.py (5)

63-63: LGTM! Clean import for the new canonical type.

The PromptResult import is correctly added to support the refactored prompt handling flow.


1779-1787: LGTM! Clean separation of concerns.

The refactor properly separates the internal PromptResult handling from MCP protocol conversion. The delegation to _get_prompt_content_middleware followed by conversion to GetPromptResult maintains a clean API boundary.


1789-1805: LGTM! Well-designed internal API.

The new _get_prompt_content_middleware method provides a clean internal interface that returns PromptResult directly, avoiding unnecessary conversions for internal operations and mounted server delegation. The middleware context setup is consistent with existing patterns.


1807-1810: LGTM! Consistent with the new internal API.

The return type change to PromptResult aligns with the canonical internal representation and maintains consistency across the refactored prompt handling flow.


1829-1831: LGTM! Efficient delegation to mounted servers.

The change to call _get_prompt_content_middleware ensures mounted servers return PromptResult directly, avoiding unnecessary protocol conversions for internal operations.

src/fastmcp/server/proxy.py (2)

30-30: LGTM! Correct conversion from MCP to internal format.

The refactor properly converts the remote server's GetPromptResult to the internal PromptResult format, correctly mapping messages, description, and meta fields. The import change aligns with the new internal API.

Also applies to: 255-274


531-541: LGTM! Correct proxy prompt rendering.

The conversion from GetPromptResult to PromptResult is implemented correctly. The type: ignore[override] comment is appropriate since the base class Prompt.render() allows both list[PromptMessage] and PromptResult return types for backward compatibility. The comment correctly notes that runtime meta (not static prompt meta) is preserved.

src/fastmcp/prompts/prompt.py (6)

7-7: LGTM! Necessary imports for the refactor.

The added imports support deprecation warnings, MCP protocol conversion, and settings-based deprecation control.

Also applies to: 12-12, 18-18


46-53: LGTM! Clear type aliases for prompt returns.

The type aliases _SyncPromptFnReturn and _PromptFnReturn clearly document what prompt functions can return before conversion to PromptResult, improving code readability and type consistency.


68-111: LGTM! Well-designed canonical result type.

The PromptResult class provides a clean canonical representation with:

  • Proper field definitions for messages, description, and meta
  • from_value class method with correct meta merging logic
  • to_mcp_prompt_result for protocol conversion

The meta merging in from_value correctly handles all cases: when both source and argument have meta (merge with argument taking precedence), when only one has meta, or when neither has meta.


165-165: LGTM! Excellent backward compatibility handling.

The refactor maintains backward compatibility while introducing the new canonical type:

  • Prompt.render() allows both list[PromptMessage] (deprecated) and PromptResult returns
  • New _render() method provides internal API that always returns PromptResult
  • Deprecation warning properly identifies the prompt class and name for easy migration
  • stacklevel=2 should correctly point to the caller of _render
  • Wrapping uses PromptResult.from_value with self.description and self.meta to preserve metadata

Also applies to: 198-230


236-236: LGTM! Type signatures consistent with new aliases.

The FunctionPrompt signatures correctly use the new _PromptFnReturn type alias, maintaining consistency across the codebase.

Also applies to: 245-245


397-457: LGTM! Concrete implementation returns canonical type.

FunctionPrompt.render() now directly returns PromptResult with messages, description, and meta, avoiding the deprecated path. This is the preferred implementation pattern that other prompt types should follow.


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: 0

🧹 Nitpick comments (1)
src/fastmcp/prompts/prompt.py (1)

207-230: Verify stacklevel for deprecation warning.

The stacklevel=2 may not point to the user's code when _render() is called through PromptManager.render_prompt(). The call chain is:
user code → PromptManager.render_prompt() → prompt._render() → warnings.warn()

With stacklevel=2, the warning will point to render_prompt() rather than the user's prompt function definition. Consider stacklevel=3 or dynamically calculating based on context if you want the warning to be more actionable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9e29cf and 1973a3c.

⛔ Files ignored due to path filters (5)
  • AGENTS.md is excluded by none and included by none
  • tests/prompts/test_prompt.py is excluded by none and included by none
  • tests/prompts/test_prompt_manager.py is excluded by none and included by none
  • tests/server/test_server.py is excluded by none and included by none
  • tests/server/test_server_interactions.py is excluded by none and included by none
📒 Files selected for processing (8)
  • docs/servers/prompts.mdx (1 hunks)
  • src/fastmcp/prompts/__init__.py (1 hunks)
  • src/fastmcp/prompts/prompt.py (8 hunks)
  • src/fastmcp/prompts/prompt_manager.py (4 hunks)
  • src/fastmcp/server/middleware/caching.py (4 hunks)
  • src/fastmcp/server/middleware/middleware.py (2 hunks)
  • src/fastmcp/server/proxy.py (4 hunks)
  • src/fastmcp/server/server.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Write Python code with Python ≥3.10 and include full type annotations
Use specific exception types in error handling - never use bare except
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if they're shorter

Files:

  • src/fastmcp/prompts/__init__.py
  • src/fastmcp/server/middleware/middleware.py
  • src/fastmcp/server/proxy.py
  • src/fastmcp/server/middleware/caching.py
  • src/fastmcp/prompts/prompt_manager.py
  • src/fastmcp/server/server.py
  • src/fastmcp/prompts/prompt.py
docs/**/*.mdx

📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)

docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...

Files:

  • docs/servers/prompts.mdx
🧠 Learnings (1)
📚 Learning: 2025-12-13T19:58:20.851Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-13T19:58:20.851Z
Learning: Applies to tests/**/*.py : Pass FastMCP servers directly to clients using in-memory transport for testing; only use HTTP transport when explicitly testing network features

Applied to files:

  • src/fastmcp/server/proxy.py
🧬 Code graph analysis (7)
src/fastmcp/prompts/__init__.py (1)
src/fastmcp/prompts/prompt.py (3)
  • Message (33-41)
  • Prompt (113-230)
  • PromptResult (68-110)
src/fastmcp/server/middleware/middleware.py (1)
src/fastmcp/prompts/prompt.py (2)
  • Prompt (113-230)
  • PromptResult (68-110)
src/fastmcp/server/proxy.py (3)
src/fastmcp/prompts/prompt.py (4)
  • Prompt (113-230)
  • PromptResult (68-110)
  • render (195-205)
  • render (397-460)
src/fastmcp/server/server.py (2)
  • get_prompt (1042-1046)
  • name (369-370)
src/fastmcp/client/client.py (3)
  • get_prompt (974-980)
  • get_prompt (983-991)
  • get_prompt (993-1022)
src/fastmcp/server/middleware/caching.py (2)
src/fastmcp/prompts/prompt.py (2)
  • Prompt (113-230)
  • PromptResult (68-110)
src/fastmcp/server/middleware/middleware.py (1)
  • CallNext (42-43)
src/fastmcp/prompts/prompt_manager.py (2)
src/fastmcp/prompts/prompt.py (4)
  • FunctionPrompt (233-460)
  • Prompt (113-230)
  • PromptResult (68-110)
  • _render (207-230)
src/fastmcp/exceptions.py (1)
  • PromptError (22-23)
src/fastmcp/server/server.py (1)
src/fastmcp/prompts/prompt.py (2)
  • PromptResult (68-110)
  • to_mcp_prompt_result (104-110)
src/fastmcp/prompts/prompt.py (2)
src/fastmcp/utilities/types.py (1)
  • FastMCPBaseModel (38-41)
src/fastmcp/server/proxy.py (1)
  • render (531-541)
⏰ 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 ubuntu-latest
  • GitHub Check: Run tests: Python 3.10 on windows-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (19)
src/fastmcp/prompts/__init__.py (1)

1-10: LGTM!

The PromptResult is correctly added to both the import and __all__ export, making it available at the package level consistent with other public types like Message and Prompt.

src/fastmcp/prompts/prompt_manager.py (3)

9-14: LGTM!

Import changes correctly bring in PromptResult and _PromptFnReturn type alias, aligning with the new type system introduced in this PR.


30-34: LGTM!

The refactored mask_error_details assignment correctly respects explicit parameter overrides while falling back to global settings when None is provided. The ternary expression is clear and idiomatic.


104-124: LGTM!

The render_prompt changes are correct:

  • Return type properly updated to PromptResult
  • Calls internal _render() which always returns PromptResult
  • PromptError is caught and re-raised directly without double-wrapping, while general exceptions are still wrapped with appropriate masking
src/fastmcp/prompts/prompt.py (3)

46-53: LGTM!

The type aliases clearly document the allowed return types for prompt functions. _SyncPromptFnReturn covers synchronous returns while _PromptFnReturn extends to include Awaitable variants.


68-111: LGTM!

The PromptResult class is well-designed:

  • Fields are properly typed with descriptive Field annotations
  • from_value() correctly handles both PromptResult and list[PromptMessage] inputs with proper meta merging
  • to_mcp_prompt_result() correctly maps to the MCP protocol's GetPromptResult

397-457: LGTM!

FunctionPrompt.render() correctly constructs and returns a PromptResult with messages, description, and meta. The conversion logic for various return types (strings, dicts, PromptMessage) is preserved.

src/fastmcp/server/middleware/middleware.py (1)

170-175: LGTM!

The on_get_prompt signature is correctly updated to use PromptResult for both the call_next return type and the method return type, maintaining consistency with the new canonical prompt result type.

docs/servers/prompts.mdx (2)

198-211: LGTM!

The return values section is updated to include PromptResult as a documented option, and the example correctly demonstrates returning list[Message] with the simplified import.


213-249: LGTM!

The new PromptResult section is well-structured:

  • Version badge (2.14.1) clearly indicates when this feature was added
  • Code example is complete and runnable
  • Field descriptions are clear and concise
  • The <Note> appropriately distinguishes runtime meta from static decorator meta

One minor observation: the example on line 239 states messages accepts "a list of PromptMessage or Message objects", which is accurate since Message is a constructor that returns PromptMessage.

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

63-63: LGTM!

Import correctly added for the new PromptResult type which is now used throughout the prompt rendering pipeline.


1742-1768: Clean separation of concerns in prompt middleware layer.

The refactor introduces a two-tier approach:

  • _get_prompt_middleware handles MCP protocol conversion via to_mcp_prompt_result()
  • _get_prompt_content_middleware returns the canonical PromptResult for internal use

This enables mounted servers to obtain PromptResult directly without redundant conversions.


1770-1808: LGTM!

The _get_prompt method correctly updated to return PromptResult. The mounted server resolution now properly delegates to _get_prompt_content_middleware to maintain type consistency throughout the call chain.

src/fastmcp/server/proxy.py (3)

30-30: LGTM!

Import correctly updated to use PromptResult from the public fastmcp.prompts module.


531-541: LGTM with clarification on type override.

The type: ignore[override] is appropriate here since ProxyPrompt.render returns only PromptResult (narrowing the return type from the base class's list[PromptMessage] | PromptResult). The comment helpfully explains that runtime meta from the remote server is preserved rather than static prompt metadata.


255-274: No action needed. The result.meta accessor is correct.

The MCP GetPromptResult type uses the standard Python pattern of a private _meta field with a public .meta property accessor. The constructor takes _meta as a parameter (as seen in tasks/handlers.py and tasks/converters.py), but accessing result.meta on an instance is the correct way to retrieve the metadata. This pattern is used consistently throughout the codebase (e.g., proxy.py lines 273, 525, 540).

Likely an incorrect or invalid review comment.

src/fastmcp/server/middleware/caching.py (3)

20-20: LGTM!

Import correctly includes PromptResult for use in the caching middleware.


223-227: Cache type migration consideration.

The cache now stores PromptResult instead of GetPromptResult. For deployments upgrading to this version with persistent cache storage, existing cached prompt entries may fail deserialization.

Consider whether cache invalidation guidance should be documented for users with persistent cache backends, or if the schema difference is compatible (both have messages, description, meta fields).


416-440: LGTM!

The on_get_prompt method is cleanly updated with consistent PromptResult typing throughout. The caching logic remains unchanged - only the stored/retrieved type is updated to match the new canonical prompt result type.

@jlowin jlowin removed the DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. label Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Major new functionality. Reserved for 2-4 significant PRs per release. Not for issues. 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