Chapter 6 · 6 lessons
Model Context Protocol (MCP)
Tools, resources, and prompts over stdio and Streamable HTTP; the stateless 2026 revision; OAuth; configuring MCP in every client; and designing lean, safe servers.
Cursor · Claude Code · OpenAI Codex · GitHub Copilot · OpenClaw · LangChain / LangGraph
Show all 6 lessonsHide list
Lesson 1 of 6
What MCP Standardizes
MCP is an open protocol (JSON-RPC 2.0 messages) that lets any AI client talk to any integration server. Instead of writing a GitHub integration per IDE, you write one MCP server and Cursor, Claude Code, Codex, Copilot, OpenClaw, and LangGraph agents can all use it.
| Primitive | Controlled by | Example |
|---|---|---|
| Tools | Model (with user approval) | create_issue, run_query, deploy_preview |
| Resources | Application / user | file://, db schema, docs pages |
| Prompts | User | A 'triage this incident' template |
| Elicitation (client feature) | Server asks, user answers | Confirm a destructive action, request a missing field |
Refresher
Tool schema design and agent failure modes are covered in the AI & FDE track's Agents & Tools chapter — this chapter focuses on the protocol and operating it.
Lesson 2 of 6
Transports and the Stateless 2026 Revision
| Transport | How | Use for |
|---|---|---|
| stdio | Client launches server as a subprocess; JSON-RPC over stdin/stdout | Local tools, dev machines, CLIs |
| Streamable HTTP | Single HTTP endpoint; POST requests, optional SSE streaming | Remote/shared servers, SaaS integrations |
The 2026-07-28 specification made MCP stateless at the protocol layer: the initialize handshake and protocol sessions are gone, clients can call server/discover to learn capabilities, and server-to-client needs (like elicitation) are expressed as input-required results the client resolves and retries. List results can carry a TTL so clients cache tool lists sensibly. Sampling and roots are deprecated.
- Stateless servers scale horizontally behind a normal load balancer
- Application state (a cart, a draft) should be explicit — pass IDs, do not rely on connection affinity
- Older SDKs and clients may still speak the prior session-based revision; check versions when debugging
Lesson 3 of 6
Configuring MCP in Each Client
| Client | Config location | Notes |
|---|---|---|
| Cursor | .cursor/mcp.json (project) or ~/.cursor/mcp.json | Toggle servers per project |
| Claude Code | .mcp.json (project), user settings, or claude mcp add | Plugins can ship .mcp.json |
| Codex | [mcp_servers.<name>] in config.toml, or codex mcp add | enabled_tools / disabled_tools to trim |
| Copilot | mcp.json in the IDE; repository settings for the cloud agent | Custom agents can declare mcp-servers |
| OpenClaw | Gateway config and plugin bundles | Per-agent tool policy |
# ~/.codex/config.toml [mcp_servers.github] url = "https://api.githubcopilot.com/mcp/" bearer_token_env_var = "GITHUB_TOKEN" enabled_tools = ["get_issue", "list_pull_requests", "create_pull_request"]
Lesson 4 of 6
The Hidden Cost of Tools
Every connected server's tool names, descriptions, and JSON schemas are loaded into context whether or not the agent uses them. Ten servers with twenty tools each can burn a large slice of the window before you type a word — and more tools means more wrong-tool calls.
- Enable servers per project, not globally
- Allowlist the tools you need (enabled_tools, tool filters, custom-agent tool lists)
- Design fewer, higher-level tools: search_issues(query) beats ten narrow list_* calls
- Keep descriptions short and schemas tight; return compact results with pagination
- Prefer a skill + CLI script when a task is local and deterministic
Signal
Strong answers mention measuring idle schema size and pruning tools. Weak answers treat 'add another MCP server' as free.
Lesson 5 of 6
Auth and Security
- Remote servers use OAuth 2.1 with PKCE; clients bind tokens to the server with resource indicators so a token for one server cannot be replayed at another
- Never pass user tokens straight through to downstream APIs (the confused-deputy problem)
- Tool descriptions and results are untrusted input — a malicious server or poisoned document can inject instructions
- Namespace tools when aggregating servers; two servers can both expose 'search'
- Return errors as tool results (isError: true) so the model can recover instead of crashing the loop
- Require human approval for writes, deletes, payments, and outbound messages
Lethal trifecta
Private data access + exposure to untrusted content + an outbound channel = exfiltration risk. If one agent session has all three, remove one or add a human gate.
Lesson 6 of 6
Worked Example: From 9 Servers to 3
A developer connected every MCP server they found. The agent got slower and started calling the wrong tools.
connected: github, gitlab, jira, linear, notion, slack, postgres,
filesystem, browser → ~180 tools loaded every session
agent: (calls linear.search_issues on a Jira project, then
notion.search for a Postgres column name).cursor/mcp.json for this repo: github, postgres (read-only role),
playwright
codex config: enabled_tools = ["get_issue", "create_pull_request"]
idle schema: ~180 tools → 19 tools
agent: picks postgres.describe_table on the first try- Enabled servers per project instead of globally
- Allowlisted tools and used a read-only database role
- Measured the idle tool count before and after
Takeaway
Every tool you connect is paid for on every turn — in tokens and in wrong choices.