MCP Tools vs Resources vs Prompts: What to Expose to an AI Agent A developer's guide to the Model Context Protocol argues that teams wrapping internal platforms in MCP should map capabilities onto its three root primitives — tools, resources, and prompts — rather than exposing every endpoint as a tool. It recommends tools for actions with side effects, resources for on-demand data reads, and prompts for reusable, parameterized procedures like an incident-triage runbook. When a team first wraps an internal platform in MCP, the result almost always looks the same: forty tools named get x , list x , create x , and update x , plus a few things that should never have been tools at all. The model gets lost, picks the wrong getter, and burns context reading giant responses it only needed a fragment of. The Model Context Protocol defines three root primitives — tools, resources, and prompts — because those are three genuinely different relationships an agent can have with your system. Mapping your capabilities onto them correctly is most of the design work. A tool is a function the model can call: it takes structured input, executes, and returns structured output or text. Tools are the right choice when the agent needs to do something, especially something with a side effect or a computation. { "name": "refund payment", "description": "Issues a full or partial refund against a captured payment. Requires the refund:write scope. Safe to retry with the same idempotency key.", "inputSchema": { "type": "object", "properties": { "payment id": { "type": "string" }, "amount cents": { "type": "integer", "minimum": 1 }, "reason": { "type": "string", "enum": "customer request", "duplicate", "fraud" } }, "required": "payment id", "reason" } } Signs something belongs in tools: A resource is data the client or model can read, identified by a URI. Resources are nouns: documents, records, configuration, log streams. They come in two shapes: static URIs like service://health/status and parameterized templates like orders://{order id} . { "uri": "orders://ord 8821", "name": "Order ord 8821", "description": "Full order record including line items, payment state, and fulfillment timeline.", "mimeType": "application/json" } The distinction that matters: a resource is pulled on demand and typically read into context as data, while a tool is invoked . When an agent needs to inspect an order before deciding, reading the resource is cheaper and more accurate than calling a get order tool whose text output gets truncated into the conversation. Signs something belongs in resources: A common mistake is exposing every GET endpoint as both a resource and a tool. Pick resources for the documents humans also read the API reference, the status page, the current user's entitlements and keep the rest as tools only when the agent must actively query with computed parameters. Templated resources let the client offer URI completion and still avoid a hand-written getter tool: { "uriTemplate": "logs://{service}/{date}", "name": "Service daily logs", "mimeType": "text/plain" } The model fills service and date ; the host fetches. Use templates when the address space is large and enumerable by pattern. A prompt is a reusable, parameterized instruction template the server contributes. Where tools and resources expose capability, prompts expose procedure : the agreed way your team wants a task done. { "name": "triage incident", "description": "Walks an on-call engineer through triaging a service alert: gather recent deploys, correlate errors, draft an incident channel summary.", "arguments": { "name": "service", "required": true }, { "name": "alert", "required": true } } When the user selects it, the server returns a prepared message sequence, often pre-wired to the right resources and tools. Prompts are the answer to "every agent reinvents our runbook differently": encode the runbook once, on the server, where you can update it without touching clients. Signs something belongs in prompts: | Capability | Primitive | Who initiates | |---|---|---| | Refund a payment, create an order, run a migration | Tool | Model | | Fetch the current order record | Resource | Model or user | | Read the API documentation or status page | Static resource | User or model | | Browse a large address space by pattern | Resource template | Model | | "Follow the incident triage runbook" | Prompt | User usually from a menu | | Convert a curl command into a documented operation | Tool | Model | Roots and sampling round out the model, and both are commonly ignored in first implementations. Roots let a client expose its own filesystem or document boundaries to the server, so a local MCP server knows which project it operates on without configuration. Sampling lets a server request a model completion from the client , useful when a tool needs an LLM sub-step but must not carry its own API key. Neither replaces tools, resources, or prompts; they handle the edges around them. A single API specification describes all three relationships at once, which is why generating an MCP server from one spec works so naturally: The spec stays the source of truth, and the three primitives stop being three things to maintain by hand. That argument is developed in your API already describes the tools your agent needs https://www.powerduck.com/blog/your-api-already-describes-the-tools-your-agent-needs/ , and the human-versus-agent framing is in one spec, two audiences https://www.powerduck.com/blog/one-spec-two-audiences-humans-and-ai-agents/ . You can see the tool and resource inventory a spec produces in the online demo https://www.powerduck.com/demo/ .