Skip to content

Commit 534e8d5

Browse files
GWealecopybara-github
authored andcommitted
docs: add to_mcp_server unit guide
Co-authored-by: George Weale <gweale@google.com> PiperOrigin-RevId: 944717677
1 parent 8db2ace commit 534e8d5

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

docs/guides/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ This directory contains specific developer guides for the ADK Python implementat
1313
* [Event and NodeInfo](events/event/index.md) - Understanding Event and NodeInfo in workflows.
1414
* [RequestInput](events/request_input/index.md) - How to use RequestInput for human-in-the-loop interactions.
1515

16+
### Tools
17+
* [to_mcp_server](tools/mcp_tool/agent_to_mcp/index.md) - Expose an ADK agent as an MCP server so any MCP host can drive it as a single tool (the MCP counterpart of to_a2a).
18+
1619
### Workflows
1720
* [Workflow](workflow/workflow/index.md) - Graph-based orchestration of complex, multi-step agent interactions.
1821
* [Workflow Graphs](workflow/graph/index.md) - Understanding nodes, edges, and graph structures in workflows.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# to_mcp_server
2+
3+
Exposes an ADK agent as an MCP server so any MCP host (Claude Code, OpenAI
4+
Codex, an IDE, or any MCP client) can drive it as a single tool. It is the MCP
5+
counterpart of `to_a2a`.
6+
7+
## Introduction
8+
9+
`to_mcp_server` turns a whole ADK agent into a standard
10+
[Model Context Protocol](https://modelcontextprotocol.io/) server. The agent —
11+
its model loop and all of its tools — is registered as a *single* MCP tool named
12+
after the agent. A host that speaks MCP sends a request string and receives the
13+
agent's final response; it never imports ADK and does not see the agent's
14+
individual tools.
15+
16+
This solves the problem of making an ADK agent consumable by harnesses that are
17+
not ADK. Where `to_a2a` publishes an agent over A2A, `to_mcp_server` publishes it
18+
over MCP, so coding agents and IDEs that already speak MCP can delegate a task to
19+
an ADK agent as if it were any other tool. It builds on `Runner` to execute the
20+
agent and returns a `FastMCP` server, leaving the choice of transport (stdio for
21+
local hosts, streamable-http for networked ones) to the caller.
22+
23+
## Get started
24+
25+
Define an agent and expose it. Running the file starts the MCP server on stdio;
26+
an MCP host can also launch it as a subprocess.
27+
28+
```python
29+
import random
30+
31+
from google.adk.agents import LlmAgent
32+
from google.adk.tools.mcp_tool import to_mcp_server
33+
34+
35+
def roll_die(sides: int) -> int:
36+
"""Roll a die with the given number of sides and return the result."""
37+
return random.randint(1, sides)
38+
39+
40+
dice_agent = LlmAgent(
41+
name="dice_agent",
42+
description="Rolls dice with any number of sides and reports the outcome.",
43+
instruction="Use the roll_die tool to roll the dice the user asks for.",
44+
tools=[roll_die],
45+
)
46+
47+
# The whole agent becomes one MCP tool named "dice_agent".
48+
server = to_mcp_server(dice_agent)
49+
50+
if __name__ == "__main__":
51+
server.run(transport="stdio")
52+
```
53+
54+
A host configured to launch this file sees one tool, `dice_agent`, and calls it
55+
with a `request` string; the ADK agent runs its own model and `roll_die` loop and
56+
returns the answer.
57+
58+
## How it works
59+
60+
`to_mcp_server` creates a `FastMCP` server and registers one tool whose handler
61+
runs the agent through a `Runner`. If no `runner` is supplied, one is built with
62+
in-memory session, artifact, memory, and credential services.
63+
64+
On each tool call the handler:
65+
66+
1. Resolves an ADK session (see below), then wraps the incoming `request` string
67+
as a user `Content`.
68+
2. Drives `Runner.run_async` and iterates the event stream.
69+
3. Forwards intermediate (non-final) text events to the host as MCP **progress
70+
notifications**, so the host can show the agent working in real time.
71+
4. Maps the parts of the final response to MCP content blocks and returns them:
72+
text becomes `TextContent`, inline image data becomes `ImageContent`, audio
73+
becomes `AudioContent`, and any other inline data becomes an
74+
`EmbeddedResource`. This is why a multimodal agent's output is preserved
75+
rather than flattened to text.
76+
77+
### Session continuity
78+
79+
`to_mcp_server` keeps one ADK session per MCP connection, so successive tool
80+
calls on the same connection form a single multi-turn conversation. The mapping
81+
from connection to session is held in a `weakref.WeakKeyDictionary`, so a
82+
session's entry is dropped when its connection is garbage-collected. Over stdio
83+
there is one connection per process, so all calls share one conversation; over
84+
streamable-http each client connection gets its own session.
85+
86+
`to_mcp_server` depends on `Runner`, the agent (`BaseAgent`/`LlmAgent`),
87+
`google.genai.types`, and `mcp.server.fastmcp.FastMCP`; it returns a `FastMCP`
88+
that the caller runs on a transport of their choice.
89+
90+
## Configuration options
91+
92+
| Option | Type | Default | Description |
93+
| --- | --- | --- | --- |
94+
| `agent` | `BaseAgent` | *required* | The agent to serve. Its model loop and all of its tools are exposed together as one MCP tool. |
95+
| `name` | `str \| None` | `None` | The MCP server and tool name. Defaults to the agent's name (or `"adk_agent"`). Set it when you want the tool to appear under a name other than the agent's. |
96+
| `instructions` | `str \| None` | `None` | Optional server instructions an MCP host may surface to its model to describe how to use the tool. |
97+
| `runner` | `Runner \| None` | `None` | A pre-built `Runner`. If omitted, one is created with in-memory services. Supply your own to use persistent or custom session, artifact, memory, or credential services — this is the recommended path for a long-lived networked server. |
98+
99+
## Advanced applications
100+
101+
### Serving over the network
102+
103+
* **Problem solved**: a host on another machine needs to reach the agent.
104+
* **Implementation**: run the same server with the networked transport:
105+
`server.run(transport="streamable-http")`. Nothing about the agent changes;
106+
only the transport differs.
107+
108+
### Bringing your own services
109+
110+
* **Problem solved**: the default in-memory services do not persist across
111+
process restarts and are not suited to multi-client production serving.
112+
* **Implementation**: build a `Runner` with your chosen services and pass it in:
113+
`to_mcp_server(agent, runner=my_runner)`. The tool then uses those services
114+
for every call.
115+
116+
### Multimodal responses
117+
118+
* **Problem solved**: the agent produces images or audio, not just text.
119+
* **Implementation**: no extra work — non-text parts of the final response are
120+
returned as `ImageContent`, `AudioContent`, or `EmbeddedResource`, so the
121+
host receives them alongside any text.
122+
123+
## Limitations
124+
125+
* **Text input only**: the tool accepts a single `request` string. Passing
126+
media *into* the agent is not supported through the tool call, because MCP
127+
tool arguments are JSON that the host's model fills in and hosts do not place
128+
media in tool arguments. For media input, use MCP resources or elicitation
129+
instead.
130+
* **Default services are in-memory**: for a long-lived streamable-http server,
131+
sessions accumulate with no eviction; inject a `runner` with a persistent or
132+
cleaning session service. Tool calls on a single connection are expected to
133+
be sequential, since they share one session.
134+
* **Experimental**: `to_mcp_server` is `@experimental` and lives behind the
135+
`mcp` extra; its behavior may change in future releases.
136+
137+
## Related samples
138+
139+
* [MCP: serve an ADK agent](../../../../../contributing/samples/mcp/mcp_serve_agent)

0 commit comments

Comments
 (0)