What Anthropic teaches in this course
MCP Foundations gave you tools, resources, and prompts. That gets you a working server. Production servers need four more capabilities, and this course is built around them: sampling, notifications, roots, and the two transports. Each one solves a problem you hit the moment a real team starts using your server. Skip them and your server breaks the first time a tool call takes 30 seconds, or the moment your security review asks where the LLM API key lives.
Sampling is the most counterintuitive of the four. It lets the server ask the *client* to make an LLM call on the server's behalf. That sounds backwards until you realize it solves a real problem: the client already has an API key, model preference, and rate limits configured. Putting another LLM key on the server doubles the surface area of secrets and model-version drift. With sampling, the server requests sampling/createMessage, the client (optionally) shows the user what's about to be sent, the client invokes the model, and the response comes back. Common use case: a database-querying server that wants to summarize a result in natural language without ever holding an Anthropic key.
Log and progress notifications are the streaming layer. A long-running tool call (a 30-second build, a multi-step research task) needs to give the client feedback or the user thinks the system has hung. MCP defines two notification flavors: notifications/message for log lines (with severity levels) and notifications/progress for percent-complete updates tied to a progress token included in the original request. The notifications are one-way: the client doesn't ack them, and the server doesn't expect a response. The Python SDK exposes ctx.report_progress() and ctx.log() so you don't write JSON-RPC by hand.
Roots are the security boundary the spec adds for filesystem access. A client publishes a list of roots, meaning directories the server is allowed to read. The server queries them with roots/list and listens for roots/list_changed to react to live changes. This is the spec's answer to "how do I let an MCP filesystem server work without giving it /". The server still implements its own enforcement (the spec is advisory at the protocol layer), but a well-behaved server treats roots as a hard fence and rejects paths that fall outside. Expect exam questions on which side enforces the boundary (server) vs which side declares it (client).
Underneath all four surfaces is JSON-RPC 2.0. MCP uses three message shapes: requests (have id, expect a response), responses (carry the matching id, return result or error), and notifications (no id, no response, fire-and-forget). Knowing this saves you when a server appears hung. Odds are you sent a notification when a request was needed, or vice versa. The Inspector and most SDKs hide the JSON, but when production breaks at 2 a.m., you read the raw frames and you need to know which is which.
Transport is the choice you make first and rarely revisit. stdio is the local default: the client spawns the server as a subprocess, messages flow over stdin/stdout with length prefixes, the connection lives as long as the subprocess does. Simple, secure (no network), but single-client. streamable-http is the remote transport: HTTP POST carries requests, optional Server-Sent Events stream server-to-client messages, and an Mcp-Session-Id header tracks per-client session state. It is the right choice for shared SaaS-style MCP servers but introduces session-stickiness, reconnection, and horizontal-scaling concerns that stdio doesn't have.
The takeaway: combine these four surfaces with the foundation primitives and you have an MCP server that can run long jobs, stream feedback, respect security boundaries, delegate model calls cleanly, and scale horizontally. Most production MCP outages trace to one of these four surfaces being skipped or misconfigured. The exam knows this; questions around "which surface fixes problem X" are reliable territory. When in doubt, run the heuristic: long-running work means notifications, server-side LLM calls means sampling, filesystem access means roots, deployment shape means transport choice. The other heuristic the exam loves is the control point. Sampling is server-requested but client-executed. Notifications are server-emitted with no client response. Roots are client-declared and server-enforced. Transport is client-chosen at connection time. Every advanced MCP question can be answered by figuring out which side owns the decision. Combine this with the foundation course's tools-resources-prompts breakdown, and you have a complete mental model of the protocol.
You'll walk away with
- How sampling lets a server delegate LLM calls back to the client (and why that beats putting an API key on the server)
- How to emit log and progress notifications during long-running tool calls so the client can render real-time feedback
- What roots are, why they matter for filesystem security, and how a server enumerates and respects them
- The full JSON-RPC 2.0 message vocabulary MCP uses (request, response, notification, error)
- When to choose stdio vs streamable-http transport, and how state and reconnection differ between them
- How session state works in streamable-http and the failure modes to design around
15 lessons, with our annotations
6 ideas to carry into practice
Lines worth keeping
Sampling: the server delegates LLM calls
Server calls sampling/createMessage; client invokes the model and returns the completion. Use when the server needs LLM output but you do not want to put another API key on it. Keeps model choice and secrets on the client.
Related concept →Notifications: server streams progress and logs
notifications/progress (with a token from the request) and notifications/message (with a severity level). One-way; no ack. Use for any tool call longer than a few seconds so the client UI can render feedback.
Related concept →Roots: filesystem security boundary
Client publishes a list of allowed directories. Server reads via roots/list, listens for roots/list_changed, and refuses access outside. The client declares; the server enforces. Always assume the spec is advisory and write a hard check.
Related concept →Transport: `stdio` vs `streamable-http`
stdio is local subprocess, single-client, simplest. streamable-http is remote, multi-client, with session IDs and optional SSE streaming. Pick streamable-http when many clients share one server; pick stdio for everything else.
Related concept →Session affinity not enforced
Mcp-Session-Id ties a client to a specific server instance's in-memory state. Without sticky routing at the load balancer, the next request hits a server that has never seen this session and 404s. Pin sessions or externalize state.
Related concept →SSE connection dropped without resume
Long-lived SSE streams die at proxies, load balancers, and on flaky mobile networks. The spec supports resume tokens; if you do not implement them, the client loses notifications on every reconnect.
Related concept →Treating `stdio` like a daemon
stdio servers live and die with the client subprocess; they do not persist. Storing in-memory state and assuming the next session will see it is wrong. Persist to disk, or accept session-scoped state.
Related concept →How this course shows up on the exam
Direct prep for D3 task statements on production integration patterns and D2 questions on tool surface design under operational constraints. Advanced MCP topics surface in the agentic-tool-design scenario, in long-running server discussions, and in any item that contrasts stdio vs remote HTTP deployments.
Blueprint weight20% (D3) + 18% (D2)Check the pattern
You add a new MCP server to .mcp.json but Claude Code does not see its tools. What did you forget?
Frequently asked
What is MCP sampling and when should I use it?
Sampling is when an MCP server asks the client to make an LLM call on the server's behalf via sampling/createMessage. Use it when your server needs natural-language output but you do not want to put a separate Anthropic key, model preference, and rate limit on it. The client owns the model decision; the server just describes what it needs.
How do I show progress for a long-running MCP tool call?
Include a progressToken in the original request, then have the server emit notifications/progress updates with that token and a percent-complete value. Pair with notifications/message for log lines. The Python SDK exposes ctx.report_progress(progress, total) so you do not write JSON-RPC frames by hand.
What are MCP roots and who enforces them?
Roots are filesystem directories the client publishes as the boundary the server is allowed to read inside. The server queries them via roots/list and reacts to roots/list_changed. The client declares; the server enforces. Treat the spec as advisory and write a hard path check, because clients vary in how strictly they police access.
Should I use stdio or streamable-http transport for my MCP server?
Use stdio for local single-client servers (simplest, no network, lifetime tied to the subprocess). Use streamable-http when many clients share one server (Slack-team-wide deployments, hosted SaaS). The latter introduces session-stickiness, reconnection, and horizontal-scaling concerns that stdio does not have.
Why is my streamable-http MCP server losing sessions when scaled to multiple instances?
Mcp-Session-Id is pinned to a specific server instance's in-memory session state. Without sticky routing at your load balancer, the next request lands on an instance that has never seen the session and returns 404. Either configure session affinity at the proxy or externalize session state to Redis or a similar shared store.
How is an MCP notification different from a request?
A request has an id and the receiver must respond (success or error). A notification has no id and the receiver must never respond. Notifications are fire-and-forget. Mixing these up is the most common source of MCP bugs at the wire level: a missing response on what you thought was a notification will hang the client forever.
Does streamable-http require Server-Sent Events?
Not for every interaction. Requests flow over standard HTTP POST. SSE is the optional upgrade path when the server needs to stream messages to the client (notifications, sampling requests, async results). The client opens the SSE stream with a GET; the server pushes events when it has them. Without SSE you can still do request-response, just no server-initiated traffic.
How do I test the advanced MCP features without writing a full client?
Use the MCP Inspector. Run mcp dev <server-file> and the Inspector launches a local web UI that supports sampling (it will prompt you for completions), notifications (you see them stream in), roots (you can publish test roots), and both transports. It is the curl-equivalent for the protocol and the fastest way to isolate bugs.
