diff --git a/AGENTS.md b/AGENTS.md
index 19ec663df..93fc9b4b8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -11,7 +11,7 @@ FastMCP is a comprehensive Python framework (Python ≥3.10) for building Model
```bash
uv sync # Install dependencies
uv run prek run --all-files # Ruff + Prettier + ty
-uv run pytest # Run full test suite
+uv run pytest -n auto # Run full test suite
```
**All three must pass** - this is enforced by CI. Alternative: `just build && just typecheck && just test`
@@ -242,9 +242,9 @@ uv sync # Installs all deps including dev tools
### Testing
-- **Standard**: `uv run pytest`
-- **Integration**: `uv run pytest -m "integration"`
-- **Excluding markers**: `uv run pytest -m "not integration and not client_process"`
+- **Standard**: `uv run pytest -n auto`
+- **Integration**: `uv run pytest -n auto -m "integration"`
+- **Excluding markers**: `uv run pytest -n auto -m "not integration and not client_process"`
### CLI Usage
diff --git a/docs/servers/prompts.mdx b/docs/servers/prompts.mdx
index 710b35ce3..8822a820a 100644
--- a/docs/servers/prompts.mdx
+++ b/docs/servers/prompts.mdx
@@ -195,13 +195,14 @@ FastMCP intelligently handles different return types from your prompt function:
- **`str`**: Automatically converted to a single `PromptMessage`.
- **`PromptMessage`**: Used directly as provided. (Note a more user-friendly `Message` constructor is available that can accept raw strings instead of `TextContent` objects.)
- **`list[PromptMessage | str]`**: Used as a sequence of messages (a conversation).
+- **`PromptResult`**: Full control over messages, description, and metadata. See [PromptResult](#promptresult) below.
- **`Any`**: If the return type is not one of the above, the return value is attempted to be converted to a string and used as a `PromptMessage`.
```python
-from fastmcp.prompts.prompt import Message, PromptResult
+from fastmcp.prompts.prompt import Message
@mcp.prompt
-def roleplay_scenario(character: str, situation: str) -> PromptResult:
+def roleplay_scenario(character: str, situation: str) -> list[Message]:
"""Sets up a roleplaying scenario with initial messages."""
return [
Message(f"Let's roleplay. You are {character}. The situation is: {situation}"),
@@ -209,6 +210,43 @@ def roleplay_scenario(character: str, situation: str) -> PromptResult:
]
```
+#### PromptResult
+
+
+
+For complete control over prompt responses, return a `PromptResult` object. This lets you include metadata alongside your prompt messages, which is useful for passing runtime information to clients.
+
+```python
+from fastmcp import FastMCP
+from fastmcp.prompts import PromptResult, Message
+
+mcp = FastMCP(name="PromptServer")
+
+@mcp.prompt
+def code_review(code: str) -> PromptResult:
+ """Returns a code review prompt with metadata."""
+ return PromptResult(
+ messages=[
+ Message(f"Please review this code:\n\n```\n{code}\n```"),
+ ],
+ description="Code review prompt",
+ meta={"review_type": "security", "priority": "high"}
+ )
+```
+
+`PromptResult` accepts three fields:
+
+**`messages`** - A list of `PromptMessage` or `Message` objects representing the conversation to send to the LLM.
+
+**`description`** - Optional description of the prompt result. If not provided, defaults to the prompt's docstring.
+
+**`meta`** - Optional metadata dictionary that will be included in the MCP response's `_meta` field. Use this for runtime metadata like categorization, priority, or other client-specific data.
+
+
+The `meta` field in `PromptResult` is for runtime metadata specific to this render response. This is separate from the `meta` parameter in `@mcp.prompt(meta={...})`, which provides static metadata about the prompt definition itself (returned when listing prompts).
+
+
+You can still return plain `str`, `PromptMessage`, or lists from your prompt functions—`PromptResult` is opt-in for when you need to include metadata.
### Required vs. Optional Parameters
diff --git a/src/fastmcp/prompts/__init__.py b/src/fastmcp/prompts/__init__.py
index f230b8c64..440816289 100644
--- a/src/fastmcp/prompts/__init__.py
+++ b/src/fastmcp/prompts/__init__.py
@@ -1,4 +1,4 @@
-from .prompt import Prompt, PromptMessage, Message
+from .prompt import Message, Prompt, PromptResult, PromptMessage
from .prompt_manager import PromptManager
__all__ = [
@@ -6,4 +6,5 @@ __all__ = [
"Prompt",
"PromptManager",
"PromptMessage",
+ "PromptResult",
]
diff --git a/src/fastmcp/prompts/prompt.py b/src/fastmcp/prompts/prompt.py
index 1d3d309fd..76fb7e624 100644
--- a/src/fastmcp/prompts/prompt.py
+++ b/src/fastmcp/prompts/prompt.py
@@ -4,15 +4,18 @@ from __future__ import annotations as _annotations
import inspect
import json
+import warnings
from collections.abc import Awaitable, Callable, Sequence
from typing import Annotated, Any
import pydantic_core
+from mcp import GetPromptResult
from mcp.types import ContentBlock, Icon, PromptMessage, Role, TextContent
from mcp.types import Prompt as SDKPrompt
from mcp.types import PromptArgument as SDKPromptArgument
from pydantic import Field, TypeAdapter
+from fastmcp import settings
from fastmcp.exceptions import PromptError
from fastmcp.server.dependencies import get_context, without_injected_parameters
from fastmcp.server.tasks.config import TaskConfig
@@ -40,13 +43,14 @@ def Message(
message_validator = TypeAdapter[PromptMessage](PromptMessage)
-SyncPromptResult = (
+# Type aliases for what prompt functions can return (before conversion to PromptResult)
+_SyncPromptFnReturn = (
str
| PromptMessage
| dict[str, Any]
| Sequence[str | PromptMessage | dict[str, Any]]
)
-PromptResult = SyncPromptResult | Awaitable[SyncPromptResult]
+_PromptFnReturn = _SyncPromptFnReturn | Awaitable[_SyncPromptFnReturn]
class PromptArgument(FastMCPBaseModel):
@@ -61,6 +65,51 @@ class PromptArgument(FastMCPBaseModel):
)
+class PromptResult(FastMCPBaseModel):
+ """Canonical result type for prompt rendering.
+
+ This is the internal type that all prompt renders return. It wraps the
+ messages with optional description and metadata.
+ """
+
+ messages: list[PromptMessage] = Field(description="The prompt messages to return")
+ description: str | None = Field(
+ default=None, description="Optional description of the prompt result"
+ )
+ meta: dict[str, Any] | None = Field(
+ default=None, description="Optional metadata about the prompt result"
+ )
+
+ @classmethod
+ def from_value(
+ cls,
+ value: list[PromptMessage] | PromptResult,
+ description: str | None = None,
+ meta: dict[str, Any] | None = None,
+ ) -> PromptResult:
+ """Convert various types to PromptResult."""
+ if isinstance(value, PromptResult):
+ # Merge meta if provided
+ if meta and value.meta:
+ merged_meta = {**value.meta, **meta}
+ else:
+ merged_meta = meta or value.meta
+ return cls(
+ messages=value.messages,
+ description=description or value.description,
+ meta=merged_meta,
+ )
+ return cls(messages=value, description=description, meta=meta)
+
+ def to_mcp_prompt_result(self) -> GetPromptResult:
+ """Convert to MCP GetPromptResult."""
+ return GetPromptResult(
+ description=self.description,
+ messages=self.messages,
+ _meta=self.meta,
+ )
+
+
class Prompt(FastMCPComponent):
"""A prompt template that can be rendered with parameters."""
@@ -113,7 +162,7 @@ class Prompt(FastMCPComponent):
@staticmethod
def from_function(
- fn: Callable[..., PromptResult | Awaitable[PromptResult]],
+ fn: Callable[..., _PromptFnReturn | Awaitable[_PromptFnReturn]],
name: str | None = None,
title: str | None = None,
description: str | None = None,
@@ -146,19 +195,45 @@ class Prompt(FastMCPComponent):
async def render(
self,
arguments: dict[str, Any] | None = None,
- ) -> list[PromptMessage]:
+ ) -> list[PromptMessage] | PromptResult:
"""Render the prompt with arguments.
This method is not implemented in the base Prompt class and must be
- implemented by subclasses.
+ implemented by subclasses. The preferred return type is PromptResult,
+ but list[PromptMessage] is still supported for backwards compatibility.
"""
raise NotImplementedError("Subclasses must implement render()")
+ async def _render(
+ self,
+ arguments: dict[str, Any] | None = None,
+ ) -> PromptResult:
+ """Internal API that always returns PromptResult.
+
+ Calls render() and wraps list[PromptMessage] in PromptResult.
+ This is what PromptManager calls internally.
+ """
+ result = await self.render(arguments)
+ if isinstance(result, PromptResult):
+ return result
+ # Deprecated in 2.14.1: returning list[PromptMessage] from render()
+ if settings.deprecation_warnings:
+ warnings.warn(
+ f"Prompt.render() returning list[PromptMessage] is deprecated (since 2.14.1). "
+ f"Return PromptResult instead. "
+ f"(Prompt: {self.__class__.__name__}, Name: {self.name})",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return PromptResult.from_value(
+ result, description=self.description, meta=self.meta
+ )
+
class FunctionPrompt(Prompt):
"""A prompt that is a function."""
- fn: Callable[..., PromptResult | Awaitable[PromptResult]]
+ fn: Callable[..., _PromptFnReturn | Awaitable[_PromptFnReturn]]
task_config: Annotated[
TaskConfig,
Field(description="Background task execution configuration (SEP-1686)."),
@@ -167,7 +242,7 @@ class FunctionPrompt(Prompt):
@classmethod
def from_function(
cls,
- fn: Callable[..., PromptResult | Awaitable[PromptResult]],
+ fn: Callable[..., _PromptFnReturn | Awaitable[_PromptFnReturn]],
name: str | None = None,
title: str | None = None,
description: str | None = None,
@@ -322,7 +397,7 @@ class FunctionPrompt(Prompt):
async def render(
self,
arguments: dict[str, Any] | None = None,
- ) -> list[PromptMessage]:
+ ) -> PromptResult:
"""Render the prompt with arguments."""
# Validate required arguments
if self.arguments:
@@ -375,7 +450,11 @@ class FunctionPrompt(Prompt):
"Could not convert prompt result to message."
) from e
- return messages
+ return PromptResult(
+ messages=messages,
+ description=self.description,
+ meta=self.meta,
+ )
except Exception as e:
logger.exception(f"Error rendering prompt {self.name}")
raise PromptError(f"Error rendering prompt {self.name}.") from e
diff --git a/src/fastmcp/prompts/prompt_manager.py b/src/fastmcp/prompts/prompt_manager.py
index 1563a1831..971eb5264 100644
--- a/src/fastmcp/prompts/prompt_manager.py
+++ b/src/fastmcp/prompts/prompt_manager.py
@@ -4,11 +4,14 @@ import warnings
from collections.abc import Awaitable, Callable
from typing import Any
-from mcp import GetPromptResult
-
from fastmcp import settings
from fastmcp.exceptions import NotFoundError, PromptError
-from fastmcp.prompts.prompt import FunctionPrompt, Prompt, PromptResult
+from fastmcp.prompts.prompt import (
+ FunctionPrompt,
+ Prompt,
+ PromptResult,
+ _PromptFnReturn,
+)
from fastmcp.settings import DuplicateBehavior
from fastmcp.utilities.logging import get_logger
@@ -24,7 +27,11 @@ class PromptManager:
mask_error_details: bool | None = None,
):
self._prompts: dict[str, Prompt] = {}
- self.mask_error_details = mask_error_details or settings.mask_error_details
+ self.mask_error_details = (
+ settings.mask_error_details
+ if mask_error_details is None
+ else mask_error_details
+ )
# Default to "warn" if None is provided
if duplicate_behavior is None:
@@ -58,7 +65,7 @@ class PromptManager:
def add_prompt_from_fn(
self,
- fn: Callable[..., PromptResult | Awaitable[PromptResult]],
+ fn: Callable[..., _PromptFnReturn | Awaitable[_PromptFnReturn]],
name: str | None = None,
description: str | None = None,
tags: set[str] | None = None,
@@ -98,18 +105,17 @@ class PromptManager:
self,
name: str,
arguments: dict[str, Any] | None = None,
- ) -> GetPromptResult:
+ ) -> PromptResult:
"""
Internal API for servers: Finds and renders a prompt, respecting the
filtered protocol path.
"""
prompt = await self.get_prompt(name)
try:
- messages = await prompt.render(arguments)
- return GetPromptResult(description=prompt.description, messages=messages)
- except PromptError as e:
+ return await prompt._render(arguments)
+ except PromptError:
logger.exception(f"Error rendering prompt {name!r}")
- raise e
+ raise
except Exception as e:
logger.exception(f"Error rendering prompt {name!r}")
if self.mask_error_details:
diff --git a/src/fastmcp/server/middleware/caching.py b/src/fastmcp/server/middleware/caching.py
index 1f6ebdb59..14b0c6c55 100644
--- a/src/fastmcp/server/middleware/caching.py
+++ b/src/fastmcp/server/middleware/caching.py
@@ -17,7 +17,7 @@ from key_value.aio.wrappers.statistics.wrapper import (
from pydantic import BaseModel, Field
from typing_extensions import NotRequired, Self, override
-from fastmcp.prompts.prompt import Prompt
+from fastmcp.prompts.prompt import Prompt, PromptResult
from fastmcp.resources.resource import Resource, ResourceContent
from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
from fastmcp.tools.tool import Tool, ToolResult
@@ -220,12 +220,10 @@ class ResponseCachingMiddleware(Middleware):
default_collection="resources/read",
)
- self._get_prompt_cache: PydanticAdapter[mcp.types.GetPromptResult] = (
- PydanticAdapter(
- key_value=self._stats,
- pydantic_model=mcp.types.GetPromptResult,
- default_collection="prompts/get",
- )
+ self._get_prompt_cache: PydanticAdapter[PromptResult] = PydanticAdapter(
+ key_value=self._stats,
+ pydantic_model=PromptResult,
+ default_collection="prompts/get",
)
self._call_tool_cache: PydanticAdapter[CachableToolResult] = PydanticAdapter(
@@ -419,10 +417,8 @@ class ResponseCachingMiddleware(Middleware):
async def on_get_prompt(
self,
context: MiddlewareContext[mcp.types.GetPromptRequestParams],
- call_next: CallNext[
- mcp.types.GetPromptRequestParams, mcp.types.GetPromptResult
- ],
- ) -> mcp.types.GetPromptResult:
+ call_next: CallNext[mcp.types.GetPromptRequestParams, PromptResult],
+ ) -> PromptResult:
"""Get a prompt from the cache, if caching is enabled, and the result is in the cache. Otherwise,
otherwise call the next middleware and store the result in the cache if caching is enabled."""
if self._get_prompt_settings.get("enabled") is False:
@@ -433,7 +429,7 @@ class ResponseCachingMiddleware(Middleware):
if cached_value := await self._get_prompt_cache.get(key=cache_key):
return cached_value
- value: mcp.types.GetPromptResult = await call_next(context=context)
+ value: PromptResult = await call_next(context=context)
await self._get_prompt_cache.put(
key=cache_key,
diff --git a/src/fastmcp/server/middleware/middleware.py b/src/fastmcp/server/middleware/middleware.py
index e0a06642d..ca3d875cc 100644
--- a/src/fastmcp/server/middleware/middleware.py
+++ b/src/fastmcp/server/middleware/middleware.py
@@ -17,7 +17,7 @@ from typing import (
import mcp.types as mt
from typing_extensions import TypeVar
-from fastmcp.prompts.prompt import Prompt
+from fastmcp.prompts.prompt import Prompt, PromptResult
from fastmcp.resources.resource import Resource, ResourceContent
from fastmcp.resources.template import ResourceTemplate
from fastmcp.tools.tool import Tool, ToolResult
@@ -170,8 +170,8 @@ class Middleware:
async def on_get_prompt(
self,
context: MiddlewareContext[mt.GetPromptRequestParams],
- call_next: CallNext[mt.GetPromptRequestParams, mt.GetPromptResult],
- ) -> mt.GetPromptResult:
+ call_next: CallNext[mt.GetPromptRequestParams, PromptResult],
+ ) -> PromptResult:
return await call_next(context)
async def on_list_tools(
diff --git a/src/fastmcp/server/proxy.py b/src/fastmcp/server/proxy.py
index f9a4f5e67..cfe70a6fe 100644
--- a/src/fastmcp/server/proxy.py
+++ b/src/fastmcp/server/proxy.py
@@ -16,7 +16,6 @@ from mcp.types import (
METHOD_NOT_FOUND,
BlobResourceContents,
ElicitRequestFormParams,
- GetPromptResult,
TextResourceContents,
)
from pydantic.networks import AnyUrl
@@ -28,7 +27,7 @@ from fastmcp.client.roots import RootsList
from fastmcp.client.transports import ClientTransportT
from fastmcp.exceptions import NotFoundError, ResourceError, ToolError
from fastmcp.mcp_config import MCPConfig
-from fastmcp.prompts import Prompt, PromptMessage
+from fastmcp.prompts import Prompt, PromptResult
from fastmcp.prompts.prompt import PromptArgument
from fastmcp.prompts.prompt_manager import PromptManager
from fastmcp.resources import Resource, ResourceTemplate
@@ -257,7 +256,7 @@ class ProxyPromptManager(PromptManager, ProxyManagerMixin):
self,
name: str,
arguments: dict[str, Any] | None = None,
- ) -> GetPromptResult:
+ ) -> PromptResult:
"""Renders a prompt, trying local/mounted first, then proxy if not found."""
try:
# First try local and mounted prompts
@@ -267,7 +266,12 @@ class ProxyPromptManager(PromptManager, ProxyManagerMixin):
client = await self._get_client()
async with client:
result = await client.get_prompt(name, arguments)
- return result
+ # Convert MCP GetPromptResult to PromptResult
+ return PromptResult(
+ messages=result.messages,
+ description=result.description,
+ meta=result.meta,
+ )
class ProxyTool(Tool, MirroredComponent):
@@ -524,11 +528,17 @@ class ProxyPrompt(Prompt, MirroredComponent):
_mirrored=True,
)
- async def render(self, arguments: dict[str, Any]) -> list[PromptMessage]: # type: ignore[override]
+ async def render(self, arguments: dict[str, Any]) -> PromptResult: # type: ignore[override]
"""Render the prompt by making a call through the client."""
async with self._client:
result = await self._client.get_prompt(self.name, arguments)
- return result.messages
+ # Convert GetPromptResult to PromptResult, preserving runtime meta from the result
+ # (not the static prompt meta which includes fastmcp tags)
+ return PromptResult(
+ messages=result.messages,
+ description=result.description,
+ meta=result.meta,
+ )
class FastMCPProxy(FastMCP):
diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py
index 2e89cab12..bc8eee40d 100644
--- a/src/fastmcp/server/server.py
+++ b/src/fastmcp/server/server.py
@@ -60,7 +60,7 @@ import fastmcp.server
from fastmcp.exceptions import DisabledError, NotFoundError
from fastmcp.mcp_config import MCPConfig
from fastmcp.prompts import Prompt
-from fastmcp.prompts.prompt import FunctionPrompt
+from fastmcp.prompts.prompt import FunctionPrompt, PromptResult
from fastmcp.prompts.prompt_manager import PromptManager
from fastmcp.resources.resource import FunctionResource, Resource, ResourceContent
from fastmcp.resources.resource_manager import ResourceManager
@@ -1781,8 +1781,18 @@ class FastMCP(Generic[LifespanResultT]):
) -> GetPromptResult:
"""
Applies this server's middleware and delegates the filtered call to the manager.
+ Converts PromptResult to GetPromptResult for MCP protocol.
"""
+ result = await self._get_prompt_content_middleware(name, arguments)
+ return result.to_mcp_prompt_result()
+ async def _get_prompt_content_middleware(
+ self, name: str, arguments: dict[str, Any] | None = None
+ ) -> PromptResult:
+ """
+ Applies this server's middleware and returns PromptResult.
+ Used internally and by parent servers for mounted prompts.
+ """
mw_context = MiddlewareContext(
message=mcp.types.GetPromptRequestParams(name=name, arguments=arguments),
source="client",
@@ -1797,7 +1807,7 @@ class FastMCP(Generic[LifespanResultT]):
async def _get_prompt(
self,
context: MiddlewareContext[mcp.types.GetPromptRequestParams],
- ) -> GetPromptResult:
+ ) -> PromptResult:
name = context.message.name
# Try mounted servers in reverse order (later wins)
@@ -1816,7 +1826,7 @@ class FastMCP(Generic[LifespanResultT]):
if not self._should_enable_component(prompt):
# Parent filter blocks this prompt, continue searching
continue
- return await mounted.server._get_prompt_middleware(
+ return await mounted.server._get_prompt_content_middleware(
try_name, context.message.arguments
)
except NotFoundError:
diff --git a/tests/prompts/test_prompt.py b/tests/prompts/test_prompt.py
index 76132b902..0bd3e6632 100644
--- a/tests/prompts/test_prompt.py
+++ b/tests/prompts/test_prompt.py
@@ -16,7 +16,8 @@ class TestRenderPrompt:
return "Hello, world!"
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Hello, world!")
)
@@ -27,7 +28,8 @@ class TestRenderPrompt:
return "Hello, world!"
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Hello, world!")
)
@@ -38,7 +40,8 @@ class TestRenderPrompt:
return f"Hello, {name}! You're {age} years old."
prompt = Prompt.from_function(fn)
- assert await prompt.render(arguments=dict(name="World")) == [
+ result = await prompt.render(arguments=dict(name="World"))
+ assert result.messages == [
PromptMessage(
role="user",
content=TextContent(
@@ -53,7 +56,8 @@ class TestRenderPrompt:
return f"Hello, {name}!"
prompt = Prompt.from_function(MyPrompt())
- assert await prompt.render(arguments=dict(name="World")) == [
+ result = await prompt.render(arguments=dict(name="World"))
+ assert result.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Hello, World!")
)
@@ -65,7 +69,8 @@ class TestRenderPrompt:
return f"Hello, {name}!"
prompt = Prompt.from_function(MyPrompt())
- assert await prompt.render(arguments=dict(name="World")) == [
+ result = await prompt.render(arguments=dict(name="World"))
+ assert result.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Hello, World!")
)
@@ -86,7 +91,8 @@ class TestRenderPrompt:
)
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Hello, world!")
)
@@ -99,7 +105,8 @@ class TestRenderPrompt:
)
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="assistant", content=TextContent(type="text", text="Hello, world!")
)
@@ -119,7 +126,8 @@ class TestRenderPrompt:
return expected
prompt = Prompt.from_function(fn)
- assert await prompt.render() == expected
+ result = await prompt.render()
+ assert result.messages == expected
async def test_fn_returns_list_of_strings(self):
expected = [
@@ -131,7 +139,8 @@ class TestRenderPrompt:
return expected
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(role="user", content=TextContent(type="text", text=t))
for t in expected
]
@@ -153,7 +162,8 @@ class TestRenderPrompt:
)
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="user",
content=EmbeddedResource(
@@ -188,7 +198,8 @@ class TestRenderPrompt:
]
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="user",
content=TextContent(type="text", text="Please analyze this file:"),
@@ -227,7 +238,8 @@ class TestRenderPrompt:
)
prompt = Prompt.from_function(fn)
- assert await prompt.render() == [
+ result = await prompt.render()
+ assert result.messages == [
PromptMessage(
role="user",
content=EmbeddedResource(
@@ -258,7 +270,7 @@ class TestPromptTypeConversion:
result_from_string = await prompt.render(
arguments={"numbers": "[1, 2, 3, 4, 5]"}
)
- assert result_from_string == [
+ assert result_from_string.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="The sum is: 15")
)
@@ -268,7 +280,7 @@ class TestPromptTypeConversion:
result_from_list_string = await prompt.render(
arguments={"numbers": "[1, 2, 3, 4, 5]"}
)
- assert result_from_list_string == result_from_string
+ assert result_from_list_string.messages == result_from_string.messages
async def test_various_type_conversions(self):
"""Test type conversion for various data types."""
@@ -298,7 +310,7 @@ class TestPromptTypeConversion:
expected_text = (
"Alice (25): 3 scores, active=True, metadata keys=['project', 'version']"
)
- assert result == [
+ assert result.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text=expected_text)
)
@@ -329,7 +341,7 @@ class TestPromptTypeConversion:
# This should work with JSON parsing (integer as string)
result1 = await prompt.render(arguments={"value": "42"})
- assert result1 == [
+ assert result1.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Value: 42")
)
@@ -337,7 +349,7 @@ class TestPromptTypeConversion:
# This should work with direct validation (already an integer string)
result2 = await prompt.render(arguments={"value": "123"})
- assert result2 == [
+ assert result2.messages == [
PromptMessage(
role="user", content=TextContent(type="text", text="Value: 123")
)
@@ -358,7 +370,7 @@ class TestPromptTypeConversion:
}
)
- assert result == [
+ assert result.messages == [
PromptMessage(
role="user",
content=TextContent(type="text", text="Hello world (repeated 3 times)"),
diff --git a/tests/prompts/test_prompt_manager.py b/tests/prompts/test_prompt_manager.py
index 076c27279..b9e666926 100644
--- a/tests/prompts/test_prompt_manager.py
+++ b/tests/prompts/test_prompt_manager.py
@@ -400,10 +400,10 @@ class TestContextHandling:
context = Context(fastmcp=mcp)
async with context:
- messages = await prompt.render(arguments={"x": 42})
+ result = await prompt.render(arguments={"x": 42})
- assert len(messages) == 1
- assert messages[0].content.text == "42" # type: ignore[attr-defined]
+ assert len(result.messages) == 1
+ assert result.messages[0].content.text == "42" # type: ignore[attr-defined]
async def test_context_optional(self):
"""Test that context is optional when rendering prompts."""
@@ -420,12 +420,12 @@ class TestContextHandling:
context = Context(fastmcp=mcp)
async with context:
- messages = await prompt.render(
+ result = await prompt.render(
arguments={"x": 42},
)
- assert len(messages) == 1
- assert messages[0].content.text == "42" # type: ignore[attr-defined]
+ assert len(result.messages) == 1
+ assert result.messages[0].content.text == "42" # type: ignore[attr-defined]
async def test_annotated_context_parameter_detection(self):
"""Test that annotated context parameters are properly detected in
@@ -460,5 +460,5 @@ class TestContextHandling:
context = Context(fastmcp=mcp)
async with context:
- messages = await prompt.render(arguments={"topic": "cats"})
- assert messages[0].content.text == "Write about cats" # type: ignore[attr-defined]
+ result = await prompt.render(arguments={"topic": "cats"})
+ assert result.messages[0].content.text == "Write about cats" # type: ignore[attr-defined]
diff --git a/tests/server/test_server.py b/tests/server/test_server.py
index dfe7a645b..16e3a33e5 100644
--- a/tests/server/test_server.py
+++ b/tests/server/test_server.py
@@ -848,7 +848,7 @@ class TestPromptDecorator:
assert prompt.name == "fn"
# Don't compare functions directly since validate_call wraps them
content = await prompt.render()
- assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
+ assert content.messages[0].content.text == "Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_without_parentheses(self):
mcp = FastMCP()
@@ -880,7 +880,7 @@ class TestPromptDecorator:
prompt = prompts_dict["custom_name"]
assert prompt.name == "custom_name"
content = await prompt.render()
- assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
+ assert content.messages[0].content.text == "Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_with_description(self):
mcp = FastMCP()
@@ -894,7 +894,7 @@ class TestPromptDecorator:
prompt = prompts_dict["fn"]
assert prompt.description == "A custom description"
content = await prompt.render()
- assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
+ assert content.messages[0].content.text == "Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_with_parameters(self):
mcp = FastMCP()
diff --git a/tests/server/test_server_interactions.py b/tests/server/test_server_interactions.py
index 8eb2d2769..e272b1e74 100644
--- a/tests/server/test_server_interactions.py
+++ b/tests/server/test_server_interactions.py
@@ -2194,7 +2194,7 @@ class TestPrompts:
assert prompt.name == "fn"
# Don't compare functions directly since validate_call wraps them
content = await prompt.render()
- assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
+ assert content.messages[0].content.text == "Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_with_name(self):
"""Test prompt decorator with custom name."""
@@ -2209,7 +2209,7 @@ class TestPrompts:
prompt = prompts_dict["custom_name"]
assert prompt.name == "custom_name"
content = await prompt.render()
- assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
+ assert content.messages[0].content.text == "Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_with_description(self):
"""Test prompt decorator with custom description."""
@@ -2224,7 +2224,7 @@ class TestPrompts:
prompt = prompts_dict["fn"]
assert prompt.description == "A custom description"
content = await prompt.render()
- assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
+ assert content.messages[0].content.text == "Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_with_parens(self):
mcp = FastMCP()