Field guide · 14 sections · 9 diagrams

MCP, explained the way it should have been the first time

The Model Context Protocol is a small idea wearing unusually large clothes. Strip the acronym away and it is this: a standard plug so that any AI application can use any tool or data source without a custom adapter for each pairing. What follows is the full mental model — the analogies that make it click, the machinery underneath, and the places where the honest answer is "it depends".

Open standard · MIT SDKs JSON-RPC 2.0 Human stays in the loop Spec version 2026-07-28

🧪 Companion lab — build a server, then try to make it betray you →

The one-sentence version

MCP is a contract that lets an AI application discover what a service can do, then call it — in a shape every AI application already knows how to speak.

Three verbs carry the whole protocol: list what exists, read the context you need, call the action you want. Everything else — transports, auth, notifications — is plumbing around those three.

Two analogies, and why you need both

The official one · USB-C MCP is a USB-C port for AI applications. Before USB-C, every device had its own cable; a drawer full of chargers. One port, many devices.
Eli's one · a REST API for AI "MCP is like a REST API for AI Models. You make a request, your model determines the best tool for answering your question, and then that is used for your query."

The USB-C analogy explains why the standard exists (fewer cables). Eli's REST analogy explains how it behaves (a request the model chooses to make). The second one is the one that will actually help you build.

The problem

Before MCP: the N×M integration tax

A useful model is a model with context. But every model vendor, every IDE and every assistant used to need its own private glue for every data source. Integrations multiply, and the multiplication is the whole problem.

Before — pairwise adapters N × M

4 AI applications × 5 data sources = 20 bespoke connectors. Each one maintained, versioned, and broken independently.

AI applications data sources Claude ChatGPT VS Code Your app Files Postgres GitHub Calendar Slack 20 connectors to write, test and keep alive
Each line is bespoke glue: its own auth, its own error handling, its own schema drift.

After — one protocol N + M

The same 4 applications and 5 sources need 9 implementations: each app speaks the protocol once, each source speaks it once.

MCP one contract Claude ChatGPT VS Code Your app Files Postgres GitHub Calendar Slack 9 implementations, and any new source plugs into all 4 apps at once
The saving is not the lines — it is that adding source number six costs one implementation, not four.
"Every new data source requires its own custom implementation, making truly connected systems difficult to scale." — the problem statement MCP was created to answer. Anthropic · Introducing the Model Context Protocol

The framing that makes it click

"AI is just a stack"

This is Eli's central teaching move, and it is the most useful sentence in the whole class: an LLM is one component of a stack, not the product. People say "AI" and mean the model. What actually ships is the model plus everything around it — and MCP is a piece of that "everything around it".

The LAMP stack, 20 years ago

If you were building a web app, this is the pile you needed. The pieces were not interchangeable — but they were known, and knowing them is what made you employable.

PHPthe back-end language MySQLthe database Apachethe web server Linuxthe operating system
Four layers, one known shape. "I know the LAMP stack" was a complete sentence about a person's capability.

The AI stack, today

The model is layer three. Everything above and below it is also work — and this is where MCP sits: it is the shelf that tooling plugs into.

Interfacechat, IDE, agent Orchestrationprompts, loops, memory Modelthe LLM — one layer, not the product MCP tools · resources · prompts Infrastructurethe box, the network, the data
Highlighted layer = the subject of this guide. The model is the middle of five, not the whole sandwich.
"As a technology professional, we do not sell technology. Nobody gives a [damn] about your technology. What they care about is: are you solving problems?" Eli the Computer Guy · Intro to MCP

That is not a motivational aside — it is the selection criterion. MCP earns its place in the stack only if wiring a tool to a model solves someone's problem faster than not doing it.

Anatomy

Host, client, server — and why the middle one exists

Three roles. The confusing one is client, because it is not "the user" and it is not "the app" — it is a small connection object the app creates, one per server.

Host

The AI application you actually use — Claude Desktop, Claude Code, VS Code, Cursor, your own agent. It owns the conversation, decides what to show the model, and enforces the permission rules.

One host. It coordinates.

Client

A connector object inside the host, one per server, holding exactly one dedicated connection. It speaks the protocol so the host does not have to.

The host makes four clients for four servers.

Server

A program that exposes tools, resources and prompts. It can run on your laptop (stdio) or on the internet (Streamable HTTP). "Server" is not a location, it is a role.

Anything you can write in Python can be one.

MCP Host the AI application · the LLM lives here Client 1dedicated connection Client 2dedicated connection Client 3dedicated connection …one client per server it wants to use Filesystem server local · stdio · same machine Database server local · stdio · same machine Sentry server remote · Streamable HTTP · OAuth …any number, local or remote JSON-RPC · stdio JSON-RPC · HTTP POST + SSE
The transport is the only difference between a "local" and a "remote" server — the messages on the wire are the same JSON-RPC either way. That is the design goal: one message format, many pipes.
Why an extra layer at all? Because one client per server keeps failure isolated and permissions scoped. A server crashing takes down its client, not the host. A server's permissions are its own, not the application's.

The three primitives

Tools, Resources, Prompts — and who is in charge of each

This is the table worth memorising. The distinguishing question is never "what data does it expose" but who decides when it gets used.

PrimitiveWhat it isWho decidesTypical methodsEli's example
Tools Executable functions with typed inputs. Can write, call, modify, trigger. the model tools/list
tools/call
"A tool would be a REST API call going out to weather and pulling that information back." Or ping, or system-health.
Resources Read-only context addressed by URI, with a declared MIME type. Files, schemas, records. the application resources/list
resources/read
resources/templates/list
"Resources… this is read-only information." His demo: hours://business returning opening hours.
Prompts Reusable, parameterised instruction templates the server author curates. the user prompts/list
prompts/get
Surfaced in clients as slash commands: type / and pick one.

Why the "who decides" column matters

It tells you where the risk lives. A tool is the only primitive that can act, and the model picks it — so a tool is where an untrusted input can turn into a side effect. Resources are read-only and the app chooses them. Prompts are inert until a human clicks one.

Rule of thumb: put capability in tools, put knowledge in resources, put repeatable judgement in prompts.

The three at a glance

Tools model decides Resources app decides Prompts user decides one server can expose all three

What a tool actually looks like on the wire

A tool is a name, a description and a JSON Schema. The model never sees your Python — it sees this.

tools/list → one entryJSON
{
  "name": "searchFlights",
  "description": "Search for available flights",
  "inputSchema": {
    "type": "object",
    "properties": {
      "origin":      { "type": "string", "description": "Departure city" },
      "destination": { "type": "string", "description": "Arrival city" },
      "date":        { "type": "string", "format": "date" }
    },
    "required": ["origin", "destination", "date"]
  }
}

That description string is doing more work than it looks like. It is the only thing the model reads when deciding whether to call your tool — which is why "tool poisoning" (see Security) is a real attack class, and why Eli's class spends time on rewriting descriptions when you swap models.

The conversation

What actually happens when you ask a question

Five messages, then an answer. The part beginners miss: the model does not call your tool. It asks the host to, and the host decides whether to obey.

You Host + model MCP client MCP server 1 · "what's the weather in Paris?" 2 · initialize → negotiate version + capabilities 3 · tools/list tool schemas returned 4 · tools/call { name, arguments } result (text / structured data) 5 · result injected as context 6 · answer, now grounded in real data the MODEL chooses the tool — the HOST performs the call
Steps 2–4 are the protocol. Step 5 is where people get confused: the tool result is just more text in the model's context. MCP moves data; the model still does the reasoning.

The handshake, as Eli describes it

"Initializes the client, communicates with the server, basically make sure that the server is there, gets back the protocol, different capabilities… the initialized response from the server comes back to the client." Eli the Computer Guy · Intro to MCP

Version and capability negotiation. This is the step that lets an old client talk to a new server without either one guessing.

