AI & MCP
Resources and tools in MCP: two primitives, two contracts
An MCP tool is a function called with arguments; a resource is a document read by URI. The difference is in the contract, not the payload.
Key points
- A tool takes arguments and returns content; a resource has a stable URI and a MIME type.
- The model picks the tool, the client or the user reads the resource: the decision point moves.
- The EneUI server exposes 9 tools and 4 resources: design systems are documents, components are searched.
In MCP a tool is a function the model calls with JSON arguments, while a resource is a document identified by a URI that the client reads and may cache. The contract differs, not the content.
The tool contract
A tool declares inputSchema, receives arguments and returns content blocks. The model decides, every call may carry different arguments, and the answer is not reusable: POST /mcp answers with Cache-Control: no-store.
The resource contract
A resource has a stable URI, a name, a description and a MIME type. The client can list it, show it to the user without involving the model, and keep it because the same URI promises the same document.
curl -s https://ui.eneawork.it/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/list"}'
The response lists 4 resources with uri, name, description and a text/markdown mimeType. There is no search parameter: you read by URI, you do not query.
curl -s https://ui.eneawork.it/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"eneui://design/minimal-dark"}}'
That second call returns the design system as Markdown: CSS variables, tokens, Tailwind rules and agent instructions. Same URI, different reads, identical content.
| Aspect | Tool | Resource |
|---|---|---|
| Address | name and arguments | URI |
| Who decides | the model | the client or the user |
| Cache | none, no-store | per URI |
| Failure | content with isError | -32602 unknown resource |
| EneUI count | 9 | 4 |
Why the catalogue exposes four resources
Design systems are stable documents: the same tokens for whoever asks, with no arguments to guess. Components are not, because they depend on query, category and library, so they stay tools.
What a resource cannot do
A resource does not search. There is no method to filter resources by text: the client gets the list and reads by URI. When search is the job, the right primitive is the tool.
The EneUI catalogue
A real block is Dashboard with Revenue and Customers, 5 files and 14 registry dependencies; modals and the Aceternity animated modal, MIT licence and 290 stars declared upstream, stay tools because they have to be searched. Counts read on 1 November 2026.
The limitation
The eneui:// scheme works only inside this server: a client cannot resolve the same URI elsewhere, and there is no per-category resource list. The protocol mandates neither global URIs nor search.
Where to continue
The three primitives are described in what MCP is; the tool schema is in designing an MCP tool; the comparison with REST APIs is in MCP vs REST. The message-template case is in prompt templates in MCP.
Frequently asked questions
What is the practical difference between a tool and a resource?
A tool takes arguments and the model decides when to call it; a resource has a URI and the client reads it on request or at startup. The first is an action, the second a document.
Can resources be searched?
No. The protocol offers listing and reading by URI, not a query. Search needs a tool, such as search_components in the EneUI catalogue.
How many resources does EneUI expose?
Four, one per design system: minimal-dark, minimal-light, corporate-blue and warm-editorial, all as Markdown under the eneui://design/ scheme.
Must a client support resources?
No. The capability is optional: a client that does not declare it uses tools and gets the same tokens through get_design_system.
What happens with an unknown URI?
The server answers with error -32602 and the message “unknown resource” followed by the URI. It returns neither empty content nor an HTTP 404.