Module 5: Prompts — Reusable Templates
What an MCP prompt is
Description
Module 1 already previewed the definition in one sentence: a prompt is "a reusable template, user-controlled." This lesson stops on every word of that sentence, the same way Module 4 did with the definition of resource. "Reusable template" rules out the confusion of thinking a prompt is fixed data like a resource. "User-controlled" rules out the most common confusion of all: thinking a prompt is just a tool with a different name, because in both cases "something happens when you invoke it."
By the end of this lesson you'll be able to distinguish a prompt from a tool and from a resource using a single criterion — who decides to activate it — without needing to see a single JSON-RPC message yet.
Connection to the module
This lesson is purely conceptual, the same way lesson 02 of Module 4 was for resources — it doesn't execute anything yet. It's the ground lessons 03 (prompts/list) and 04 (prompts/get) stand on: if the definition is clear here, those two lessons are just "what the JSON for this concept looks like."
The definition, word by word
An MCP prompt is a named message template, optionally parameterized by arguments, that a server exposes for the user to activate explicitly — unlike a tool (which the model decides to invoke) or a resource (which the application decides to show).
"Named message template"
A prompt has a name (like "plan_booking") that identifies it, just as a tool has its name and a resource its uri. But what sits behind that name is different: it's not a function that produces an effect (like a tool) nor a document that already exists, ready to be read (like a resource) — it's a message recipe, a text (or a sequence of turns) the server builds at the moment it's requested, typically combining a fixed template with the concrete values of its arguments.
"Optionally parameterized by arguments"
A prompt can declare zero or more arguments, each with name, description, and required (True/False). In Reservo, plan_booking declares a single argument, room, and it's optional (required: False) — the prompt remains valid and useful even if no one specifies a room. Compare it to a tool's inputSchema (Module 3): there, every property carries a full type, possibly an enum, and can be validated with type precision. A prompt's arguments are deliberately simpler — there's no type or enum in the PromptArgument specification, because its purpose isn't to validate a function call: it's to identify which blank in the template can be filled in.
"User-controlled"
This is the word that sets a prompt apart from everything else. The MCP specification categorizes the three primitives by who decides to use them — the same table you already saw, partially, in Module 4:
tools -> model-controlled the MODEL decides to invoke it, based on the conversation
resources -> application-driven the APPLICATION decides what to show as available context
prompts -> user-controlled the USER decides to activate it explicitly
"User-controlled" means activating a prompt is a decision made by the person on the other side of the application — not the model reasoning about the conversation, not the application deciding on its own logic — typically choosing it from a catalog the application shows them (prompts/list, lesson 03 of this module). The model doesn't "decide" to use plan_booking the same way it decides to call get_quote; by the time the prompt is activated, the model hasn't even taken part in that decision yet — the user already made it, and what the model receives is the result: an already-assembled message, asking it to do something specific.
Prompt vs. tool: the contrast table
TOOL PROMPT
--------------------------------------------------------------------------
What it is A function that produces A message template
an effect or a calculation filled in with data
Identified by name + inputSchema name + arguments (flat
(full JSON Schema) list, no types)
Used with tools/call, with arguments prompts/get, with
that vary each time arguments that vary
Who decides The MODEL, reading the The USER, choosing
to use it description from a catalog
What it returns content: the result of messages: already-
running something assembled conversation turns
Reservo example get_quote(room, tier, hours) plan_booking(room?)
-> {"price_cents": 6000} -> instruction to review
policy and get a quote
Prompt vs. resource: "user-controlled" is not the same as "application-driven"
Another possible confusion: thinking that, since neither tools nor resources are user-controlled, prompts and resources are basically the same thing from the user's point of view. They aren't. The difference lies in who makes the decision, in each case:
- A resource is chosen for display by the application (the host) — according to its own logic, without the user necessarily having asked for anything at that specific moment. It could, for example, automatically inject a resource's content when a new conversation opens, without the user having clicked on anything.
- A prompt is chosen for activation by the user — an explicit act, typically choosing from a menu or typing a recognizable command. There's no way for a prompt to "activate itself": there's always a human choice involved.
In Reservo, this difference is clear: the two policies (M4) could appear automatically in the context of any conversation about Reservo, without anyone explicitly requesting them — that's consistent with being application-driven. plan_booking, on the other hand, only appears when someone, explicitly, decides "I want to use the plan-a-booking template" — never by the application deciding on their behalf.
Worked example: classifying five pieces of Reservo
Before touching wire protocol, a mental exercise — the same in spirit as lesson 02 of Module 4, now with all three categories complete — to cement the "who decides" criterion:
# No wire protocol in this lesson yet -- this is just for reasoning
# about the classification, with Python as clear notation.
candidates = [
{"name": "get_quote", "decides": "model"},
{"name": "cancellation-policy", "decides": "application"},
{"name": "plan_booking", "decides": "user"},
{"name": "book_room", "decides": "model"},
{"name": "membership-tiers", "decides": "application"},
]
kind_by_decider = {"model": "tool", "application": "resource", "user": "prompt"}
for item in candidates:
kind = kind_by_decider[item["decides"]]
print(f"{item['name']:20s} decide={item['decides']:12s} -> {kind}")
What to expect:
get_quote decide=model -> tool
cancellation-policy decide=application -> resource
plan_booking decide=user -> prompt
book_room decide=model -> tool
membership-tiers decide=application -> resource
The criterion is the same one across all three categories, and it never changes: it's not "how complex it is," nor "whether it has arguments," nor "whether it produces text" — it's, exclusively, who decides to activate it. Lesson 07 revisits this same table, now with the exact wire protocol shapes of each primitive next to it.
Common mistakes
-
Thinking that "optional" via
required: falseis a property exclusive to prompts. No — tools can also have optional fields in theirinputSchema(outsiderequired). What's distinctive about a prompt isn't that its arguments are optional, but that the decision to activate it in the first place belongs to the user, whether it has arguments or not. -
Assuming a prompt needs at least one argument to make sense. No. A prompt with no arguments at all is still a legitimate template — a fixed instruction, with no blanks to fill —
plan_bookingwith its single optional argument already shows you that, even with zero arguments provided, the prompt remains useful. -
Confusing "the user decides to activate it" with "the user writes the prompt's text by hand." No. The user chooses which template to activate (and, optionally, with what arguments) — the content of the generated message is built by the server, following the logic it declared (lesson 06 of this module shows it in code).
-
Believing a prompt replaces writing instructions directly in a conversation. It doesn't replace it — it standardizes it. The same text
plan_bookinggenerates, you could, in principle, write yourself every time in a conversation; the prompt's advantage is not having to draft it from memory each time, and that any MCP-compatible host knows to show it to you as a recognizable option.
Exercises
Exercise 1: Tool, resource, or prompt (Easy)
For each of these four capabilities of a hypothetical MCP server for a technical support tool, say which of the three primitives it is, and in one sentence, who decides to activate it:
A) create_ticket(subject, body) -> creates a new ticket
B) sla_policy.md -> fixed document with the guaranteed response times
C) draft_incident_report(severity?) -> template that assembles an initial
incident report, with severity as an optional argument
D) close_ticket(ticket_id) -> closes an existing ticket
See solution
- A) Tool. The model decides to invoke it mid-conversation, when the user asks to create a ticket — the model reads the
descriptionand decides it's time to call it now. - B) Resource. A fixed document with a known URI; the application decides when to show it as context, and it's not an action that "gets executed."
- C) Prompt. The user decides to activate the template explicitly (for example, choosing "draft incident report" from a menu), with
severityas an optional argument that fills in the template — the exact same pattern asplan_bookingwithroom. - D) Tool. It has an effect (closes the ticket) and the model invokes it when the conversation calls for it — the same pattern as
cancel_bookingin Reservo.
Exercise 2: Explain "user-controlled" without using that phrase (Medium)
A teammate tells you: "I don't get why prompts is a separate category — in the end, both a tool and a prompt end up being 'something activated with a name and some arguments,' right?" Write a 3-4 line explanation answering specifically with the concept of "who makes the decision to activate it," without using the phrase "user-controlled" (you have to explain it in your own words).
See solution
You're right that the shape of the message looks similar — a name, some arguments — but the real difference is in who decides that name gets invoked in the first place. With a tool, it's the model that, evaluating the ongoing conversation, decides "I need to call get_quote now" — a decision it makes without anyone explicitly asking for it at that instant. With a prompt, that decision is never made by the model: it's made by the person using the application, choosing from a catalog before the relevant conversation even starts. A prompt never "activates itself" mid-conversation the way a tool can.
Exercise 3: Design a prompt for a new case (Hard)
You're designing an MCP server for an internal code library. You want to expose a template that helps a developer prepare a pull request description, with two optional arguments: ticket_id (the ticket it resolves) and breaking_change (whether it introduces a breaking change). Declare the full Prompt object (following the name/description/arguments shape from this lesson, with no type in the arguments) and explain, in one sentence, why this is a prompt and not a tool, even though it ends up generating text to paste into a PR description — something a tool could, in principle, also do.
See solution
DRAFT_PR_DESCRIPTION_PROMPT = {
"name": "draft_pr_description",
"description": "Draft a pull request description following the team's template",
"arguments": [
{"name": "ticket_id", "description": "Ticket this PR resolves", "required": False},
{"name": "breaking_change", "description": "Whether this PR introduces a breaking change", "required": False},
],
}
Why it's a prompt and not a tool: even though the final result is text (as a tool's result could also be), what sets the primitive apart isn't what it produces, but who decides to use it. A developer about to open a PR explicitly chooses "I want the PR description template" — it's not the model, evaluating the conversation, autonomously deciding "I'm going to draft a PR description now" without anyone having asked for it. If, instead, there were a draft_pr_description(...) tool the model could invoke on its own when it detects the user finished coding something, that would be a legitimate tool with the same name and the same result — the difference between the two versions isn't in the text they generate, but in whether activation depends on an explicit human choice (prompt) or on a model decision mid-conversation (tool).
Summary and next step
- An MCP prompt is a message template with
nameand optionalarguments, whose use is decided by the user (user-controlled), not by the model (like a tool) nor by the application (like a resource). - A prompt's arguments (
name/description/required) are deliberately simpler than a tool'sinputSchema: they carry notypeorenum, because they don't validate a function call — they identify which blank in the template can be filled in. - The classification criterion is the same across all three primitives: who decides to activate it, not how complex it is or what it produces.
- This guide's anchor prompt,
plan_booking, has a single optional argument (room) and remains useful even without it — lesson 06 confirms this by running both cases.
Next lesson: 03 — prompts/list with arguments. The module's first real method: how a client discovers a server's prompt catalog, run against reservo-mcp-server extended with plan_booking.
Additional resources
- Model Context Protocol — Specification 2025-06-18: Prompts — The primitive's formal definition, including the user-controlled categorization and the shape of
PromptArgument. - Model Context Protocol — Specification 2025-06-18: Tools — The direct contrast: model-controlled, already covered in Module 3.
- Model Context Protocol — Specification 2025-06-18: Resources — The direct contrast: application-driven, already covered in Module 4.
- Model Context Protocol — Architecture overview — The overview of the three primitives and who controls each one.