Skip to content
Level 5 · Agentic EngineerLessonPart 24 · page 3 of 730 min
30Minutes
12Sources

Model Context Protocol: Servers, Clients and Transports

The tools in the previous lesson were functions inside one program. That is fine until you want the same three tools in your own agent, in an editor and in a desktop chat application, at which point you are writing them three times. The Model Context Protocol exists to make a tool something you write once and connect to anything.

By the end of this lesson you will be able to name the three roles the protocol defines, choose between its two transports for a given deployment, say what a resource and a prompt are and why they are not tools, read a specification version and know what it commits you to, and work through a checklist before letting a third-party server anywhere near your machine.

The specification’s overview is short about this, and precise. The protocol “uses JSON-RPC 2.0 messages to establish communication between” three things:

  • Hosts: “LLM applications that initiate connections”
  • Clients: “Connectors within the host application”
  • Servers: “Services that provide context and capabilities”

Where each role sits when your agent talks to a tool server

  1. HostThe application the person is using: your agent script, an editor, a desktop chat app. It holds the model connection, the conversation, and the decision about what runs.you are here in the labs
  2. ClientOne connector per server, inside the host. Speaks JSON-RPC over a transport, lists what the server offers, and calls it. A host with four servers has four clients.
  3. Transportstdio for a subprocess on the same machine, Streamable HTTP for a service you deploy. The protocol semantics are identical on both.
  4. ServerA separate program exposing tools, resources and prompts. It knows nothing about the model, the conversation, or which host is calling.
  5. The actual capabilityA filesystem, a database, an API, a search index. The part with consequences.
The model never talks to a server. The host reads the server's tool list, gives it to the model as ordinary tool schemas, and calls the server when the model asks for one.

The separation is the whole value. A server written once is reachable from every host that speaks the protocol, and the host keeps the parts that need judgement: which servers to trust, what to show the user, and what to run without asking.

The transports overview names exactly two standard bindings and is explicit that they change nothing about meaning: “Protocol semantics are identical on every transport. A transport is a binding.”

stdio is the one you will use in the lab. The client launches the server as a subprocess and they exchange newline-delimited JSON-RPC over its standard streams. The rules are short enough to quote in full, and two of them cause every stdio bug there is: “The server MUST NOT write anything to its stdout that is not a valid MCP message”, while “The server MAY write UTF-8 strings to stderr for any logging purposes”. A stray print() in a server breaks the connection; the same message on stderr is fine. Shutdown is by closing the server’s standard input, and servers “SHOULD exit promptly when their standard input is closed”.

Streamable HTTP is for a server that runs as its own process and serves several clients. The server exposes “a single HTTP endpoint (the MCP endpoint) that accepts POST”, each JSON-RPC message is its own POST, and the server answers “with either a single JSON object or a Server-Sent Events (SSE) stream scoped to that request”.

Its security section is three normative sentences that should be read as a deployment checklist:

  1. “Servers MUST validate the Origin header on all incoming connections to prevent DNS rebinding attacks.”
  2. “When running locally, servers SHOULD bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0).”
  3. “Servers SHOULD implement proper authentication for all connections.”

The specification states the reason plainly: “Without these protections, attackers could use DNS rebinding to interact with local MCP servers from remote websites.” A page you visit in a browser can be made to talk to a service listening on your own machine. Part 10’s lesson on serving beyond localhost made the same argument about model endpoints; it applies with more force here, because a tool server is by definition a thing that does something.

Servers offer three kinds of thing, and the specification separates them by who decides to use them. Servers offer “Resources: Context and data, for the user or the AI model to use”, “Prompts: Templated messages and workflows for users” and “Tools: Functions for the AI model to execute”.

Primitive Who chooses Typical example
Tool The model, during a loop Search the index, run the tests, read a file
Resource The application, on the user’s behalf A file’s contents, a database row, a document by URI
Prompt A person, by name A slash command or menu item that fills in a template

The distinction is not decoration. Tools are “model-controlled”, and the specification puts a normative warning next to that: “For trust & safety and security, there SHOULD always be a human in the loop with the ability to deny tool invocations.” Applications “SHOULD provide UI that makes clear which tools are being exposed to the AI model”, insert visual indicators when tools are invoked, and present confirmation prompts. Resources and prompts do not carry that warning, because nothing in them is chosen by a token predictor.

Two details of the tool definition matter when you write one in the second lab. The inputSchemaMUST be a valid JSON Schema object (not null)”, and for a tool with no parameters the recommended form is { "type": "object", "additionalProperties": false }. And errors come in two flavours: protocol errors, for an unknown tool or a malformed request, and tool execution errors returned inside a normal result with isError: true. The specification’s guidance on which to use is about what the model can do with them, since clients “SHOULD provide tool execution errors to language models to enable self-correction”. An invalid date should come back as a result saying so, not as a JSON-RPC error.

The specification has a date, and the date is the version

Section titled “The specification has a date, and the date is the version”

MCP versions are dates. The versioning page defines the format as YYYY-MM-DD, “to indicate the last date backwards incompatible changes were made”, and notes that the version is not incremented for backwards-compatible updates. Revisions are marked Draft, Current or Final, and as read on 2026-09-09 the current version is 2026-07-28.

That revision was not a small one, which is a useful thing to know before you read older material. Its changelog lists, among its major changes, removing “protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport”, making the protocol stateless by removing “the initialize/notifications/initialized handshake” so that every request carries its protocol version in a _meta field, adding a mandatory server/discover RPC, replacing the HTTP GET stream with a subscriptions/listen request, and moving tasks into an official extension. It also deprecates the Roots, Sampling and Logging features, with the migration advice to “log to stderr (stdio) or use OpenTelemetry instead of Logging”.

Governance is worth two minutes because it tells you how fast this can change under you. The project “has been established as Model Context Protocol a Series of LF Projects, LLC”, with code and specification contributions under Apache 2.0 and documentation under Creative Commons Attribution 4.0. Technical governance is a four-level ladder of Contributors, Maintainers, Core Maintainers and Lead Maintainers, the last described as “the final decision makers”, and together they form the MCP Steering Group. Changes to the specification must be submitted as Specification Enhancement Proposals, the Core Maintainer group “meets every two weeks to discuss and vote on proposals”, and the 2026-07-28 revision added a feature lifecycle policy with “a minimum twelve-month deprecation window” and a registry of deprecated features.

So: a foundation-hosted specification, an open proposal process, a published deprecation window, and dated revisions that have recently made large breaking changes. Pin the revision your code speaks and read the changelog when it moves.

Everything above is the useful half. This is the half that decides whether you should connect a particular server at all.

A tool description is text the model reads. So is a tool result. Neither is under your control when the server is not yours, and a model has no mechanism for treating one span of its context as less authoritative than another. OWASP’s definition covers the general case: a prompt injection vulnerability “occurs when user prompts alter the LLM’s behavior or output in unintended ways”, with the indirect form arriving when a model “accepts input from external sources like websites or files”.

MCP adds two specific shapes to that.

Tool poisoning. Invariant Labs’ write-up of 1 April 2025 describes instructions embedded in tool descriptions that are “invisible to users but visible to AI models”. The asymmetry is the attack: “AI models see the complete tool descriptions, including hidden instructions, while users typically only see simplified versions in their UI.” You approve a tool called “add two numbers” after reading one line; the model reads four hundred words, the last of which tell it to read a key file and pass the contents as an argument.

Rug pulls and shadowing. The same write-up describes a server that can “change the tool description after the client has already approved it”, so approval is not a durable statement about what a tool does. And tool shadowing, where a description from one server “modifies the agent’s behavior with respect to a trusted service or tool”, lets a malicious server “override rules and instructions from other servers” without ever appearing in the user-facing log. This is why the specification says clients “MUST consider tool annotations to be untrusted unless they come from trusted servers”, and why “trusted” has to mean something you checked rather than something you downloaded.

Injection through results. Even a perfectly honest server returns content it did not write. A file, a web page, a database field, an issue comment: any of them can contain text addressed to the model. The second lab builds exactly this and tests whether your agent follows it.

Work through this before adding anything you did not write. It takes ten minutes and it is the only defence that acts before the model sees anything.

  1. Source. Who publishes it, and does the name match a real project? Read the repository, not the directory listing that links to it. Prefer something you can pin to a commit.
  2. Read the tool descriptions in full. Not the summary the host shows you. Open the source or dump tools/list yourself, and read every character of every description and every parameter description. This is where poisoning lives, and it is visible if you look.
  3. What does it read, and what does it write? List the files, directories, network hosts and credentials it touches. A server that needs your home directory to summarise a document is asking for more than the job needs.
  4. Transport and reach. stdio, or HTTP? If HTTP, does it bind to localhost, validate Origin and require authentication, as the specification says it should?
  5. Permissions it will run with. Your user, or a dedicated one? A container, or your machine? The MCP specification cannot enforce anything here and says so; the sandbox is yours to build.
  6. Does it complete the trifecta? With this server added, does the agent have private data, untrusted content and a way to send data out, all at once? If so, remove one of the three.
  7. Pin it, and re-check on update. Because descriptions can change after approval, an unpinned server is a standing invitation. Record the version you reviewed.

LM Studio’s own MCP page puts the short version bluntly: “Never install MCPs from untrusted sources”, because “some MCP servers can run arbitrary code, access your local files, and use your network connection”. That is the correct level of alarm, and it applies to every host.

Separate protocol discovery from permission to execute

Section titled “Separate protocol discovery from permission to execute”

Discovering a tool tells the host its name, description and input schema. It does not establish that the tool is trustworthy, that every user may invoke it or that its result should become an instruction. Treat tool descriptions and returned content as data from the connected server, with provenance retained.

Trace a minimal connection through initialisation, capability negotiation, tool listing and one call. Keep the protocol revision and transport explicit. For a local standard-input/output server, protocol messages must remain distinct from diagnostic output; for HTTP, endpoint authentication and session handling belong to that transport’s contract.

Before connecting the model, call the tool directly with valid and invalid arguments and verify the expected denial or error. Then test the model-mediated path. A host may aggregate tools from several servers, so use stable qualified identities to avoid ambiguity. The protocol standardises communication; the application still decides which server to trust, which resources a caller may access and whether a proposed action needs approval.

MCP defines hosts, which are the applications holding the model and the consent; clients, which are one connector per server inside a host; and servers, which expose capabilities and know nothing about the conversation. Two transports carry identical semantics: stdio, where the client launches the server as a subprocess and the only rules that matter are that stdout carries nothing but protocol messages and stderr carries the logs, and Streamable HTTP, whose specification requires Origin validation, recommends binding to localhost and recommends authentication. Servers offer tools chosen by the model, resources chosen by the application and prompts chosen by a person, and only the first of those carries a normative human-in-the-loop warning. The specification is versioned by date, currently 2026-07-28, governed through a Linux Foundation project with a public proposal process and a twelve-month deprecation window, and that revision removed the initialisation handshake and protocol-level sessions, so older material describes a different protocol. And connecting someone else’s server means importing text the model will read as instructions, in tool descriptions that can change after you approved them and in results that can contain anything, which is why the checklist and the lethal-trifecta test come before the install command.

Check your understanding

Question 1. Your MCP server over stdio connects, then the client reports a protocol error as soon as a tool runs. The tool works fine when you call the function directly. What is the most likely cause?
Show the answer and why

Answer: The tool prints to stdout, and the specification requires that a server write nothing to stdout that is not a valid MCP message

