MCP defines exactly three server-side primitives — tools, resources, and prompts — and nearly every design mistake in a new MCP server comes from collapsing them into one: modeling everything as a tool. That instinct is understandable, because a tool is the primitive most teams meet first, and it is genuinely the most general one. But generality is not the same as fitness, and a server that exposes 'get_document', 'get_release_notes', and 'get_onboarding_guide' as three tools has paid a real cost for no benefit: three schema definitions, three descriptions the model has to read and distinguish, and three turns spent deciding to call something that needed no decision at all.
The three primitives differ along exactly one axis that matters more than any other: who controls when they get used. A tool is invoked by the model — it appears in the list the model sees, and the model decides, turn by turn, whether calling it is worth the effort. A resource is read by the host — the application code, not the model, decides what to fetch and puts the result directly into context. A prompt is offered by the server and selected by a human or the host — the server never gets to force it onto anyone; it just sits available, like a saved query.
This lesson gives you a concrete test for choosing between tools and resources, shows why resources exist at all when a tool could technically do the same job, explains why prompts are the primitive almost nobody uses and what they're actually for, and connects tool annotations — readOnlyHint, destructiveHint, idempotentHint, openWorldHint — to the authorization-as-forcing-function idea from lesson 07. By the end, 'which primitive' should be a five-second decision, not a design meeting.