diff --git a/README.md b/README.md index dded24a46..082c4dc22 100644 --- a/README.md +++ b/README.md @@ -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}" diff --git a/docs/servers/context.mdx b/docs/servers/context.mdx index 2818de3c0..0f075d264 100644 --- a/docs/servers/context.mdx +++ b/docs/servers/context.mdx @@ -71,7 +71,7 @@ async def get_user_profile(user_id: str, ctx: Context) -> dict: ```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 diff --git a/docs/servers/fastmcp.mdx b/docs/servers/fastmcp.mdx index 40808fb95..13279525d 100644 --- a/docs/servers/fastmcp.mdx +++ b/docs/servers/fastmcp.mdx @@ -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) diff --git a/docs/servers/prompts.mdx b/docs/servers/prompts.mdx index f1e292dd6..47f19ff9e 100644 --- a/docs/servers/prompts.mdx +++ b/docs/servers/prompts.mdx @@ -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?" ``` diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py index b8fd31def..5486b1c76 100644 --- a/src/fastmcp/server/server.py +++ b/src/fastmcp/server/server.py @@ -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) diff --git a/tests/client/test_client.py b/tests/client/test_client.py index d496bb8a5..e012d8336 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -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}!" diff --git a/tests/client/test_sse.py b/tests/client/test_sse.py index b0960eb14..df24d1d8e 100644 --- a/tests/client/test_sse.py +++ b/tests/client/test_sse.py @@ -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}!" diff --git a/tests/client/test_streamable_http.py b/tests/client/test_streamable_http.py index da8502c63..6d3294e5a 100644 --- a/tests/client/test_streamable_http.py +++ b/tests/client/test_streamable_http.py @@ -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}!" diff --git a/tests/deprecated/test_deprecated.py b/tests/deprecated/test_deprecated.py index fcb6fe2ed..a48e7c216 100644 --- a/tests/deprecated/test_deprecated.py +++ b/tests/deprecated/test_deprecated.py @@ -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" diff --git a/tests/deprecated/test_mount_separators.py b/tests/deprecated/test_mount_separators.py index 514de02cf..85ec63436 100644 --- a/tests/deprecated/test_mount_separators.py +++ b/tests/deprecated/test_mount_separators.py @@ -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" diff --git a/tests/server/http/test_http_dependencies.py b/tests/server/http/test_http_dependencies.py index 60ebbb1c6..81a52a90c 100644 --- a/tests/server/http/test_http_dependencies.py +++ b/tests/server/http/test_http_dependencies.py @@ -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() diff --git a/tests/server/test_import_server.py b/tests/server/test_import_server.py index c903cfa8a..581940561 100644 --- a/tests/server/test_import_server.py +++ b/tests/server/test_import_server.py @@ -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!" diff --git a/tests/server/test_mount.py b/tests/server/test_mount.py index e94f19b18..090666ccc 100644 --- a/tests/server/test_mount.py +++ b/tests/server/test_mount.py @@ -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}!" diff --git a/tests/server/test_proxy.py b/tests/server/test_proxy.py index 8ad2450d6..9ca60f0d8 100644 --- a/tests/server/test_proxy.py +++ b/tests/server/test_proxy.py @@ -61,7 +61,7 @@ def fastmcp_server(): # --- Prompts --- - @server.prompt() + @server.prompt def welcome(name: str) -> str: return f"Welcome to FastMCP, {name}!" diff --git a/tests/server/test_run_server.py b/tests/server/test_run_server.py index 8e631c3a4..65e3112ac 100644 --- a/tests/server/test_run_server.py +++ b/tests/server/test_run_server.py @@ -53,7 +53,7 @@ # # --- Prompts --- -# @server.prompt() +# @server.prompt # def welcome(name: str) -> str: # return f"Welcome to FastMCP, {name}!" diff --git a/tests/server/test_server.py b/tests/server/test_server.py index f7666b345..ab729846b 100644 --- a/tests/server/test_server.py +++ b/tests/server/test_server.py @@ -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!" diff --git a/tests/server/test_server_interactions.py b/tests/server/test_server_interactions.py index 19fe2c3f6..a15f9b5ff 100644 --- a/tests/server/test_server_interactions.py +++ b/tests/server/test_server_interactions.py @@ -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}" diff --git a/tests/test_servers/fastmcp_server.py b/tests/test_servers/fastmcp_server.py index 6e13d2ddc..1bc942295 100644 --- a/tests/test_servers/fastmcp_server.py +++ b/tests/test_servers/fastmcp_server.py @@ -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}!"