The same exchange as JSON-RPC

client → serverJSON-RPC 2.0
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "checkWeather",
    "arguments": { "city": "Paris" }
  }
}

Every method in the protocol is just a method string on this envelope. There is no second protocol to learn.

Transports

Two pipes, one message format

The transport is how bytes get from client to server. It changes deployment, not semantics — the same JSON-RPC goes down either pipe.

 stdioStreamable HTTP
How it connectsHost launches the server as a child process; messages over stdin/stdoutHTTP POST for client→server; optional Server-Sent Events for streaming back
Where it runsSame machine as the hostAnywhere — another container, another continent
Who it servesTypically a single clientTypically many clients
AuthNone needed — it inherits the user's own privilegesBearer tokens, API keys, custom headers; OAuth recommended
LatencyNo network hopNetwork hop + TLS + auth
Right forLocal tools: files, shell, local DB, system healthShared services: SaaS APIs, team servers, hosted tooling

The security consequence of the choice

A stdio server is you — same filesystem, same credentials, same rights. A remote server is someone else's program with its own tokens, and now every question about trust, scope and revocation applies. Most MCP security incidents in the wild live on the remote side of this line.

The code

A server is a function with a docstring

The SDK's own words: you write no JSON Schema, no request parsing, no validation code, no protocol handling. Two type-hinted Python functions and a docstring. Everything below is lifted from Eli's class repository and the official SDK.

server with two tools — Eli's mcp-server-basic.pyPython
from datetime import datetime
from mcp.server.mcpserver import MCPServer

mcp = MCPServer("time-server")

@mcp.tool()
def get_time() -> str:
    """Return the current time (HH:MM:SS)."""
    return datetime.now().strftime("%H:%M:%S")

@mcp.tool()
def get_day_month_year() -> str:
    """Return the current day of the week."""
    return datetime.now().strftime("%A, %B, %Y")

if __name__ == "__main__":
    mcp.run()

The decorator IS the schema declaration: () -> str tells the protocol the signature, the docstring becomes the description the model reads.

a resource, not a tool — Eli's mcp-server-resource-hours.pyPython
from mcp.server.mcpserver import MCPServer

mcp = MCPServer("Business Hours Server")

@mcp.resource("hours://business")
def get_business_hours() -> str:
    """Provides the current hours of operation."""
    return (
        "Store Hours of Operation:\n"
        "Monday - Friday: 9:00 AM - 6:00 PM\n"
        "Saturday: 10:00 AM - 4:00 PM\n"
        "Sunday: Closed"
    )

if __name__ == "__main__":
    mcp.run()

Note the URI scheme. hours://business is addressed like a document, because it is one — read-only, no arguments, the application decides whether to pull it in.

And a tool that actually does something

From Eli's mcp-server-ping.py — the shape of every useful tool: validate the input, do the work, return a string. Here the agent gets network tools without ever holding a shell.

excerptPython
IPV4_PATTERN = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")

@mcp.tool()
def extract_ip(text: str) -> str:
    """Find and return the first valid IPv4 address in a block of text."""
    for candidate in IPV4_PATTERN.findall(text):
        try:
            ipaddress.IPv4Address(candidate)   # rejects things like 999.1.1.1
            return candidate
        except ValueError:
            continue
    return "Error: no valid IPv4 address found in the text."

That validation line is the whole argument for MCP over "just give the model a shell". You expose one bounded capability, not a terminal.

The other end: a client in ten lines

official Python SDKPython
import asyncio
from mcp import Client

async def main() -> None:
    async with Client("http://localhost:8000/mcp") as client:
        result = await client.call_tool("add", {"a": 1, "b": 2})
        print(result.structured_content)   # {'result': 3}

asyncio.run(main())

A URL means Streamable HTTP. The same Client can launch a local server as a stdio subprocess instead. That is the entire local-vs-remote difference, from the caller's side.

The map is not the territory

Where the spec has moved since the class

An honest guide has to say this: the mental model you learn from a class filmed in October 2026 and the current specification are not the same document. The concepts below are stable; the wire has moved. Here is what changed, so a tutorial does not mislead you later.

What you learn in the class (and should keep)

  • Host / client / server roles
  • Three primitives, and who controls each
  • A handshake that negotiates version + capabilities
  • stdio for local, HTTP for remote
  • Anything you can code in Python can become a server
  • Human approval before a tool runs

None of this is wrong. It is the vocabulary you will use for years.

What the 2026-07-28 spec now says

  • Stateless. Every request carries the protocol version and the caller's capabilities in its _meta field — the server processes each request on its own.
  • server/discover is the mandatory request where a server advertises supported versions and capabilities. Clients may send it before anything else.
  • Notifications became subscriptions. A client opens subscriptions/listen with a filter (e.g. toolsListChanged: true) and gets tagged events on that stream.
  • Sampling is deprecated as of this protocol version.
Why say this at all? Because "it didn't work and the tutorial said it would" is almost always a version gap, not a mistake in your code. Pin the version you build against, and read the spec page for that version — the docs site serves a different URL per protocol version for exactly this reason.

Security

The part where you stop trusting the plumbing

MCP gives a model the ability to act. Everything interesting and everything dangerous follows from that. The official security guidance is not vague about this; here are the real named failures.

"I'm always going to say allow once, because again — the most dangerous finger in the entire world is yours." Eli the Computer Guy · on the approval prompt

1 · Confused deputy

An MCP proxy server holds one static client ID with a third-party API. It lets clients register dynamically, and the third party drops a consent cookie after the first approval. An attacker then sends you a link with their redirect URI; your browser still has the consent cookie, so the consent screen is skipped and the code lands on attacker.com

Mitigation: per-client consent stored per user, exact-match redirect URIs, single-use state issued only after consent.

2 · Token passthrough

A server accepts a token from a client without verifying it was issued to that server, then passes it downstream. The token's audience is never checked, so a token minted for one service becomes a key to another.

Mitigation: validate audience on every token; never forward a client's token as your own credential.

3 · Omnibus scopes

The spec's own "common mistakes" list is blunt: wildcard scopes like *, all, full-access, and publishing your entire scope catalog in every challenge. Consent fatigue then trains users to click approve.

Mitigation: request the scopes the operation needs, log elevation events, let users down-scope.

4 · The one that follows from the design: poisoned tool descriptions

Not from the OAuth section — from the primitives. The model decides whether to call a tool by reading its description string. That string is attacker-controlled if the server is. A malicious or compromised server can therefore ship a tool whose description steers the model, and no amount of schema validation catches it, because the schema is valid.

Which is why the approval prompt must show what will run and with what arguments, not just a tool name — and why "allow always" is the setting that converts one bad click into a standing grant.

Where the exposure concentrates author's assessment of *impact × likelihood* for a small self-hosted deployment — reasoning, not a measurement Poisoned tool description Confused deputy (OAuth proxy) Omnibus / wildcard scopes Token passthrough highhigh medmed
The bars are an opinion, labelled as one. The named attacks and their mitigations above them are not.

Four lenses, one subject

Ask the same protocol four different questions

A single explainer serves one reader. This is the same material interrogated from the perspectives that actually decide whether a thing gets adopted — learner, builder, security reviewer, architect.

Perspective 1 · the learner

"What do I actually have to remember?"

Three roles, three primitives, two transports, one envelope. If you can say "the host makes one client per server; the model picks a tool and the host runs it; tools act, resources inform, prompts wait", you know more than most people deploying this.

Common misconception to drop: MCP does not make the model smarter, and it does not give the model memory. It gives it reach.

Perspective 2 · the implementer

"Where will my afternoon go?"

Not into the protocol. The SDK removes it: a decorator and a docstring. Your afternoon goes into tool design — naming, granularity, error text the model can act on, and the schemas that stop it calling your tool with nonsense.

A tool that returns "Error" teaches the model nothing. A tool that returns "no valid IPv4 in the text, expected dotted-quad" teaches it to retry correctly.

