Resources and prompts
Explain MCP roles and lifecycle and trace a tool-discovery and tool-call exchange under a pinned protocol version.
THE IDEA
Resources and prompts
Tools are only one of the capabilities a server can offer. Resources expose contextual data (a file, a record) that the host can read and place in context, via resources/list and resources/read. Prompts expose reusable interaction templates with arguments, via prompts/list and prompts/get. They are different from tools: a resource read returns data and changes nothing; a prompt produces messages to send; a tool may act on the world.
Reading a resource returns data; it is not the same as executing an arbitrary shell command. Each capability has its own methods and its own risks.
Predict first. Is reading a resource identical to executing an arbitrary shell command?
Chapter context
MCP defines a protocol for applications to connect models with external capabilities. It standardizes exchanges; it does not grant a server universal authority or make every discovered tool appropriate for every user request.
TRY IT YOURSELF
Part 1. Do it hands-on
Classify capabilities by hand, then write resource and prompt handlers.
Reading a resource returns data; it is not the same as executing an arbitrary shell command. Each capability has its own methods and its own risks.
- To do: Classify each as tool, resource or prompt. The text of a policy document the model should read as context
- To do: An action that sends an email
- To do: A reusable template "Summarise {topic} in {n} bullet points" with arguments
- To do: Is reading a resource identical to executing an arbitrary shell command? Type yes or no
Part 2. Do it in code
- The envelope helpers (check_envelope, error_response) are PROVIDED as before. The demo server has RESOURCES = {uri: text} and PROMPTS = {name: {"arguments": [...], "template": "... {arg} ..."}}.
- resources_list(req): result resources = [{"uri", "name"}] for every resource (name = the uri's last path segment), in the dict's order.
- resources_read(req): params.uri → result contents = [{"uri", "text"}]. An unknown uri is a protocol error -32602 "Unknown resource: <uri>". Reading changes nothing.
- prompts_list(req): result prompts = [{"name", "arguments"}] for every prompt.
- prompts_get(req): params.name and params.arguments (a dict). Unknown prompt → -32602 "Unknown prompt: <name>"; a missing required argument → -32602 "Missing argument: <arg>". Otherwise result messages = [{"role": "user", "content": {"type": "text", "text": the template with each {arg} replaced}}].
- handle(text): envelope check, notifications → None, then dispatch resources/list, resources/read, prompts/list, prompts/get; anything else → -32601.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Is reading a resource identical to executing an arbitrary shell command?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
The MCP specification revision examined here is 2026-07-28. Request envelopes use JSON-RPC 2.0, an ID for response correlation, a method, and method-specific parameters that include _meta with the required io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities. A successful result carries resultType, here "complete". Do not treat this date as a claim about every installed client or SDK.
Worked example
A tools/call request with id 7 names add, supplies a=2,b=3 and carries the protocol version and client capabilities in _meta. The server runs the permitted operation and answers with the same id and a content array holding the text 5. The offline example below checks envelope fields and arithmetic; a production server must also implement discovery, schema validation, errors and transport behavior.
Reference code
The following example uses the Python standard library and does not call a remote service.
meta={'io.modelcontextprotocol/protocolVersion':'2026-07-28','io.modelcontextprotocol/clientCapabilities':{}}
request={'jsonrpc':'2.0','id':7,'method':'tools/call','params':{'name':'add','arguments':{'a':2,'b':3},'_meta':meta}}
args=request['params']['arguments']
total=args['a']+args['b']
response={'jsonrpc':'2.0','id':request['id'],'result':{'resultType':'complete','content':[{'type':'text','text':str(total)}],'isError':False}}
assert request['method'] == 'tools/call'
assert request['params']['_meta']['io.modelcontextprotocol/protocolVersion'] == '2026-07-28'
assert response['id'] == 7
assert response['result']['content'][0]['text'] == '5'
assert response['result']['isError'] is False
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
MCP is not a language model or an automatic permission system. Resource, prompt and tool capabilities differ. Do not assume an initialize handshake: that belongs to revisions before 2026-07-28. Protocol versions and SDK releases must be matched when turning this draft into runnable setup instructions.