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.
greet server consumed over HTTP.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.
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.
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.
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:
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:
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:
repo://file/{path}. No side effects — safe to fetch and cache.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.
| Dimension | Function calling | MCP |
|---|---|---|
| Standardization | Proprietary, vendor-specific format and implementation | Open protocol; promotes interoperability across LLMs and tools |
| Scope | One model requests one predefined function | General framework for discovering and communicating with external tools |
| Architecture | One-to-one: model wired to app's tool logic | Client-server: one app can connect to many MCP servers |
| Discovery | Functions told to the model up front, at design time | Dynamic — client queries the server's capabilities at runtime |
| Reusability | Integration tightly coupled to one app + one model | Independent, 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).
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?
Interview prompts
- What problem does MCP solve that plain function calling does not? (§1, §4 — the N×M integration explosion: without a standard, every agent-system pair needs a bespoke adapter; MCP makes it N+M by standardizing discovery and communication so reusable servers serve any compliant client.)
- Name the four roles in an MCP interaction and what each does. (§2 — LLM decides when to reach out; MCP client translates intent to standard requests and handles discovery/connection; MCP server exposes a domain's capabilities and authenticates clients; the third-party service is the real backend where the action lands.)
- Distinguish resources, tools, and prompts in MCP. (§3 — resources are read-only data addressed by URI; tools are executable, side-effecting functions; prompts are reusable templates structuring how the LLM interacts with a capability.)
- When would you choose function calling over MCP? (§4 — when you have a small fixed set of functions and one model; the protocol overhead of MCP pays off only for complex, scalable, interoperable systems that need runtime discovery across many tools or LLMs.)
- "Just wrap our existing ticket API in MCP and the agent will handle the rest." What's wrong? (§5 — a bad API wrapped in MCP is still bad; one-ticket-at-a-time fetches make summarizing high-priority tickets slow and inaccurate. The underlying API needs deterministic filtering/sorting; agents don't replace deterministic plumbing.)
- How does MCP's discovery differ from function calling's, and why does it matter? (§2, §4 — MCP clients query server capabilities at runtime ("just-in-time discovery"), so an agent picks up new tools without redeploy; function-calling tools are fixed in the prompt at design time.)
- What does standardizing the protocol NOT give you for free? (§5 — authentication/authorization, scoped credentials, timeouts, a defined error contract back to the LLM, and provenance/audit logging all still have to be designed and enforced.)