Prompts in the client
Your server exposes a format prompt. The inspector shows it interpolating correctly. Now the client needs two more methods — list_prompts and get_prompt — so your CLI chatbot can discover available prompts and invoke them with arguments. At that point you can wire up a slash-command UI where a user types /format and gets the expert prompt with zero typing.
Adding list_prompts
Open mcp_client.py and add:
from mcp import types
async def list_prompts(self) -> list[types.Prompt]:
result = await self.session().list_prompts()
return result.prompts
Exactly the same shape as list_tools:
- Call the session's built-in
list_prompts. - Unwrap
result.promptsand return.
Each entry in the returned list is a types.Prompt object carrying a name, a description, and the arguments the prompt expects. Your application uses this information to populate the slash-command UI — each prompt becomes a selectable command, and the argument definitions drive the follow-up prompts your CLI asks the user to fill in.
Adding get_prompt
async def get_prompt(self, prompt_name: str, args: dict[str, str]):
result = await self.session().get_prompt(prompt_name, args)
return result.messages
Same simplicity, slightly more to pay attention to:
prompt_name— the name of the prompt the user selected.args— a dict of argument values the user supplied, keyed by the parameter names the prompt declared.result.messages— the fully interpolated list of messages. These are ready to be appended to your conversation history and sent straight to Claude.
The SDK handles all the interpolation. Your client passes the raw dict, the server calls the prompt function with those arguments as keyword arguments, and the rendered messages come back.
How prompt arguments flow
This is the round trip in full:
-
Server side — the prompt function declares its parameters:
@mcp.prompt(name="format", description="...") def format_document(doc_id: str = Field(description="Id of the document to format")): ... -
Client discovery —
list_prompts()returns atypes.Promptwith the name"format"and an argument list containingdoc_id. -
Client invocation — the application calls
get_prompt("format", {"doc_id": "report.pdf"}). -
Server-side dispatch — the SDK routes the call to
format_document(doc_id="report.pdf"). The parameter name in the dict becomes the keyword argument name in the function. -
Client response — the fully interpolated messages come back as
result.messages.
The parameter names have to match. If your dict has document_id but the server function parameter is doc_id, the call fails. Use the argument definitions returned from list_prompts to build your UI — that way you're guaranteed to use the right names because they come from the same source of truth.
A slash-command UI in the CLI
Here's the interaction shape the CLI chatbot supports once prompts are wired up:
- User types
/at the prompt. - The CLI calls
client.list_prompts()and displays the results as a picker —/format — Rewrites a document in Markdown,/summarise — Writes a one-paragraph summary, and so on. - User selects
/format. - The CLI inspects the
formatprompt's argument list, asks the user for each required argument (doc_id?), and collects their answers. - The CLI calls
client.get_prompt("format", {"doc_id": "report.pdf"})and receives the interpolated messages. - The CLI appends those messages to the conversation history and sends the whole thing to Claude.
- Claude runs with the expert prompt as if the user had typed it themselves — including any tool calls the prompt directs it to make (like using
edit_documentto apply the formatting). - The user sees the final result.
The user typed /format report.pdf — nine characters — and invoked an expert-authored, multi-paragraph, carefully tuned prompt that references the server's own tools. That's the user-facing value of prompts. You get the quality of a hand-crafted prompt with the ergonomics of a slash command.
Multi-turn starter conversations
The return value of a prompt is a list of messages, not a single string. That means a prompt can include more than one message — for example, a user message with instructions followed by an assistant message with a worked-example answer to prime the model.
This is where multi-shot prompting (Module 5) meets MCP prompts. If the expert version of your task benefits from a worked example, include it as an AssistantMessage after the UserMessage in the returned list:
return [
base.UserMessage(instructions),
base.AssistantMessage(example_output),
base.UserMessage(task_request),
]
The client sends all three messages as part of the conversation, and Claude continues from there. The MCP prompt has effectively given Claude a few-shot primer before the real work begins. Powerful technique for anything format-heavy or domain-specific where a single worked example makes a dramatic difference.
Best practices, again
- Keep prompts relevant. Don't ship prompts for tasks outside your server's domain — they clutter the UI and dilute the server's value proposition.
- Test thoroughly before deployment. A broken prompt is worse than no prompt because the user trusted you to have tested it.
- Use clear, specific instructions inside the prompt — every Module 5 technique applies.
- Design prompts to leverage your server's tools. A prompt that tells Claude "use
edit_documentto apply changes, then re-read to verify" is exactly the kind of orchestration a user couldn't easily figure out on their own. - Think about which arguments users actually need to supply. Keep the argument list short — every argument is friction in the slash-command UI.
Everything together now
At this point your client has four methods:
list_tools()— discover callable toolscall_tool(name, input)— invoke a tool on Claude's behalfread_resource(uri)— fetch data directly from the serverlist_prompts()+get_prompt(name, args)— surface expert prompts as commands
That's the full MCP client surface for the three primitives. Combined with a custom MCP server exposing all three, you have a working end-to-end MCP system. In the next lesson you'll step back and review the whole architecture before taking the module quiz.
Key Takeaways
- 1Add list_prompts() and get_prompt(name, args) to your MCPClient wrapper — list_prompts discovers available prompts, get_prompt interpolates arguments and returns ready-to-send messages.
- 2Prompt arguments flow as a dict from the client into keyword arguments on the server-side prompt function; the parameter names must match, so always drive the UI from list_prompts output.
- 3Wire prompts into a slash-command UI: user types /, picks a prompt, fills in arguments, and the client sends the interpolated messages directly to Claude.
- 4The return value of a prompt is a list of messages — take advantage of that by including assistant messages for few-shot priming when the task benefits from it.
- 5MCP prompts give users the quality of an expert-authored prompt with the ergonomics of a slash command, and they can reference the server's own tools for end-to-end orchestration.