A tool is something a model chooses to call. A resource is something a human chooses to hand it. That distinction shapes how you should design a server.
1The Real Difference Is Who Decides
A tool call is initiated by the model, mid-reasoning, based on the conversation so far. A resource is typically selected by the client application or the user themselves — 'attach this file to the conversation' — and handed to the model as context, without the model needing to decide anything. Prompts follow the same user-driven pattern: a user explicitly invokes a named, reusable prompt template your server defines, rather than the model deciding to run one.
2Why This Distinction Should Shape Your Design
Modeling something as a tool implies you trust the model to decide when it's relevant — appropriate for read_file, where the model can reason about which file it needs. Modeling large or sensitive context as a resource instead keeps that decision with a human, which matters for data too large, too costly, or too sensitive to leave entirely to the model's judgment about when to fetch it.
3Step-by-Step Breakdown
Tools Aren't the Only Primitive. Everything so far has been a tool — something the model decides to call mid-reasoning. MCP defines two other primitives: resources, read-only contextual data the client or user attaches deliberately (like 'include this file in the conversation'), and prompts, reusable templated instructions a user explicitly invokes. Not everything your server exposes should be a tool.
Expose Files as Real Resources. list_resources and the not-found guard are already written. Finish read_resource so a URI that does exist actually returns its real content instead of falling through to None.
What's the key conceptual difference between an MCP resource and an MCP tool?
- →A resource is read-only contextual data the client or user chooses to attach to a conversation, while a tool is an action the model itself decides to invoke while reasoning.
- →They're just two different names for the exact same mechanism, with no functional distinction.
A Complete, Hardened Server. DevAssist now exposes real, safely-guarded tools and real resources, describable to any MCP client. One step remains: actually connecting it to a real AI assistant and shipping it. That's the final lesson.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1List Resource Names in Plain Language, Not Raw URIs Alone
A resource picker in a client UI should show a human-readable name alongside the URI, so a user selecting context doesn't have to parse a raw scheme like file:// to know what they're attaching.
{ uri: "file://README.md", name: "README.md" }SEO Implications
- 1
Target 'MCP resources vs tools' and 'MCP resource URI example' separately
Developers designing a server search for the conceptual distinction and the concrete implementation shape independently.
Best Practices
Model Large or Sensitive Data as a Resource, Not a Tool Result
A tool result gets fed straight back into the model's context automatically — a resource lets a human decide whether that data should be included at all.
Frequent Bugs
Turning every piece of exposed data into a tool, including things a human should explicitly choose to attach.
This removes the human's ability to control what enters the model's context and can leak sensitive data the model decided, on its own, was worth fetching.
Real-World Examples
IDE Resource Pickers
A code editor's MCP integration exposes open files as resources a developer explicitly attaches to a question, while exposing run_tests as a tool the assistant can decide to invoke on its own — the same server can offer both primitives.
resources: open files | tools: run_tests()