Diving into MCP's two passive primitives: Resources as context data providers via resources/list and resources/read with resource URIs and templates, and Prompts as reusable templates via prompts/list and prompts/get with argument binding.

In episode 4 we dissected Tools — the primitives that "act". Now it's the turn of two more passive but equally important primitives: Resources and Prompts. If tools are the server's hands, then resources are the library and prompts are the ready-made recipes.
This episode covers how resources are registered and read via resources/list and resources/read, what resource URIs and resource templates are, and how prompt templates are registered via prompts/list and retrieved via prompts/get with argument binding. Understand these two primitives, and your MCP knowledge is three quarters of the way there.
Resources are formatted data the model can read as context. Some real examples:
README.md or a server configuration.The distinguishing property of resources: passive. Reading a resource doesn't change any state — the model reads, the server sends data, done. Because of this nature, resources are a great fit for the RAG (retrieval-augmented generation) pattern we'll cover in episode 20.
Every resource has a unique address in the form of a URI. MCP uses standard URIs (file://, https://, or custom schemes) with a scheme followed by a host and path. Examples: file:///home/user/config.json, weather://jakarta/current, or git://repo/main/README.md. The client uses this URI to request a specific resource — whether that resource is a physical file, a database entry, or server-generated data.
The resources/list method returns the list of available resources:
{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/list",
"params": {}
}The server responds with a resource list containing uri and name. Just like tools, this list supports pagination via nextCursor when it's large.
To fetch the contents of a resource, the client calls resources/read with the URI:
{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/read",
"params": {
"uri": "file:///home/user/config.json"
}
}The server replies with formatted content (text or blob):
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"contents": [
{
"uri": "file:///home/user/config.json",
"mimeType": "application/json",
"text": "{ \"port\": 8080 }"
}
]
}
}mimeType tells the host the content type, while text (or blob for binary) holds the data. This is where the model gets its context to answer questions.
Sometimes resources can't be registered one by one — for example, "any file inside a directory". That's where resource templates come in. A template is a URI pattern with placeholder parameters:
{
"resourceTemplates": [
{
"uriTemplate": "file://{path}",
"name": "File di workspace"
}
]
}A URI pattern with a path placeholder matches any URI like file:///home/user/app.py. When a client wants to read file:///home/user/app.py, the server simply matches the URI against the template and serves the request. This makes resources effectively infinite in number — from a single pattern.
If tools and resources provide content, prompts provide structure. A prompt is a reusable message template designed to complete a specific task — for example "summarize meeting", "generate commit message", or "review pull request".
An important difference from tools: a prompt doesn't "do" anything. It prepares messages that will later be sent to the model. The model is the one that acts — but the direction of the task has already been designed by the server.
The prompts/list method returns the list of available prompts:
{
"jsonrpc": "2.0",
"id": 1,
"method": "prompts/list",
"params": {}
}Each prompt is defined by name, description, and a list of arguments the user can fill in.
When a user selects a prompt, the host calls prompts/get with the prompt name and arguments:
{
"jsonrpc": "2.0",
"id": 2,
"method": "prompts/get",
"params": {
"name": "summarize-meeting",
"arguments": {
"meetingId": "2026-08-03"
}
}
}The server replies with a sequence of messages ready to send to the model. Note the argument binding part: the server inserts the meetingId value into the template, producing a message specific to that meeting.
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"description": "Ringkas catatan rapat",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Ringkas catatan rapat dengan ID 2026-08-03 menjadi tiga poin."
}
}
]
}
}Info
Resources and prompts are both passive primitives, but their roles differ: resources provide data for the model to read, while prompts provide instructions sent to the model. Tools that perform actions can use either as inputs or outputs.
To keep them straight, here's a quick guide:
| Need | Choose |
|---|---|
| The model needs data/facts (file contents, query results) | Resources |
| The model needs task structure (summarize, review, generate) | Prompts |
| The model must perform an action (send email, write file) | Tools |
The simple rule: data is read via resources, work is structured via prompts, and actions are executed via tools. All three complement each other within a single server. To see the full list of resources and prompts on your server interactively, run npx @modelcontextprotocol/inspector and open the corresponding tab.
In this episode 5 you've understood Resources as context data providers (with resource URIs and resource templates), and Prompts as reusable templates (with argument binding). You also know when to use each primitive.
Key takeaways:
resources/read using resource URIs.prompts/get and produce messages ready to send to the model.In the next episode 6 we start getting hands-on: building an MCP server with the SDK — TypeScript using McpServer and FastMCP, and Python using FastMCP, up to a working simple tool server ready to test. See you in episode 6!