Skip to content

Introduce Message and PromptResult as canonical prompt types#2738

Merged
jlowin merged 3 commits intomainfrom
promptresult-cleanup
Dec 26, 2025
Merged

Introduce Message and PromptResult as canonical prompt types#2738
jlowin merged 3 commits intomainfrom
promptresult-cleanup

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Dec 26, 2025

Prompts now follow the same pattern as resources (PR #2734): FastMCP types internally, MCP SDK types at the boundary. Follows up on #2610

Message is now a proper class with auto-serialization—dicts, lists, and Pydantic models are JSON-serialized automatically:

from fastmcp.prompts import Message, PromptResult

@mcp.prompt
def simple() -> str:
    return "Hello!"  # wrapped as Message("Hello!")

@mcp.prompt  
def conversation() -> list[Message]:
    return [
        Message("Analyze this data"),
        Message({"key": "value"}),  # auto-serialized to JSON
        Message("Done.", role="assistant"),
    ]

@mcp.prompt
def with_metadata() -> PromptResult:
    return PromptResult(
        messages=[Message("Review this code")],
        meta={"priority": "high"}
    )

Breaking change: Returning mcp.types.PromptMessage directly is no longer supported. Use Message instead.

Convert Message from factory function to class with auto-serialization.
PromptResult now enforces strict typing: str | list[Message].

Breaking change: mcp.types.PromptMessage no longer supported as return type.
@jlowin jlowin added the feature Major new functionality. Reserved for 2-4 significant PRs per release. Not for issues. label Dec 26, 2025
@marvin-context-protocol marvin-context-protocol Bot added the breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. label Dec 26, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 26, 2025

Walkthrough

This PR replaces the previous PromptMessage/TextContent return model with a new Message pydantic model and a PromptResult wrapper. Prompt.render() now accepts/returns str, list[Message | str], or PromptResult; Prompt.convert_result() normalizes raw outputs to PromptResult. FunctionPrompt/from_function signatures were loosened to Callable[..., Any]. Server caching and proxy layers were updated to wrap/unwrap cached prompt results and to convert runtime messages to the new Message type. Documentation and public examples were updated to reference Message and the new serialization rules.

Possibly related PRs

  • PR 2610: Makes overlapping changes to introduce canonical PromptResult/Message types and updates server, proxy, and caching codepaths.
  • PR 2663: Modifies Prompt.convert_result(), render(), and related prompt plumbing that integrates Message/PromptResult handling.
  • PR 2608: Updates public prompt API surfaces (Prompt, PromptResult, Message) and adjusts server/middleware/proxy integrations accordingly.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive The description provides code examples, explains auto-serialization, documents the breaking change, and includes key information about the feature, but lacks the Contributors and Review Checklist items required by the repository template. Add the checked Contributors Checklist items (issue reference, workflow, testing, documentation) and Review Checklist items to fully comply with the repository's pull request template.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Introduce Message and PromptResult as canonical prompt types' accurately and concisely summarizes the main change—introducing two new classes as the primary API for prompt returns.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch promptresult-cleanup

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

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7da0602 and d4bc1ab.

⛔ Files ignored due to path filters (5)
  • tests/client/tasks/test_prompt_task_mcp_message.py is excluded by none and included by none
  • tests/prompts/test_prompt.py is excluded by none and included by none
  • tests/server/middleware/test_caching.py is excluded by none and included by none
  • tests/server/providers/test_local_provider_prompts.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 (5)
  • docs/development/upgrade-guide.mdx
  • docs/servers/prompts.mdx
  • src/fastmcp/prompts/prompt.py
  • src/fastmcp/server/middleware/caching.py
  • src/fastmcp/server/providers/proxy.py
🧰 Additional context used
📓 Path-based instructions (4)
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/development/upgrade-guide.mdx
  • docs/servers/prompts.mdx
docs/**/*.{md,mdx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Documentation uses Mintlify framework. Files must be in docs.json to be included. Never modify docs/python-sdk/** (auto-generated)

Files:

  • docs/development/upgrade-guide.mdx
  • docs/servers/prompts.mdx
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.{md,mdx}: Code examples in documentation must explain before showing code and make blocks fully runnable (include imports)
Documentation structure: Headers form navigation guide with logical H2/H3 hierarchy. Content should be user-focused with sections motivating features (why) before mechanics (how). Use prose over code comments for important information
Never use 'This isn't...' or 'not just...' constructions in writing - state what something IS directly. Avoid defensive writing patterns

Files:

  • docs/development/upgrade-guide.mdx
  • docs/servers/prompts.mdx
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/middleware/caching.py
  • src/fastmcp/server/providers/proxy.py
  • src/fastmcp/prompts/prompt.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 : Python ≥ 3.10 with full type annotations required

Applied to files:

  • docs/development/upgrade-guide.mdx
  • src/fastmcp/server/middleware/caching.py
  • docs/servers/prompts.mdx
  • src/fastmcp/server/providers/proxy.py
  • src/fastmcp/prompts/prompt.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 tests/**/*.py : Pass FastMCP servers directly to clients for testing without network complexity; only use HTTP transport when explicitly testing network features

Applied to files:

  • docs/servers/prompts.mdx
  • src/fastmcp/server/providers/proxy.py
🧬 Code graph analysis (2)
src/fastmcp/server/middleware/caching.py (1)
src/fastmcp/prompts/prompt.py (3)
  • Message (42-95)
  • Prompt (195-365)
  • PromptResult (110-192)
src/fastmcp/server/providers/proxy.py (1)
src/fastmcp/prompts/prompt.py (3)
  • Message (42-95)
  • Prompt (195-365)
  • PromptResult (110-192)
🪛 Ruff (0.14.10)
src/fastmcp/prompts/prompt.py

176-179: Avoid specifying long messages outside the exception class

(TRY003)


181-183: Avoid specifying long messages outside the exception class

(TRY003)


299-302: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (16)
docs/development/upgrade-guide.mdx (1)

119-151: LGTM! Clear migration documentation with accurate before/after examples.

The upgrade section effectively documents the breaking change with:

  • Complete, runnable code examples showing the migration path
  • Key changes clearly bullet-pointed
  • Appropriate link to detailed documentation
docs/servers/prompts.mdx (3)

31-45: LGTM! Import path and Message usage are correct.

The updated import and function signature properly demonstrate the new Message-centric API.


191-209: LGTM! Return values documentation accurately reflects the implementation.

The documented return types (str, list[Message | str], PromptResult) correctly match the Prompt.render() signature and the convert_result() normalization logic.


211-244: LGTM! Comprehensive Message documentation with clear examples.

The new Message section effectively documents:

  • Auto-serialization behavior for different content types
  • Role parameter with default
  • Proper ParamField component usage for API documentation
src/fastmcp/server/providers/proxy.py (2)

36-36: LGTM! Import updated to include Message.

The import correctly pulls Message from fastmcp.prompts alongside Prompt and PromptResult.


408-414: LGTM! Correct conversion from MCP PromptMessages to Message objects.

The conversion properly handles the MCP response by:

  1. Extracting content and role from each MCP PromptMessage
  2. Wrapping them in Message objects (which correctly pass through TextContent/EmbeddedResource content types)
  3. Constructing PromptResult with the converted messages
src/fastmcp/server/middleware/caching.py (4)

20-20: LGTM! Import updated to support new caching wrappers.


99-134: Cachable wrappers correctly implement wrap/unwrap pattern.

The CachableMessage and CachablePromptResult classes follow the established pattern from CachableResourceResult and CachableToolResult.

One observation: CachableMessage.role is typed as str while Message.role is Literal["user", "assistant"]. This is acceptable for caching since the data originates from valid Message objects, but consider adding validation in unwrap() if you want defense-in-depth against corrupted cache data.


273-277: LGTM! Cache adapter correctly typed for CachablePromptResult.


474-485: LGTM! Prompt caching correctly wraps and unwraps PromptResult.

The flow properly:

  1. Unwraps cached CachablePromptResult to PromptResult on cache hit
  2. Wraps fresh PromptResult to CachablePromptResult before caching
src/fastmcp/prompts/prompt.py (6)

42-95: LGTM! Well-designed Message class with clear auto-serialization behavior.

The implementation correctly:

  • Handles passthrough for already-wrapped content types
  • Converts strings to TextContent
  • JSON-serializes complex types with a safe fallback=str
  • Provides clean conversion to MCP types via to_mcp_prompt_message()

110-192: LGTM! PromptResult properly normalizes inputs and provides clear error messages.

The _normalize_messages method correctly validates inputs and provides actionable error messages when invalid types are passed. The static analysis hint about long exception messages (TRY003) can be safely ignored here—the informative messages improve developer experience.


231-272: LGTM! Updated signatures support the new Message-centric API.

The relaxed fn: Callable[..., Any] type and updated render() return signature properly accommodate the new flexible prompt return types.


274-308: LGTM! convert_result correctly normalizes prompt return values.

The method properly handles:

  • PromptResult passthrough
  • str wrapped as single-message PromptResult
  • list|tuple with item-by-item conversion

Note: Single Message objects are not directly supported (as previously flagged in the documentation review).


368-555: LGTM! FunctionPrompt updated consistently with the new type system.

The fn field type and from_function signature changes are consistent with the base Prompt class. The render() method correctly uses convert_result() for normalization.


334-336: LGTM! Server entry point correctly normalizes via convert_result.

This ensures all prompt execution paths produce consistent PromptResult objects.

Comment thread docs/servers/prompts.mdx 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: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4bc1ab and c1c655e.

⛔ Files ignored due to path filters (2)
  • tests/prompts/test_prompt.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 (1)
  • docs/servers/prompts.mdx
🧰 Additional context used
📓 Path-based instructions (3)
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
docs/**/*.{md,mdx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Documentation uses Mintlify framework. Files must be in docs.json to be included. Never modify docs/python-sdk/** (auto-generated)

Files:

  • docs/servers/prompts.mdx
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.{md,mdx}: Code examples in documentation must explain before showing code and make blocks fully runnable (include imports)
Documentation structure: Headers form navigation guide with logical H2/H3 hierarchy. Content should be user-focused with sections motivating features (why) before mechanics (how). Use prose over code comments for important information
Never use 'This isn't...' or 'not just...' constructions in writing - state what something IS directly. Avoid defensive writing patterns

Files:

  • docs/servers/prompts.mdx
🧠 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 : Python ≥ 3.10 with full type annotations required

Applied to files:

  • docs/servers/prompts.mdx
📚 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 tests/**/*.py : Pass FastMCP servers directly to clients for testing without network complexity; only use HTTP transport when explicitly testing network features

Applied to files:

  • docs/servers/prompts.mdx
⏰ 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: Python 3.10 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies

Comment thread docs/servers/prompts.mdx
Comment on lines +43 to +45
def generate_code_request(language: str, task_description: str) -> Message:
"""Generates a user message requesting code generation."""
content = f"Write a {language} function that performs the following task: {task_description}"
return PromptMessage(role="user", content=TextContent(type="text", text=content))
return Message(f"Write a {language} function that performs the following task: {task_description}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Example contradicts documented return types.

This example shows returning a single Message object with type annotation -> Message, but the documented return types (lines 193-197) only support str, list[Message | str], or PromptResult. Line 293 confirms single Message is not supported. Users following this example will encounter runtime errors.

🔎 Fix: Update example to return a supported type

Option 1: Return a string (simplest):

 @mcp.prompt
-def generate_code_request(language: str, task_description: str) -> Message:
+def generate_code_request(language: str, task_description: str) -> str:
     """Generates a user message requesting code generation."""
-    return Message(f"Write a {language} function that performs the following task: {task_description}")
+    return f"Write a {language} function that performs the following task: {task_description}"

Option 2: Return a list (shows Message usage):

 @mcp.prompt
-def generate_code_request(language: str, task_description: str) -> Message:
+def generate_code_request(language: str, task_description: str) -> list[Message]:
     """Generates a user message requesting code generation."""
-    return Message(f"Write a {language} function that performs the following task: {task_description}")
+    return [Message(f"Write a {language} function that performs the following task: {task_description}")]

Option 3: Return PromptResult (shows metadata capability):

+from fastmcp.prompts import PromptResult, Message
+
 @mcp.prompt
-def generate_code_request(language: str, task_description: str) -> Message:
+def generate_code_request(language: str, task_description: str) -> PromptResult:
     """Generates a user message requesting code generation."""
-    return Message(f"Write a {language} function that performs the following task: {task_description}")
+    return PromptResult(Message(f"Write a {language} function that performs the following task: {task_description}"))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def generate_code_request(language: str, task_description: str) -> Message:
"""Generates a user message requesting code generation."""
content = f"Write a {language} function that performs the following task: {task_description}"
return PromptMessage(role="user", content=TextContent(type="text", text=content))
return Message(f"Write a {language} function that performs the following task: {task_description}")
def generate_code_request(language: str, task_description: str) -> str:
"""Generates a user message requesting code generation."""
return f"Write a {language} function that performs the following task: {task_description}"

@jlowin jlowin merged commit 61ebc64 into main Dec 26, 2025
13 checks passed
@jlowin jlowin deleted the promptresult-cleanup branch December 26, 2025 03:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. feature Major new functionality. Reserved for 2-4 significant PRs per release. Not for issues.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant