Model Context Protocol from the ground up
Why a protocol
A model that can call tools is only half a system. The other half is an agreement about what exists and how to ask for it, so any host can use any server without a bespoke adapter.
The adapter tax
Every tool-using assistant starts with a private integration. The editor can read a file because someone wrote that code; the chat app can query the issue tracker because someone wrote a second version; the agent runtime can do both because it borrowed one and reimplemented the other. Each adapter is small on its own, and the number of them grows with the product of hosts and tools, which is the expensive shape.
The Language Server Protocol made this argument for editors and languages and settled it. A language server speaks one protocol, an editor speaks one protocol, and the pair works without either side knowing the other’s internals. MCP is the same arrangement for model-facing capabilities: one wire, many hosts, many servers, and no adapter written twice.
The cost is not only the code. Every adapter has its own authentication story, its own pagination, its own error shape and its own way of being subtly wrong. A protocol does not remove that work; it moves it into one place where it can be done once, reviewed once and tested against every host that speaks it.
Three roles, one wire
The host is the application a person is using: an IDE, a chat client, an agent runtime. It owns the conversation, the model, the permissions and the interface. The client is the connector inside the host, one per server, and it is the part that actually speaks the protocol. The server is the program that exposes capabilities, whether that is a filesystem, a database or a ticket system.
The model is deliberately not a role. It sits inside the host and emits a request to call a tool; the host decides whether to forward that request. The extra hop is where policy lives. A protocol that handed the model a socket would have removed the only place where a decision can be made.
A host can run several clients at once, one per server, while the user sees a single assistant. That fan-out is why the host, not the server, has to hold the whole picture: a server knows its own capability and nothing about the others, and it should not need to.
The first exchange
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {"roots": {"listChanged": True}},
"clientInfo": {"name": "demo-host", "version": "1.0"},
},
}
response = {
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {"tools": {"listChanged": True}},
"serverInfo": {"name": "demo-server", "version": "0.1"},
},
}A server with two methods
Run the handshake, then add a tools/list branch that returns an empty list. Notice that the unknown method gets a protocol error rather than a crash.
What the protocol does not decide
MCP standardises discovery and invocation: what exists, what arguments it takes, what came back, and how to say that something failed. It does not standardise the prompt, the model, the interface, the authorisation model or the retry policy. Two hosts can speak the same protocol and behave completely differently, which is the point. The agreement is narrow enough to implement and wide enough to be useful.
A server is also not a sandbox. A stdio server is a process on the user’s machine with the user’s privileges. An HTTP server is a remote service holding whatever credentials it was given. The protocol tells you how to ask a question; it does not tell you whether the answer is safe to act on. That judgement stays with the host, and the guardrails course is about making it.
The useful test is to ask what a new server must implement to be useful. If the answer is a transport, a handshake and two methods, the protocol is doing its job. If the answer is a page of product decisions, then the protocol has quietly become a framework.
Read the wire before you read the SDK. A frame you can write by hand is a frame you can debug when a server and a client disagree about what was sent.
Why not just an SDK
An SDK is one implementation of an idea. A protocol is the idea, written down well enough that two implementations can disagree about everything else and still interoperate. A Python server written with the official SDK has to work with a TypeScript host written by someone else, and it does, because the wire format is the contract rather than the library.
That is also why the protocol is versioned by revision rather than by major number. Each revision names a date, adds methods and fields, and keeps the old ones working where it can. The negotiation in chapter seven is how two ends discover which revision they share before either assumes that a method exists.
The same split appears in HTTP. The request line and the headers are the contract; a client library is a convenience. You can debug an HTTP exchange with a socket, and you can debug an MCP exchange with a JSON file and a pipe, which is exactly what the examples in this course do.
Ids and notifications
Send a call and then a cancellation notice. Confirm that the call carries an id and the notification does not, which is what lets a server answer one and ignore the other.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does MCP standardise?
