MCPmcptoolsresourcespromptscapabilities

MCP Primitives: Tools, Resources, Prompts

An MCP server exposes tools (model-invoked actions), resources (application-read data) and prompts (user-selected templates); knowing which is which decides who controls the call.

Interview question
Progress

Three primitives, three controllers

The cleanest way to remember the primitives is by who decides to use them. Tools are model-controlled: the LLM chooses to call search_repository mid-reasoning. Resources are application-controlled: the host decides to load file:///repo/README.md into context. Prompts are user-controlled: a person picks "Summarise this PR" from a menu and the server returns a filled template.

This split matters for safety and cost. Anything model-controlled can be triggered by injected text and must be permissioned. Anything application-controlled is deterministic and can be cached. Anything user-controlled is explicit intent.

  • Tool: search_repository(query, language?) on a GitHub server — has a JSON-schema inputSchema, returns text/JSON content, may have side effects.
  • Resource: file:///workspace/src/main.py or github://org/repo/issues/42 — addressed by URI, read-only, has a MIME type, may support subscriptions for change notifications.
  • Prompt: review_pull_request(pr_number) — returns a message list the host inserts into the conversation, optionally embedding resources.

Tools in detail

A tool definition is a name, a description and a JSON schema for arguments. The description is the only thing the model reads to decide whether to call it, so it carries the same weight as in Tool Schemas: say what it does, when to use it, and what it returns.

Servers can also annotate tools with hints such as readOnlyHint or destructiveHint. Hosts should use those hints to route destructive tools through Approval Gates and Risk Classes rather than trusting the model to be careful.

A minimal tool definition as returned by tools/list
1const searchRepository = {
2 name: 'search_repository',
3 description: 'Full-text search over code in one repository. Use for "where is X defined" questions. Returns up to 20 file paths with matching snippets.',
4 inputSchema: {
5 type: 'object',
6 properties: {
7 repo: { type: 'string', description: 'owner/name, e.g. acme/billing' },
8 query: { type: 'string', minLength: 2 },
9 language: { type: 'string', enum: ['ts', 'py', 'go'] },
10 },
11 required: ['repo', 'query'],
12 },
13 annotations: { readOnlyHint: true },
14}

Resources and prompts in detail

Resources are the RAG-shaped primitive: they let a host pull a document, a database row or a log excerpt into context without the model issuing a call. A file server might list file:///workspace/** and the IDE decides which open files to attach. Because the host controls it, resource loading belongs in Dynamic Context Assembly, not in the agent loop.

Prompts package a workflow the server author knows well. A database server might ship explain_slow_query(query_id) which fetches the plan as a resource and wraps it with instructions. They are reusable starting points, not something the model discovers and invokes on its own.

  • Resource templates (github://{owner}/{repo}/issues/{id}) let servers expose whole namespaces without enumerating every item.
  • Subscriptions (resources/subscribe) push change notifications so the host can refresh context instead of polling.
  • Capability negotiation at startup tells the client which of the three a server supports (MCP Lifecycle: Init, Discovery, Invocation).

Key points

  • Tools: model-controlled actions with a JSON schema; may have side effects.
  • Resources: application-controlled, URI-addressed, read-only data with MIME types.
  • Prompts: user-controlled templates that return message lists.
  • Who controls the primitive determines its risk profile and caching strategy.
  • Tool descriptions are the model's only guide; write them like Tool Schemas prescribes.
  • Use tool annotations (destructiveHint) to drive approval gates in the host.

When to use — and when not to

Use it when
  • Expose an action the model must decide on at runtime → tool.
  • Expose data the application should attach deterministically → resource.
  • Package a repeatable, human-initiated workflow → prompt.
Avoid it when
  • Do not model bulk data access as a tool; a 5 MB tool result wrecks the context budget — use a resource with pagination.
  • Do not expose 40 near-duplicate tools; consolidate behind fewer, well-described ones.
  • Do not rely on prompts for anything security-relevant — they are convenience, not control.

Failure modes

  • Vague tool descriptions ("Search things") cause misrouting between similar tools.
  • Resources without size limits push the host over its token budget (context-overflow-degradation).
  • Ignoring destructiveHint and letting the model call delete-class tools unattended.
  • Server returns tool results containing instructions that the model follows.