Prompts: templates a user picks
Support agents write the same kinds of reply all day. A prompt keeps a good starting point on the server, and a person picks it from a menu.
@mcp.prompt(title="Reply to a customer")
def reply_to_customer(ticket: str, tone: str = "friendly") -> list[Message]:
"""Draft a reply to a support ticket."""
return [
UserMessage(f"Write a {tone} reply to this support ticket:\n\n{ticket}"),
AssistantMessage("Hello, and thank you for getting in touch."),
]@mcp.prompt() registers a prompt. The imports gain Message, UserMessage and AssistantMessage from mcp.server.mcpserver.prompts.base. Returning a list of messages seeds a conversation; returning a plain string would give one user message.
The last message is from the assistant. Pre-filling the start of the reply steers how the model continues it, without the user typing any instructions.
import asyncio
from mcp import Client
from shop import mcp
async def main():
async with Client(mcp) as client:
listed = await client.list_prompts()
prompt = listed.prompts[0]
print(prompt.name, "/", prompt.title)
print([(argument.name, argument.required) for argument in prompt.arguments])
result = await client.get_prompt("reply_to_customer", {"ticket": "My mug arrived broken"})
for message in result.messages:
print(message.role, "->", message.content.text)
asyncio.run(main())Prompt arguments are a flat list of named strings, not a JSON Schema: they fill a form a person sees. tone has a default, so it is not required. get_prompt renders the messages, and the application adds them to the chat.
Three kinds of thing, one server
The shop server now offers all three: tools the model calls, resources the application loads, prompts the user picks. In Claude Code, a server's prompts appear as slash commands.
- Render the prompt with
"tone": "formal". - Call
get_promptwithoutticketand read the error. - Add a prompt
summarise_order(order_id: str) -> strthat returns one user message.
Every expert started right here.