all_lessons/agentic_systems/12 · mcplesson 13 / 25

Part IV - State, collaboration, and protocols

MCP - model context protocol as integration contract

Lesson 07 gave the agent tools — one model, one hand-wired list of callable functions. That works until you have ten agents that each need the same database, mail server, and file system. MCP is the standard that turns those one-off wires into a reusable, discoverable contract between any agent and any external system.

Book source
Chapter 10 - Model Context Protocol (模型上下文协议); PDF outline pages 120-130. The book frames MCP as a "universal adapter," walks the client-server interaction flow, contrasts it with plain function calling, and ships two runnable Google ADK examples: an ADK agent driving a filesystem MCP server, and a FastMCP greet server consumed over HTTP.
Linear position
Prerequisite: Lesson 07 (tool use / function calling) — you already know how one model is handed a typed list of callable functions and chooses one. Lesson 10 (memory) and lesson 11 (learning) so external state and traces have meaning.
New capability: a protocol boundary. Instead of baking each integration into each agent, you stand up servers that expose capabilities, and any compliant agent host discovers and consumes them through one stable contract.
The plan
Five moves. (1) Show the integration explosion that motivates MCP — the N×M problem — with real arithmetic, and the universal-socket analogy that dissolves it. (2) Pin down MCP's client-server architecture and its four roles (LLM, client, server, third-party service). (3) Define the three things a server exposes — resources, prompts, tools — because conflating them is the most common design error. (4) Contrast MCP with ordinary function calling feature by feature, so you know when each is the right tool. (5) Build out the realities the book insists on: discovery, transports, security, and the blunt warning that a bad API wrapped in MCP is still a bad API. We thread the running coding/research agent and the book's ADK + FastMCP code through all of it, then hand off to goal monitoring in lesson 13.

1 · Why a protocol — the N×M integration explosion

Picture the running coding/research agent from earlier lessons. To be useful it must touch real systems: the code repository, a test runner, a ticket tracker, a documentation store, maybe BigQuery for analytics. In lesson 07 you wired each of these as a function the model could call. That was fine for one agent. Now your organization has several agents — a coding agent, a research agent, a support triage agent — and they overlap heavily on which systems they need.

If every agent integrates every system directly, you build a separate adapter per pair. With N agents and M systems that is N × M adapters, each with its own auth handling, schema, error mapping, and retry logic — and each one a place to drift and rot. The book's image for the fix is a universal power-socket system: the socket does not supply tools itself, it defines a standard interface so any compliant appliance plugs into any compliant outlet. MCP is that socket for agents. Servers expose capabilities once; any compliant client consumes them. The wiring count collapses from N × M to N + M: each agent learns the protocol once, each system is wrapped once.

Worked number
Say you have N = 6 agents and M = 8 backend systems. Point-to-point, that is 6 × 8 = 48 bespoke integrations to write, secure, and maintain. With an MCP hub it is 6 + 8 = 14 — six clients that speak MCP plus eight servers that expose MCP. That is a 71% reduction in integration surface, and it gets better as you grow: at N = 10, M = 15 it is 150 vs 25 — an 83% cut. The savings are not just lines of code; each adapter you delete is auth logic, an error-translation layer, and a schema you no longer have to keep in sync.

Drag the sliders below to feel the scaling. The point-to-point count grows as a product; the MCP count grows as a sum. That gap is the entire economic argument for a protocol.

Integration surface: point-to-point (N×M) vs MCP hub (N+M)
Left: every agent wires directly to every system — one bespoke adapter per line. Right: agents and systems each speak MCP once and meet at the hub. Raise the counts and watch the point-to-point web explode while the hub stays linear.
Point-to-point N×M
20
MCP hub N+M
9
Adapters saved
11
Reduction
55%
Show the core JS
const pp  = N * M;          // bespoke adapters, one per agent-system pair
const hub = N + M;          // each agent + each system wraps MCP once
const saved = pp - hub;
const pct = pp > 0 ? Math.round(100 * saved / pp) : 0;
// left panel: draw every N*M line; right panel: draw N+M lines through a hub node.

2 · The client-server architecture and its four roles

MCP is an open standard — the book lists Gemini, OpenAI GPT, Mixtral, and Claude as models it aims to let speak to external systems uniformly. The architecture is client-server, and the book is careful to name four distinct roles in the loop. Conflating them is where mental models go wrong, so hold them apart:

LLMThe core agent. It reads the user request, plans, and decides when to reach outside for information or to act. It never speaks MCP directly.
ClientThe MCP client — the host application or wrapper around the LLM. It translates the model's intent into a standard MCP request, and handles discovery, connection, and communication. One client can connect to many servers.
ServerThe gateway to one external domain (a database, a mail service, a file system). It exposes tools, resources, and prompts to authorized clients and authenticates them.
ServiceThe actual third-party system behind the server — the real database, SaaS API, or weather endpoint where the operation finally lands.