stdout is the protocol channel. A print(), a library banner or a progress bar lands in the middle of the JSON-RPC stream and corrupts it. The specification allows any logging on stderr, so send diagnostics there. This is the single most common stdio bug and it looks nothing like its cause.

Question 2. Which of these are stated requirements or recommendations for a Streamable HTTP MCP server in the current specification? Select all that apply.
Show the answer and why

Answer: Validate the Origin header on all incoming connections, Bind only to localhost rather than all interfaces when running locally, Implement authentication for all connections

The first three are the security section of the transport, aimed at DNS rebinding. The fourth was part of earlier revisions and was removed in 2026-07-28, along with protocol-level sessions; a server implementing only the current revision answers GET on the MCP endpoint with 405.

Question 3. A colleague sends you an MCP server that "just adds two numbers" and its published description is one line. What does the tool-poisoning research say you should do before connecting it?
Show the answer and why

Answer: Read the complete tool descriptions from the source or from tools/list, because models see the full text while users typically see a simplified version in the UI

The attack depends precisely on that asymmetry. Instructions hidden in a description are invisible in a host UI and fully visible to the model. It is also why approval is not durable: a server can change a description after you approved it, so pin the version you reviewed.

Question 4. An agent has a tool that reads your private notes, a tool that fetches arbitrary web pages, and a tool that posts to a chat channel. Why is this combination singled out?
Show the answer and why

Answer: It is the lethal trifecta: private data, untrusted content and an outward channel together mean a successful injection has both something to steal and a way to send it

Each capability is fine alone. Together they turn any injected instruction into exfiltration. The published advice is avoidance rather than mitigation, since no prompt-level defence is fully reliable: drop one of the three, or split them across agents that cannot reach each other.

Sources for this lesson

12 verified · checked 2026-09-09

  1. 01Model Context Protocol — Specification§ Overview; features; Security and Trust & Safetymodelcontextprotocol.io/specification2026-09-09
  2. 02Model Context Protocol — Versioning§ Revision states; the current version; negotiationmodelcontextprotocol.io/specification/versioning2026-09-09
  3. 03Model Context Protocol — Transports overview§ Standard bindings; messages; custom transportsmodelcontextprotocol.io/specification/2026-07-28/basic/transports2026-09-09
  4. 04Model Context Protocol — stdio transport§ Framing; stdout and stderr rules; shutdownmodelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio2026-09-09
  5. 05Model Context Protocol — Streamable HTTP transport§ Security and endpoint; sending and receiving messagesmodelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http2026-09-09
  6. 06Model Context Protocol — Tools§ User interaction model; tool definitions; error handling; security considerationsmodelcontextprotocol.io/specification/2026-07-28/server/tools2026-09-09
  7. 07Model Context Protocol — Key changes in 2026-07-28§ Major changes; deprecated featuresmodelcontextprotocol.io/specification/2026-07-28/changelog2026-09-09
  8. 08Model Context Protocol — Governance and stewardship§ Project policies; technical governance; SEPsmodelcontextprotocol.io/community/governance2026-09-09
  9. 09Invariant Labs — MCP security notification, tool poisoning attacks§ Tool poisoning; rug pulls; tool shadowinginvariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks2026-09-09
  10. 10Simon Willison — The lethal trifecta for AI agents§ The three capabilitiessimonwillison.net/2025/Jun/16/the-lethal-trifecta2026-09-09
  11. 11OWASP LLM01:2025 Prompt Injection§ Direct and indirect injection; preventiongenai.owasp.org/llmrisk/llm01-prompt-injection2026-09-09
  12. 12LM Studio — MCP§ Configuration; cautionslmstudio.ai/docs/app/mcp2026-09-09

Every technical claim on this page was checked against the official documentation of the tool, vendor or model publisher on the date shown, at the version pinned for the course. Where the course disagrees with folklore, the source is how you can tell which one to trust.