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
stdiovsstreamable-httptransport, and how state and reconnection differ between them - How session state works in
streamable-httpand the failure modes to design around
Read these first
Lesson outline
Every lesson from Model Context Protocol: Advanced Topics with our one-line simplification. The Skilljar course is the source; we summarize.
| # | Skilljar lesson | Our simplification |
|---|---|---|
| 1 | Let's get started! | Course framing: the four advanced surfaces are sampling, notifications, roots, and transports. Each fixes a real production gap. |
| 2 | Sampling | Sampling lets a server ask the client to make an LLM call on its behalf, so secrets and model choice stay on the client. |
| 3 | Sampling walkthrough | End-to-end: server requests sampling/createMessage, client prompts user (optional), invokes model, returns the completion. |
| 4 | Log and progress notifications | Notifications are one-way messages: notifications/message for logs, notifications/progress for percent-complete on long calls. |
| 5 | Notifications walkthrough | Hands-on: emit progress with a token from the request, stream log lines, watch them surface in the client UI. |
| 6 | Roots | Roots are filesystem boundaries the client publishes; the server enumerates them via roots/list and stays inside them. |
| 7 | Roots walkthrough | Wire a server that respects roots, listens for roots/list_changed, and refuses access outside the published set. |
| 8 | Survey | Mid-course feedback checkpoint; no technical content. |
| 9 | JSON message types | MCP rides JSON-RPC 2.0: requests have id and expect a response, notifications have no id and never get one. |
| 10 | The STDIO transport | stdio is the default for local servers: client spawns the server as a subprocess, messages flow over stdin/stdout, length-prefixed. |
| 11 | The StreamableHTTP transport | streamable-http is the remote transport: HTTP POST for requests, optional SSE for server-to-client streaming, sessions over Mcp-Session-Id header. |
| 12 | StreamableHTTP in depth | Walk through the headers, session lifecycle, GET-for-stream pattern, and resume tokens; this is the layer to debug when production breaks. |
| 13 | State and the StreamableHTTP transport | Sessions are sticky to a server instance; for horizontal scaling either pin sessions or externalize state to Redis or similar. |
| 14 | Assessment on MCP concepts | Final assessment covering all four advanced surfaces. |
| 15 | Wrapping up | Recap and pointers: combine these surfaces to ship a production-ready MCP server with security and observability. |
The course in 7 paragraphs
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.
The 4 advanced MCP surfaces and what each fixes
Every advanced MCP feature solves a specific production problem. Memorize the failure mode each one prevents.
- Sampling: the server delegates LLM calls
Server calls
Concept: mcp ↗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. - Notifications: server streams progress and logs
Concept: streaming ↗notifications/progress(with a token from the request) andnotifications/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. - Roots: filesystem security boundary
Client publishes a list of allowed directories. Server reads via
Concept: mcp ↗roots/list, listens forroots/list_changed, and refuses access outside. The client declares; the server enforces. Always assume the spec is advisory and write a hard check. - Transport: `stdio` vs `streamable-http`
Concept: mcp ↗stdiois local subprocess, single-client, simplest.streamable-httpis remote, multi-client, with session IDs and optional SSE streaming. Pickstreamable-httpwhen many clients share one server; pickstdiofor everything else.
3 transport pitfalls in production
When streamable-http MCP servers fail in production, it is almost always one of these three.
- Session affinity not enforced
Concept: session-state ↗Mcp-Session-Idties 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. - 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.
Concept: streaming ↗ - Treating `stdio` like a daemon
Concept: session-state ↗stdioservers 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.
6 takeaways with cross-pillar bridges
Sampling lets the server request LLM completions through the client, keeping API keys and model choice on the client side.
Use notifications/progress with a token from the request and notifications/message for logs whenever a tool call takes more than a few seconds.
Roots are the filesystem boundary the client publishes and the server is expected to enforce; never trust the spec alone, write a hard path check.
MCP rides JSON-RPC 2.0 with three message shapes: requests carry an id, responses match by id, notifications have no id and never get a response.
Choose stdio for local single-client servers and streamable-http only when multiple clients share one server; the latter introduces session-stickiness and reconnection.
streamable-http sessions are pinned to a specific server instance via Mcp-Session-Id; horizontal scaling requires sticky routing or externalized session state.
How this maps to the CCA-F exam
3 hand-picked extras
These amplify the Skilljar course beyond what the course itself covers. Each was picked for a specific reason.
Model Context Protocol: Specification
The authoritative spec covering sampling, notifications, roots, and both transports at the wire level. The Skilljar videos teach intuition; this is where you go to settle a debate.
Read source ↗Streamable HTTP transport: MCP docs
Deep-dive into headers, session lifecycle, SSE upgrade path, and resume semantics. The single best reference for debugging a streamable-http MCP server in production.
MCP Inspector
Official Inspector repo with usage examples for testing sampling, notifications, and both transports. Indispensable when you cannot reproduce a wire-level bug from a real client.
Read source ↗Concepts in this course
Model Context Protocol
Core protocol the course extends
Concept: mcp ↗Streaming
Mechanism behind progress and log notifications
Concept: streaming ↗Session state
How `streamable-http` tracks per-client context
Concept: session-state ↗Tool calling
Where sampling and notifications live in the loop
Concept: tool-calling ↗Where you'll see this in production
Other course mirrors you may want next
8 questions answered
Phrased as the way real students search. Tagged by intent so you can scan to what you actually need.
DefinitionWhat is MCP sampling and when should I use it?
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-toHow do I show progress for a long-running MCP tool call?
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.DefinitionWhat are MCP roots and who enforces them?
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.ComparisonShould I use stdio or streamable-http transport for my MCP server?
stdio does not have.TroubleshootWhy 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.ComparisonHow is an MCP notification different from a request?
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.ScopeDoes streamable-http require Server-Sent Events?
How-toHow do I test the advanced MCP features without writing a full client?
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.