docs/servers/transforms/prompts-as-tools.mdx
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.0.0" />Some MCP clients only support tools. They cannot list or get prompts directly because they lack prompt protocol support. The PromptsAsTools transform bridges this gap by generating tools that provide access to your server's prompts.
When you add PromptsAsTools to a server, it creates two tools that clients can call instead of using the prompt protocol:
list_prompts returns JSON describing all available prompts and their argumentsget_prompt renders a specific prompt with provided argumentsThis means any client that can call tools can now access prompts, even if the client has no native prompt support.
Pass your FastMCP server to PromptsAsTools when adding the transform. The generated tools route through the server at runtime, which means all server middleware — auth, visibility, rate limiting — applies to prompt operations automatically, exactly as it would for direct prompts/get calls.
from fastmcp import FastMCP
from fastmcp.server.transforms import PromptsAsTools
mcp = FastMCP("My Server")
@mcp.prompt
def analyze_code(code: str, language: str = "python") -> str:
"""Analyze code for potential issues."""
return f"Analyze this {language} code:\n{code}"
@mcp.prompt
def explain_concept(concept: str) -> str:
"""Explain a programming concept."""
return f"Explain: {concept}"
# Add the transform - creates list_prompts and get_prompt tools
mcp.add_transform(PromptsAsTools(mcp))
Clients now see three items: whatever tools you defined directly, plus list_prompts and get_prompt.
The list_prompts tool returns JSON with metadata for each prompt, including its arguments.
result = await client.call_tool("list_prompts", {})
prompts = json.loads(result.data)
# [
# {
# "name": "analyze_code",
# "description": "Analyze code for potential issues.",
# "arguments": [
# {"name": "code", "description": null, "required": true},
# {"name": "language", "description": null, "required": false}
# ]
# },
# {
# "name": "explain_concept",
# "description": "Explain a programming concept.",
# "arguments": [
# {"name": "concept", "description": null, "required": true}
# ]
# }
#]
Each argument includes:
name: The argument namedescription: Optional description from type hints or docstringsrequired: Whether the argument must be providedThe get_prompt tool accepts a prompt name and optional arguments dict. It returns the rendered prompt as JSON with a messages array.
# Prompt with required and optional arguments
result = await client.call_tool(
"get_prompt",
{
"name": "analyze_code",
"arguments": {
"code": "x = 1\nprint(x)",
"language": "python"
}
}
)
response = json.loads(result.data)
# {
# "messages": [
# {
# "role": "user",
# "content": "Analyze this python code:\nx = 1\nprint(x)"
# }
# ]
# }
If a prompt has no arguments, you can omit the arguments field or pass an empty dict:
result = await client.call_tool(
"get_prompt",
{"name": "simple_prompt"}
)
Rendered prompts return a messages array following the standard MCP format. Each message includes:
role: The message role ("user" or "assistant")content: The message text contentMulti-message prompts are supported - the array will contain all messages in order.
Unlike resources, prompts always return text content. There is no binary encoding needed.