The interaction is a five-step cycle. The book walks it concretely with a "send email" example; here it is on our coding agent asking to read a file:

1. DISCOVER client asks server: "what can you do?" ──▶ server returns manifest: tools[search_symbols, run_test], resources[repo://file/{path}], prompts[...] 2. CONSTRUCT LLM decides: read_file_slice(path="src/auth.py", start=40, end=80) client formats this as a standard MCP request 3. SEND client ──(JSON-RPC)──▶ server 4. EXECUTE server authenticates client, validates request, calls the underlying service (filesystem read) 5. RESPOND server ──▶ standardized result + provenance ──▶ client ──▶ LLM context updated; agent continues planning

The value the standard adds lives in step 1. Because discovery is part of the protocol, a client can query a server's capabilities at runtime. The book calls this "just-in-time discovery": an agent can pick up a newly added tool without being rebuilt or restarted. Contrast that with function calling, where the available functions are baked into the prompt at design time.

3 · Three exposures: resources, prompts, tools

An MCP server does not just expose "tools." It exposes three kinds of thing, and the book is emphatic that you keep them distinct because they have different semantics, caching behavior, and risk profiles:

Resources
Static data the agent can read: a PDF, a database record, a file slice. Addressed by URI like repo://file/{path}. No side effects — safe to fetch and cache.
Tools
Executable functions with side effects or live computation: send an email, run a query, run a test. These are the dangerous ones — they change the world.
Prompts
Reusable templates that structure how the LLM should interact with a resource or tool — a vetted, parameterized interaction pattern the server ships so every client uses the capability the same way.

For the running coding/research agent, a single repo MCP server makes the split obvious. It exposes read_file_slice and repo://file/{path} as resources (read-only views of the codebase), search_symbols and list_tests as tools that compute over the repo, run_read_only_test as a tool with carefully bounded side effects, and a prompt template like "summarize the diff between two commits for a reviewer." The agent discovers these four capabilities instead of being handed unrestricted shell access — which is the whole safety argument for putting MCP at the boundary.

# FastMCP server side — the book's decorator style.
# Docstrings and type hints become the tool's auto-generated interface spec.
from fastmcp import FastMCP
mcp = FastMCP()

@mcp.tool
def search_symbols(query: str) -> list[SymbolHit]:
    """Find symbol definitions matching a query across the repo."""
    ...

@mcp.resource("repo://file/{path}")
def read_file_slice(path: str, start: int = 0, end: int = -1) -> FileSlice:
    """Read a bounded slice of one repo file (read-only)."""
    ...

if __name__ == "__main__":
    mcp.run(transport="http", host="127.0.0.1", port=8000)

On the consuming side, the book's Google ADK example connects an LlmAgent to a server through an MCPToolset, and — crucially — supports a tool_filter so a client can expose only a subset of what a server offers. The filesystem example runs a community Node server via npx @modelcontextprotocol/server-filesystem rooted at one folder; the FastMCP example connects over HTTP with tool_filter=['greet'] so the agent sees exactly one tool:

# ADK client side — consume a running FastMCP server, expose only 'greet'.
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, HttpServerParameters

root_agent = LlmAgent(
    model="gemini-2.0-flash",
    name="fastmcp_greeter_agent",
    instruction="You greet people using the 'greet' tool.",
    tools=[MCPToolset(
        connection_params=HttpServerParameters(url="http://localhost:8000"),
        tool_filter=["greet"],          # the client narrows the server's surface
    )],
)

4 · MCP vs function calling — when each is right

This is the comparison the book builds an entire table around, and it is the most common interview question on the topic. Both let an LLM do more than generate text. The difference is abstraction level. Function calling is the model's action mechanism: a one-to-one, usually vendor-proprietary request from one model to one predefined function. MCP is the integration standard: a discovery-and-communication framework letting any compliant model reach any compliant tool across a client-server boundary.

DimensionFunction callingMCP
StandardizationProprietary, vendor-specific format and implementationOpen protocol; promotes interoperability across LLMs and tools
ScopeOne model requests one predefined functionGeneral framework for discovering and communicating with external tools
ArchitectureOne-to-one: model wired to app's tool logicClient-server: one app can connect to many MCP servers
DiscoveryFunctions told to the model up front, at design timeDynamic — client queries the server's capabilities at runtime
ReusabilityIntegration tightly coupled to one app + one modelIndependent, reusable servers any compliant app can access

The book's rule of thumb, restated: function calling is like handing the AI a fixed set of dedicated tools (a specific wrench and screwdriver) — perfect for a small, fixed task. MCP is the universal socket system: it provides no tools itself but lets any compliant tool plug into a dynamic, extensible workshop. So reach for plain function calling when you have a handful of fixed functions and one model; reach for MCP when you are building a complex, scalable, or enterprise system that must interoperate across many tools, data sources, and possibly many LLMs, and where agents should discover new capabilities without redeploys.

5 · The realities the book insists on

MCP is not a magic wand, and the chapter spends real space on the ways it disappoints if you treat it as one.

A bad API wrapped in MCP is still a bad API. MCP is an "agent interface" contract; its quality is bounded by the underlying API. The book's example: a ticketing system whose API only fetches one ticket at a time. Wrap it in MCP unchanged and ask the agent to "summarize all high-priority tickets" — it must loop one call at a time, slowly and inaccurately. The fix is not more agent cleverness; it is to give the underlying API deterministic capabilities like server-side filtering and sorting. Agents do not magically replace deterministic data plumbing — they need it.

Data the agent can't read is worthless to expose. A second warning: an MCP server that returns only raw PDF bytes is useless if the agent can't parse PDFs. Better to build an API that returns text (Markdown). Compatibility is about the data format, not just the connection.

Transports. The book names them precisely. Local servers use JSON-RPC over STDIO — efficient inter-process communication, ideal for sensitive data and high performance. Remote servers use Streamable HTTP and SSE for persistent, efficient client-server communication and organization-wide sharing. Local trades reach for control; remote trades control for scale.

Security is not optional. Any protocol that exposes tools and data must authenticate and authorize clients and scope what each one may do. MCP implementations must support this; the protocol standardizing discovery does not remove your obligation to scope credentials, enforce permissions, set timeouts, and keep an audit trail. The book also stresses defined error handling: the protocol must report tool failures, server-unavailable, and invalid-request errors back to the LLM in a form it can reason about and route around (which is exactly what lesson 14 on recovery will build on).

Trap
The seductive failure is treating MCP as permission to expose broad, dangerous capabilities "because it's standardized." Wrapping a raw shell or an unscoped admin API as a convenient MCP tool is how an agent deletes production data. Expose capabilities at task-shaped granularity (run_read_only_test, not exec), with side-effecting tools clearly separated from read-only resources, and record which server supplied every fact and action.

Where this points next

We now have a governed, discoverable boundary between the agent and the world: servers expose resources, prompts, and tools; clients discover and consume them through one contract, with provenance and scoped permissions. But a connected agent that can do things still needs to know whether what it is doing is the right thing, and when to stop. Lesson 13 — Goal setting and monitoring — gives the loop a measurable objective and a way to judge whether each MCP call moved it closer or just burned a turn. Discovery told the agent what it can do; goals tell it what it should do next.

Failure modes

  • Wrapping a broad, dangerous API (raw shell, admin endpoint) as a convenient MCP tool.
  • Dozens of overlapping tools with weak descriptions, so the LLM can't pick the right one.
  • Wrapping a bad underlying API — one-at-a-time fetches, no server-side filter/sort — so the agent is slow and inaccurate.
  • Exposing data the agent can't consume (raw PDF bytes instead of text/Markdown).
  • No audit trail recording which server supplied a given fact or performed a given action.
  • No defined error contract, so tool failures and timeouts confuse the model instead of triggering recovery.

Implementation checklist

  • Are exposures correctly split into resources (read-only), tools (side-effecting), and prompts?
  • Which tools have side effects, and are they separated from read-only resources?
  • Does the underlying API support deterministic filtering/sorting the agent needs?
  • Is the returned data in a format the agent can parse (text/Markdown, not opaque bytes)?
  • How are credentials scoped, and is per-client authorization enforced?
  • Local (STDIO) or remote (HTTP/SSE) transport — and does the choice match the data sensitivity?
  • Is there a defined error contract back to the LLM?
  • Does the trace record MCP provenance: server, capability, args, result summary?
Takeaway
MCP is the open, client-server contract at the agent-environment boundary. It collapses the N × M integration explosion to N + M by letting servers expose resources (read-only data), tools (side-effecting functions), and prompts (interaction templates) once, which any compliant client discovers at runtime and consumes. It differs from function calling along five axes — standardization, scope, architecture, discovery, reusability — so use function calling for a few fixed functions and MCP for complex, interoperable, discover-as-you-go systems. But the protocol guarantees none of correctness: a bad API wrapped in MCP is still bad, unparseable data is still useless, and standardized discovery never excuses skipping permissions, scoped credentials, error contracts, and provenance in the trace.

Interview prompts