You've connected servers. You've called tools. This is the rest of it: the handshake you never watched, the primitives nobody ships, what your tool schemas silently cost, and the attack surface hiding in plain text.
A model in a box can't read your Jira, query your database, or open a PR. Tools are how it reaches out — and before MCP, every app wired up every tool by hand. That's the whole problem in one picture.
One protocol, both sides. A host
(Claude, ChatGPT, Cursor, your own app) contains an MCP client;
it speaks MCP to a server you or a vendor wrote. Write the server
once — every host can use it. Two transports worth knowing:
stdio for local processes,
Streamable HTTP for remote. People call it "USB-C for AI tools",
and for once the analogy holds.
Worth 90 seconds even for people who ship this daily, because the whole security half of this talk hangs off one detail in step 2.
The detail: in step 2 the model does not run anything — it emits JSON that asks for a call. Your host decides whether to execute it. That asymmetry is the only reason guardrails are possible at all, and we spend the back half of this talk on it. The flip side: everything the model knows about a tool — its name, its description, its schema — is text somebody else wrote, sitting in your context window.
Every session used to start with a conversation you've probably never read. It's JSON-RPC 2.0 — and the interesting part is that both sides declare capabilities. Yes, the client too. Learn it, because it's what every server in production speaks right now — then watch the 2026-07-28 spec delete it.
What actually changed on 2026-07-28:
initialize and notifications/initialized
are gone, and so is Mcp-Session-Id —
every request now carries its own protocol version, client info and capabilities in
_meta, so any instance behind a round-robin load balancer can
answer it. Servers MUST implement server/discover
so a client can still ask "what do you support?" up front. And server→client requests no
longer hold a stream open: the server returns
resultType:"input_required" and the client
retries the same call carrying the answers (MRTR).
Ask someone what an MCP server does and they'll say "it has tools". The protocol grew six interaction primitives — and three of them flow in the opposite direction: the server calls you. The 2026-07-28 spec deprecated two of them, which makes this the best possible moment to learn what they were for.
tools/call — model-controlled actions. The one you know.
resources/read — data by URI, host-controlled, subscribable.
Context without burning a tool call.
prompts/get — user-invoked templates. Slash commands in your
editor? Often these.
sampling/createMessage — the server borrows your LLM.
No API key on the server. Migration: call an LLM provider API directly.
elicitation/create — mid-call, the server pauses and asks the user
for structured input. The only surviving client feature.
roots/list — the client tells the server which directories are
in scope. Migration: pass paths as tool params or resource URIs.
The nugget, and the plot twist. The nugget: a server needing an LLM step — summarize, classify, extract — could borrow the client's model instead of holding an API key. The twist: SEP-2577 deprecated Sampling, Roots and Logging in the 2026-07-28 revision. They keep working for at least twelve months under the new lifecycle policy, but new code shouldn't adopt them. Elicitation survives — and it's still the one almost nobody ships. That's your differentiator now.
Every connected server injects its tool schemas into the model's context — on every single request. Watch a typical five-server setup load, then watch the same setup with deferred tool discovery.
Two costs, not one. The obvious one is tokens —
you pay it on every turn, cache or not it occupies the window.
The sneaky one is accuracy, and it's measured: on Anthropic's internal MCP evals with
large tool libraries, deferring tool loading moved Opus 4.5 from
79.5% → 88.1% (Opus 4: 49% → 74%). Fewer, task-shaped tools beat
one-tool-per-endpoint.
Hosts are shipping the other fix — deferred loading / tool search — today, and the
2026-07-28 spec helps: tools/list results now carry
ttlMs + cacheScope so clients can
cache them, and servers should return tools in a
deterministic order — same bytes every turn means your prompt
cache actually hits.
Illustrative: tool counts are the right order of magnitude for these
servers (they change release to release); token sizes are typical schema measurements
(~350–700 tok/tool), rounded. Measure your own setup.
The newest consumption pattern: instead of the model ping-ponging tool calls through its context, it writes a script that calls the same MCP tools inside a sandbox. Intermediate data never touches the context window.
Anthropic's published example (gdrive sheet → filter → salesforce update): ~150,000 tokens as chained tool calls, ~2,000 as generated code — loops, retries and batching come free. MCP still provides the connectors; code provides the composition. Not either/or.
If you formed your mental model in early 2025, it's stale. A sweep through what actually changed on the wire:
Bonus trivia for the pedants: JSON-RPC batching was
mandated in 2025-03-26 and removed three months later in
2025-06-18 — the shortest-lived feature in the spec. Two things to act on:
the old HTTP+SSE transport is now formally Deprecated with a twelve-month clock, and
ping, logging/setLevel and
notifications/roots/list_changed were
removed outright. Note six revisions in twenty months —
versioning is not optional here.
An agent becomes dangerous when three things combine. None of them is a bug; each is a feature you asked for. And the most elegant delivery mechanism for the attack is a field you have never read.
Why this works: tool descriptions are instructions
to the model, delivered in a channel no UI shows. The spec agrees with you —
it says descriptions and annotations "should be considered
untrusted" unless the server is. Combine that with a re-fetched
tools/list — a server you approved on Monday can
swap its descriptions on Friday (the "rug pull") — and note
that nothing in the 2026-07-28 rewrite fixes this; it's your side of the line.
Injection filtering is not a solved problem;
the fix is breaking the trifecta, not detecting the attack.
PoC pattern: Invariant Labs, 1 Apr 2025 — an add tool
whose hidden block exfiltrates ~/.ssh/id_rsa and
~/.cursor/mcp.json through a sidenote argument.
You can't filter your way out. You can put every tool call through gates that don't rely on detecting cleverness:
The mental shift: treat an agent like a
new employee with a laptop — identity, scoped access, monitoring —
not like a library you npm install. Every gate above is
boring, deterministic infrastructure. That's the point.
Three scenarios, one decision rule. Watch where each one lands.
Start with a CLI. Wrap it as MCP when its assumptions — a shell and a technical user — stop holding. Add code execution when tool traffic gets heavy. Rungs of one ladder, not competing religions.
The dense half of the deck, compressed. Check off what was new:
Capabilities are declared both ways — the client too, and every feature is gated on it. As of 2026-07-28 that declaration rides in _meta on every request instead of a one-time handshake.
Elicitation is the primitive to adopt — ask the user for structured input mid-call, no custom UI. Sampling and Roots were deprecated on 2026-07-28; don't start new work on them.
Schemas cost you on every turn — a five-server setup can eat ~50k tokens before the first word, and too many tools degrades selection accuracy.
Code execution flips the loop — same MCP servers, ~98.7% fewer tokens when intermediate data stays in the sandbox.
Tool descriptions are attack surface — the model reads text you never see, and an approved server can change it later. The spec itself says to treat them as untrusted.
Break the trifecta, don't filter — remove or gate one of: private data, untrusted content, exfiltration channel. Per deployment.