Perspective 3 · the security reviewer

"What can this reach that it shouldn't?"

A stdio server inherits your whole user account. A remote server brings tokens you can't revoke locally. Every tool is an action surface, and the model choosing it means an untrusted document can influence which action fires.

The questions to ask: what is the blast radius of this one tool, who can change its description, and is "always allow" ever on?

Perspective 4 · the architect / skeptic

"Why not just an API?"

Often you should. A REST API with a typed client is simpler, cacheable and debuggable. MCP pays off when many hosts must speak to many servers and you refuse to write the glue twice — and when you want discovery at runtime instead of a compile-time dependency.

If exactly one application talks to exactly one service, MCP is ceremony. (See the next section.)

Judgement

When MCP is the wrong answer

A guide that only argues for its subject is marketing. These are the cases where plain code wins, and saying so is the difference between a protocol and a religion.

Use plain function calling or a REST client when…

  • One app, one service. You are writing a bespoke integration anyway; a typed API client is less machinery.
  • The tool set is fixed at build time. Runtime discovery buys you nothing if the list never changes.
  • Latency is the product. An extra protocol hop and a schema round-trip cost milliseconds that a chat UI never notices and a trading loop always does.
  • You need caching, retries or batching as first-class features. HTTP gives those to you; MCP does not.

Reach for MCP when…

  • Many hosts × many sources. The N+M argument is the entire point.
  • You want one server to serve Claude, an IDE and your own agent.
  • The capability should be discoverable at runtime — tools that come and go with permissions, not a pinned client library.
  • You want a bounded capability instead of a shell. "Here is a tool that validates an IP address", not "here is root".

The one-line test

Would you write this integration more than once, for a tool you don't control? If yes — MCP. If no — a function call.

Check yourself

Eight questions that prove you understood

Click a card to reveal the answer. If any of these surprise you, the section above it is worth a second read.

Provenance

Sources

Everything factual above traces to one of these. Quotations are verbatim from the transcript; diagrams are redrawn from the concepts, not copied.

  1. Eli the Computer Guy — Intro to MCP: Model Context Protocol for AI Tool Use (published 17 Oct 2026, 31,806 views). youtube.com/watch?v=TXrZ0ymjbZs — the class this guide is built around. Transcript obtained via youtubetotranscript.com (YouTube blocks this host's IP range directly).
  2. Silicon Dojo — class workbook and code samples, MCP - Model Context Protocol/. github.com/SiliconDojo/In-Person-Classes — the time server, ping tools and hours resource code above are his.
  3. MCP documentation — What is the Model Context Protocol. modelcontextprotocol.io/docs/getting-started/intro — the USB-C analogy, and the list of clients that support MCP.
  4. MCP documentation — Architecture overview. modelcontextprotocol.io/docs/learn/architecture — participants, the two layers, statelessness, primitives, subscriptions.
  5. MCP documentation — Understanding MCP servers. modelcontextprotocol.io/docs/learn/server-concepts — the tools/resources/prompts table including "who controls it", and the protocol operations.
  6. MCP documentation — Security best practices (protocol 2026-07-28). modelcontextprotocol.io/…/security_best_practices — confused deputy, token passthrough, consent and scope requirements.
  7. MCP Python SDK (MIT). github.com/modelcontextprotocol/python-sdk — the 15-line server and 10-line client, and the "what you did not write" framing.
  8. Anthropic — Introducing the Model Context Protocol. anthropic.com/news/model-context-protocol — the origin: open-sourced, created by David Soria Parra and Justin Spahr-Summers, early adopters Block and Apollo.
How this was researched. The video was transcribed and mined for what it actually asserts; the primary documentation was then read for the same concepts so the guide could distinguish what the class teaches from what the current specification says; and the material was re-interrogated from four stakeholder perspectives to surface the trade-offs a single narrative hides. Where a claim could not be traced to a source — the risk chart — it is labelled as judgement rather than evidence.