mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-22 05:24:18 +02:00
Update tests for prompts
This commit is contained in:
parent
e7a94eb8a6
commit
099d340208
18 changed files with 52 additions and 49 deletions
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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?"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}!"
|
||||
|
|
|
|||
|
|
@ -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}!"
|
||||
|
|
|
|||
|
|
@ -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}!"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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!"
|
||||
|
|
|
|||
|
|
@ -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}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ def fastmcp_server():
|
|||
|
||||
# --- Prompts ---
|
||||
|
||||
@server.prompt()
|
||||
@server.prompt
|
||||
def welcome(name: str) -> str:
|
||||
return f"Welcome to FastMCP, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
# # --- Prompts ---
|
||||
|
||||
# @server.prompt()
|
||||
# @server.prompt
|
||||
# def welcome(name: str) -> str:
|
||||
# return f"Welcome to FastMCP, {name}!"
|
||||
|
||||
|
|
|
|||
|
|
@ -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!"
|
||||
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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}!"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue