Update tests for prompts

This commit is contained in:
Jeremiah Lowin 2025-06-04 16:37:41 -04:00
commit 099d340208
18 changed files with 52 additions and 49 deletions

View file

@ -173,10 +173,10 @@ Learn more in the [**Resources & Templates Documentation**](https://gofastmcp.co
### Prompts
Prompts define reusable message templates to guide LLM interactions. Decorate functions with `@mcp.prompt()`. Return strings or `Message` objects.
Prompts define reusable message templates to guide LLM interactions. Decorate functions with `@mcp.prompt`. Return strings or `Message` objects.
```python
@mcp.prompt()
@mcp.prompt
def summarize_request(text: str) -> str:
"""Generate a prompt asking for a summary."""
return f"Please summarize the following text:\n\n{text}"

View file

@ -71,7 +71,7 @@ async def get_user_profile(user_id: str, ctx: Context) -> dict:
<VersionBadge version="2.2.5" />
```python
@mcp.prompt()
@mcp.prompt
async def data_analysis_request(dataset: str, ctx: Context) -> str:
"""Generate a request to analyze data with contextual information."""
# Context is available as the ctx parameter

View file

@ -87,7 +87,7 @@ See [Resources & Templates](/servers/resources) for detailed documentation.
Prompts are reusable message templates for guiding the LLM.
```python
@mcp.prompt()
@mcp.prompt
def analyze_data(data_points: list[float]) -> str:
"""Creates a prompt asking for analysis of numerical data."""
formatted_data = ", ".join(str(point) for point in data_points)

View file

@ -33,13 +33,13 @@ from fastmcp.prompts.prompt import Message, PromptMessage, TextContent
mcp = FastMCP(name="PromptServer")
# Basic prompt returning a string (converted to user message automatically)
@mcp.prompt()
@mcp.prompt
def ask_about_topic(topic: str) -> str:
"""Generates a user message asking for an explanation of a topic."""
return f"Can you please explain the concept of '{topic}'?"
# Prompt returning a specific message type
@mcp.prompt()
@mcp.prompt
def generate_code_request(language: str, task_description: str) -> PromptMessage:
"""Generates a user message requesting code generation."""
content = f"Write a {language} function that performs the following task: {task_description}"
@ -69,7 +69,7 @@ FastMCP intelligently handles different return types from your prompt function:
```python
from fastmcp.prompts.prompt import Message
@mcp.prompt()
@mcp.prompt
def roleplay_scenario(character: str, situation: str) -> list[Message]:
"""Sets up a roleplaying scenario with initial messages."""
return [
@ -89,7 +89,7 @@ Type annotations are important for prompts. They:
from pydantic import Field
from typing import Literal, Optional
@mcp.prompt()
@mcp.prompt
def generate_content_request(
topic: str = Field(description="The main subject to cover"),
format: Literal["blog", "email", "social"] = "blog",
@ -111,7 +111,7 @@ def generate_content_request(
Parameters in your function signature are considered **required** unless they have a default value.
```python
@mcp.prompt()
@mcp.prompt
def data_analysis_prompt(
data_uri: str, # Required - no default value
analysis_type: str = "summary", # Optional - has default value
@ -154,13 +154,13 @@ FastMCP seamlessly supports both standard (`def`) and asynchronous (`async def`)
```python
# Synchronous prompt
@mcp.prompt()
@mcp.prompt
def simple_question(question: str) -> str:
"""Generates a simple question to ask the LLM."""
return f"Question: {question}"
# Asynchronous prompt
@mcp.prompt()
@mcp.prompt
async def data_based_prompt(data_id: str) -> str:
"""Generates a prompt based on data that needs to be fetched."""
# In a real scenario, you might fetch data from a database or API
@ -183,7 +183,7 @@ from fastmcp import FastMCP, Context
mcp = FastMCP(name="PromptServer")
@mcp.prompt()
@mcp.prompt
async def generate_report_request(report_type: str, ctx: Context) -> str:
"""Generates a request for a report."""
return f"Please create a {report_type} report. Request ID: {ctx.request_id}"
@ -207,12 +207,12 @@ mcp = FastMCP(
on_duplicate_prompts="error" # Raise an error if a prompt name is duplicated
)
@mcp.prompt()
@mcp.prompt
def greeting(): return "Hello, how can I help you today?"
# This registration attempt will raise a ValueError because
# "greeting" is already registered and the behavior is "error".
# @mcp.prompt()
# @mcp.prompt
# def greeting(): return "Hi there! What can I do for you?"
```

View file

@ -796,7 +796,7 @@ class FastMCP(Generic[LifespanResultT]):
This decorator supports multiple calling patterns:
- @server.prompt (without parentheses)
- @server.prompt() (with empty parentheses)
- @server.prompt (with empty parentheses)
- @server.prompt("custom_name") (with name as first argument)
- @server.prompt(name="custom_name") (with name as keyword argument)
- server.prompt(function, name="custom_name") (direct function call)
@ -818,7 +818,7 @@ class FastMCP(Generic[LifespanResultT]):
}
]
@server.prompt()
@server.prompt
def analyze_with_context(table_name: str, ctx: Context) -> list[Message]:
ctx.info(f"Analyzing table {table_name}")
schema = read_table_schema(table_name)

View file

@ -53,7 +53,7 @@ def fastmcp_server():
return {"id": user_id, "name": f"User {user_id}", "active": True}
# Add a prompt
@server.prompt()
@server.prompt
def welcome(name: str) -> str:
"""Example greeting prompt."""
return f"Welcome to FastMCP, {name}!"

View file

@ -55,7 +55,7 @@ def fastmcp_server():
return dict(request.headers)
# Add a prompt
@server.prompt()
@server.prompt
def welcome(name: str) -> str:
"""Example greeting prompt."""
return f"Welcome to FastMCP, {name}!"

View file

@ -55,7 +55,7 @@ def fastmcp_server():
return dict(request.headers)
# Add a prompt
@server.prompt()
@server.prompt
def welcome(name: str) -> str:
"""Example greeting prompt."""
return f"Welcome to FastMCP, {name}!"

View file

@ -133,7 +133,7 @@ def test_mount_prompt_separator_deprecation_warning():
main_app.mount("sub", sub_app, prompt_separator="-")
# Verify the separator is ignored and the default is used
@sub_app.prompt()
@sub_app.prompt
def test_prompt():
return "test"

View file

@ -53,7 +53,7 @@ def test_mount_prompt_separator_deprecation_warning():
main_app.mount("sub", sub_app, prompt_separator="-")
# Verify the separator is ignored and the default is used
@sub_app.prompt()
@sub_app.prompt
def test_prompt():
return "test"

View file

@ -28,7 +28,7 @@ def fastmcp_server():
return dict(request.headers)
# Add a prompt
@server.prompt()
@server.prompt
def get_headers_prompt() -> str:
"""Get the HTTP headers from the request."""
request = get_http_request()

View file

@ -130,7 +130,7 @@ async def test_import_with_prompts():
assistant_app = FastMCP("AssistantApp")
# Add a prompt to the assistant app
@assistant_app.prompt()
@assistant_app.prompt
def greeting(name: str) -> str:
return f"Hello, {name}!"
@ -174,11 +174,11 @@ async def test_import_multiple_prompts():
sql_app = FastMCP("SQLApp")
# Add prompts to each app
@python_app.prompt()
@python_app.prompt
def review_python(code: str) -> str:
return f"Reviewing Python code:\n{code}"
@sql_app.prompt()
@sql_app.prompt
def explain_sql(query: str) -> str:
return f"Explaining SQL query:\n{query}"
@ -316,7 +316,7 @@ async def test_import_with_proxy_prompts():
main_app = FastMCP("MainApp")
api_app = FastMCP("APIApp")
@api_app.prompt()
@api_app.prompt
def greeting(name: str) -> str:
"""Example greeting prompt."""
return f"Hello, {name} from API!"

View file

@ -194,7 +194,7 @@ class TestMultipleServerMount:
def working_resource():
return "Working resource"
@working_app.prompt()
@working_app.prompt
def working_prompt() -> str:
return "Working prompt"
@ -384,7 +384,7 @@ class TestPrompts:
main_app = FastMCP("MainApp")
assistant_app = FastMCP("AssistantApp")
@assistant_app.prompt()
@assistant_app.prompt
def greeting(name: str) -> str:
return f"Hello, {name}!"
@ -409,7 +409,7 @@ class TestPrompts:
main_app.mount("assistant", assistant_app)
# Add a prompt after mounting
@assistant_app.prompt()
@assistant_app.prompt
def farewell(name: str) -> str:
return f"Goodbye, {name}!"
@ -507,7 +507,7 @@ class TestProxyServer:
# Create original server
original_server = FastMCP("OriginalServer")
@original_server.prompt()
@original_server.prompt
def welcome(name: str) -> str:
return f"Welcome, {name}!"

View file

@ -61,7 +61,7 @@ def fastmcp_server():
# --- Prompts ---
@server.prompt()
@server.prompt
def welcome(name: str) -> str:
return f"Welcome to FastMCP, {name}!"

View file

@ -53,7 +53,7 @@
# # --- Prompts ---
# @server.prompt()
# @server.prompt
# def welcome(name: str) -> str:
# return f"Welcome to FastMCP, {name}!"

View file

@ -659,7 +659,7 @@ class TestPromptDecorator:
async def test_prompt_decorator(self):
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def fn() -> str:
return "Hello, world!"
@ -720,7 +720,7 @@ class TestPromptDecorator:
async def test_prompt_decorator_with_parameters(self):
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def test_prompt(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
@ -789,7 +789,7 @@ class TestPromptDecorator:
class MyClass:
@staticmethod
@mcp.prompt()
@mcp.prompt
def test_prompt() -> str:
return "Static Hello, world!"
@ -802,7 +802,7 @@ class TestPromptDecorator:
async def test_prompt_decorator_async_function(self):
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
async def test_prompt() -> str:
return "Async Hello, world!"

View file

@ -1091,7 +1091,7 @@ class TestPrompts:
"""Test that the prompt decorator registers prompts correctly."""
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def fn() -> str:
return "Hello, world!"
@ -1133,20 +1133,23 @@ class TestPrompts:
content = await prompt.render()
assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
def test_prompt_decorator_error(self):
"""Test error when decorator is used incorrectly."""
async def test_prompt_decorator_with_parens(self):
mcp = FastMCP()
with pytest.raises(TypeError, match="decorator was used incorrectly"):
@mcp.prompt # type: ignore
def fn() -> str:
return "Hello, world!"
@mcp.prompt()
def fn() -> str:
return "Hello, world!"
prompts_dict = await mcp.get_prompts()
assert len(prompts_dict) == 1
prompt = prompts_dict["fn"]
assert prompt.name == "fn"
async def test_list_prompts(self):
"""Test listing prompts through MCP protocol."""
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def fn(name: str, optional: str = "default") -> str:
return f"Hello, {name}! {optional}"
@ -1169,7 +1172,7 @@ class TestPrompts:
"""Test getting a prompt through MCP protocol."""
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def fn(name: str) -> str:
return f"Hello, {name}!"
@ -1185,7 +1188,7 @@ class TestPrompts:
"""Test getting a prompt that returns resource content."""
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def fn() -> PromptMessage:
return PromptMessage(
role="user",
@ -1220,7 +1223,7 @@ class TestPrompts:
"""Test error when required arguments are missing."""
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def prompt_fn(name: str) -> str:
return f"Hello, {name}!"
@ -1271,7 +1274,7 @@ class TestPromptContext:
async def test_prompt_context(self):
mcp = FastMCP()
@mcp.prompt()
@mcp.prompt
def prompt_fn(name: str, ctx: Context) -> str:
assert isinstance(ctx, Context)
return f"Hello, {name}! {ctx.request_id}"

View file

@ -53,6 +53,6 @@ async def get_user(user_id: str) -> dict[str, Any] | None:
# --- Prompts ---
@server.prompt()
@server.prompt
def welcome(name: str) -> str:
return f"Welcome to FastMCP, {name}!"