--- id: mcp title: Model Context Protocol (MCP) code: MC subtitle: Build, call and serve stateless MCP 2026-07-28 servers over stdio and Streamable HTTP. hue: 160 sources: phases/13-tools-and-protocols/06-mcp-fundamentals, phases/13-tools-and-protocols/07-building-an-mcp-server, phases/13-tools-and-protocols/08-building-an-mcp-client, phases/13-tools-and-protocols/09-mcp-transports, phases/13-tools-and-protocols/12-mcp-roots-and-elicitation, phases/13-tools-and-protocols/16-mcp-security-oauth-2-1, phases/13-tools-and-protocols/29-mcp-reliability-cancellation-and-flow-control source_hash: 971c475a75b5a0e9 --- ## Tasks type: task-table from: phases/13-tools-and-protocols/07-building-an-mcp-server/docs/en.md, phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py, phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/09-mcp-transports/code/main.py | Task | Code | |---|---| | Serve over stdio | `python3 main.py` | | Run the stdio demo | `python3 main.py --demo` | | Serve Streamable HTTP | `python3 main.py --serve` | | Probe the HTTP server | `python3 main.py --probe` | | Discover a server | `make_request(1, "server/discover")` | | List tools | `make_request(2, "tools/list")` | | Call a tool | `make_request(5, "tools/call", {"name": "", "arguments": })` | | Add a legacy peer | `client.add_server("", , allow_legacy=True)` | ## Define and run a tool type: annotated-code lang: python from: phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py ```python TOOLS = [{ "name": "notes_search", # name the model sees "description": "Search note titles and bodies by keyword.", "inputSchema": {"type": "object", # JSON Schema of arguments "properties": {"query": {"type": "string"}}, "required": ["query"]}, "annotations": {"readOnlyHint": True}, # hint only }] def handle_tools_call(params): executor = TOOL_EXECUTORS.get(params.get("name")) if executor is None: # unknown tool return complete({"content": [...], "isError": True}) content = executor(params.get("arguments", {})) return complete({"content": content, "isError": False}) ``` ## One tools/call over HTTP type: annotated-code lang: text from: phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md ```text POST /mcp # one message per POST Accept: application/json, text/event-stream MCP-Protocol-Version: 2026-07-28 # equals the _meta version Mcp-Method: tools/call # equals the JSON-RPC method Mcp-Name: notes_search # equals params.name {"jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": {"name": "notes_search", "arguments": {"query": "MCP"}, "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}} ``` ## Architecture type: architecture from: phases/13-tools-and-protocols/08-building-an-mcp-client/docs/en.md, phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py, phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/16-mcp-security-oauth-2-1/docs/en.md ```arch node | host | 1,1 | Agent host | merges tool catalogs node | client | 2,1 | MultiServerClient | one Peer per server node | stdio | 3,1 | stdio | one JSON line each node | server | 4,1 | MCP server | dispatch, HANDLERS node | authz | 2,2 | Authorization server | issues tokens node | http | 3,2 | Streamable HTTP | POST /mcp node | prims | 4,2 | Primitives | tools, resources, prompts edge | host | client | route edge | client | stdio | subprocess edge | client | authz | token edge | client | http | POST edge | stdio | server | stdin edge | http | server | /mcp edge | server | prims | handler ``` ## Discover, list, call type: flow from: phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md, phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py, phases/13-tools-and-protocols/07-building-an-mcp-server/docs/en.md 1. **Client**: Sends `server/discover` with version and capabilities in `params._meta`. 2. **Server**: Returns `supportedVersions` and `capabilities`. No session starts. 3. **Client**: Sends `tools/list`. Gets a sorted list with `ttlMs` and `cacheScope`. 4. **Client**: Sends `tools/call` with `name`, `arguments` and a new `id`. 5. **Server**: `validate_request()` checks `params._meta`. A bad version gives `-32022`. 6. **Server**: `dispatch()` routes by `method` to `handle_tools_call()`. 7. **Server**: Returns `resultType` complete with `content` and `isError`. ## Methods type: command-table from: phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/29-mcp-reliability-cancellation-and-flow-control/docs/en.md | Command | What it does | |---|---| | `server/discover` | Versions, capabilities, instructions, server identity | | `tools/list` | Lists tools. Cacheable | | `tools/call` | Runs one tool. Returns content blocks | | `resources/list` | Lists URI-addressed data. Cacheable | | `resources/read` | Reads one resource by URI | | `prompts/list`, `prompts/get` | Lists and renders prompt templates | | `subscriptions/listen` | Long-lived POST stream for change events | | `notifications/cancelled` | Cancels a request on stdio. No response | ## Key concepts type: concepts from: phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md, phases/13-tools-and-protocols/07-building-an-mcp-server/docs/en.md, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md - **Stateless core**: Every request carries the metadata to interpret it. - **Request metadata**: Version and capabilities are required. `clientInfo` is recommended. - **Complete result**: Every success has `resultType` set to complete. - **Cacheable result**: Lists, reads and discovery carry `ttlMs` and `cacheScope`. - **Tool annotations**: `readOnlyHint`, `destructiveHint` are hints. The server enforces. - **stdio**: Client-launched subprocess. JSON on stdout, logs on stderr. - **Streamable HTTP**: One `/mcp` endpoint. Each message is a new POST. - **Legacy era**: Up to `2025-11-25`, `initialize` then `notifications/initialized`. ## Defaults type: defaults from: phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py, phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py, phases/13-tools-and-protocols/09-mcp-transports/code/main.py | Setting | Default | Note | |---|---|---| | `PROTOCOL_VERSION` | 2026-07-28 | Modern revision in the lessons | | `server/discover` ttl | 3_600_000 | ms, `cacheScope` public | | `tools/list` ttl | 60_000 | ms, `cacheScope` public | | `resources/read` ttl | 5_000 | ms, `cacheScope` private | | `cache_scope` | private | `complete()` default | | HTTP port | 8017 | `--serve` binds 127.0.0.1 | | `MAX_REQUEST_BYTES` | 1_048_576 | HTTP body cap | | `legacy_probe_timeout_ms` | 1_000 | Deadline for one `initialize` probe | ## Troubleshooting type: troubleshooting from: phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md, phases/13-tools-and-protocols/07-building-an-mcp-server/docs/en.md, phases/13-tools-and-protocols/08-building-an-mcp-client/docs/en.md, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/16-mcp-security-oauth-2-1/docs/en.md | Symptom | Cause | Fix | |---|---|---| | `-32022` | Version not supported | Pick from `data.supported`. Retry, new id | | `-32602` | Missing request metadata | Stamp `_meta` on every request | | HTTP 400 `-32020` | Header disagrees with body | Mirror version, method and name exactly | | HTTP 403 on POST | `Origin` not allowed | Add the exact origin to the allowlist | | HTTP 405 | GET or DELETE on `/mcp` | POST, and `subscriptions/listen` for events | | stdio client fails to parse | Logs on stdout | Write diagnostics to stderr | | HTTP 401 | Invalid, expired or wrong-audience token | Get a token for this resource | | 403 `insufficient_scope` | Tool needs more scope | Step up with the challenge scope | ## Glossary type: glossary from: phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/12-mcp-roots-and-elicitation/docs/en.md, phases/13-tools-and-protocols/16-mcp-security-oauth-2-1/docs/en.md - **JSON-RPC 2.0**: Envelope of every MCP request, response and notification. - **SSE**: `text/event-stream` response. Notifications, then the final response. - **MRTR**: Multi Round-Trip Request. `input_required`, then a fresh retry. - **PKCE**: Verifier and S256 challenge. Protects an intercepted code. - **CIMD**: HTTPS metadata document whose URL is the client id. - **DCR**: Deprecated dynamic client enrollment for compatibility. - **RFC 9728**: Protected-resource metadata. Names the authorization servers. ## Questions and answers type: qa from: phases/13-tools-and-protocols/07-building-an-mcp-server/docs/en.md, phases/13-tools-and-protocols/08-building-an-mcp-client/docs/en.md, phases/13-tools-and-protocols/16-mcp-security-oauth-2-1/docs/en.md, phases/13-tools-and-protocols/29-mcp-reliability-cancellation-and-flow-control/docs/en.md, phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md - **Tool error or protocol error?** `isError: true` for a valid call that fails. JSON-RPC error for a bad request. - **Can a server call the client?** No. It returns `input_required`. The client retries with a new id. - **How do I cancel?** stdio: send `notifications/cancelled`. HTTP: close the response stream. - **Does stdio need OAuth?** No. Authorization applies to HTTP transports. - **When do I send `initialize`?** Only to an allowlisted legacy peer. Never by default. ## Tips type: tips from: phases/13-tools-and-protocols/06-mcp-fundamentals/docs/en.md, phases/13-tools-and-protocols/08-building-an-mcp-client/docs/en.md, phases/13-tools-and-protocols/09-mcp-transports/docs/en.md, phases/13-tools-and-protocols/16-mcp-security-oauth-2-1/docs/en.md - Sort list items. Stable order gives stable cache keys and model context. - Bind local servers to `127.0.0.1`. Origin checks are not authentication. - Match origins exactly. Prefix checks accept attacker suffixes. - Send the bearer token in `Authorization`. Never in the URL. - Return an explicit state handle. Sticky routing breaks on failover. - On a tool name collision, prefix or reject. Never overwrite.