insideout Get Pro

15 · MCP end to end

Pro

Agents, lesson 15

MCP end to end

The messages between an agent and its tools, and how they get attacked.

Many agents reach their tools through MCP, so when a call goes wrong you read its messages. "Designing tools a model can use" showed tools/list and tools/call, and "The harness around a coding agent" showed what a server can reach. Here you follow the messages, in the current spec, and the attacks on them.

By the end, you can:

  1. Trace one tool call between the model, the host and a server.
  2. Read a request in the current spec, and say how it reaches a local or remote server.
  3. Follow a call that stops to ask the user, and say what each kind of ask risks.
  4. Spot a poisoned, changed or shadowing tool description, and name the host's defences.

Part 1. Four parties, one tool call

Trace one tool call between the model, the host and a server.

You ask a chat app with a GitHub MCP server to "file a bug: the export button does nothing in Safari". Four parties take part: you, the app, the model behind it and the server. Follow the messages: the model and the server never talk.

Example 1.1

One bug report, message by message

The app has one MCP server, offering create_issue(repo, title). Count the messages between the app, the model and the server, from connecting until you read the reply. How many are MCP?

  1. List the toolsapp → server: tools/list, and its response

    Sent before you type a word. The response holds each tool's name, description and schema.

  2. Offer the toolapp → model: your message + the tool's definition

    Over the model provider's own tool-calling API, not MCP. The model never learns which server it came from.

  3. Let the model pickmodel → app: create_issue("acme/web", …)

    Only a proposal. The app can check your permissions, or ask you.

  4. Call the serverapp → server: tools/call, and its response

    The server opens the issue: "Created acme/web#482".

  5. Hand back the resultapp → model: result; model → app: reply

    The model writes "Done, I filed #482."

Answer8 messages, and 4 are MCP: a request and a response for tools/list, and the same for tools/call.

Message 5 and its response, in the current spec
{"jsonrpc": "2.0", "id": 2, "method": "tools/call",
 "params": {"name": "create_issue",
            "arguments": {"repo": "acme/web", "title": "Export button does nothing in Safari"},
            "_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28",
                      "io.modelcontextprotocol/clientCapabilities": {}}}}

{"jsonrpc": "2.0", "id": 2,
 "result": {"resultType": "complete", "isError": false,
            "content": [{"type": "text", "text": "Created acme/web#482"}]}}

That split is the point. The app turns each server's tool definitions into its model's format, and the model's choice back into tools/call, so one server works with any app and any model. The server sees only its own calls, never the conversation.

ModelHostone client per serverServergithub1tools/list2create_issue, …3message + tool4create_issue(…)5tools/call6“Created #482”7the result8“Done, filed #482”the model's own APIMCP
Figure 1The 8 messages of the bug report. Only the right half is MCP, and no line joins the model and the server.

Host, client and server

The host is the app you use: a chat app, an IDE, an agent. It holds the conversation, calls the model, and runs one MCP client per server it connects to. A server offers tools, resources and prompts. The model only talks to the host.

model ↔ host (one client per server) ↔ server

Failures come back two ways. A tool the server doesn't have gets a JSON-RPC error. A tool that ran and failed, say on a past date, returns a result with isError: true, which the host passes to the model to fix its call.

Your turn

The app already has the tool list. You ask it to comment on the Safari bug that it's fixed. The model calls search_issues, then add_comment. How many MCP messages are sent, and how many times is the model called?

Each tools/call is a request and a response: 2 × 2 = 4. The model picks search_issues, then add_comment, then writes the reply: 3 calls. 6 lists the tools again.

Part 2. Every request stands alone

Read a request in the current spec, and say how it reaches a local or remote server.

Keep going with Pro

You've done the first part. Pro opens the rest of this lesson, and every other one.

Still to come
  1. Every request stands alone
  2. When the server needs more
  3. How it gets attacked

Agents overviewSpotted a mistake? Tell us.