Skip to content

Commit f56f6cd

Browse files
authored
Merge branch 'main' into fix/litellm-content-none-parts
2 parents e88988f + 1ac6875 commit f56f6cd

89 files changed

Lines changed: 2318 additions & 357 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

contributing/samples/adk_team/adk_documentation/adk_release_analyzer/agent.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ def summary_instruction(readonly_context: ReadonlyContext) -> str:
559559
name="summary_agent",
560560
description="Compiles recommendations and creates the GitHub issue.",
561561
instruction=summary_instruction,
562+
include_contents="none",
562563
tools=[
563564
get_all_recommendations,
564565
create_issue,

contributing/samples/managed_agent/basic/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Managed Agent
22

3+
> For setup, authentication, backends, and background on `ManagedAgent`, see the
4+
> [ManagedAgent guide](../../../../docs/guides/agents/managed_agent/index.md).
5+
36
## Overview
47

58
This sample runs a `ManagedAgent` configured with the built-in `google_search`

contributing/samples/managed_agent/code_execution/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Managed Agent - Code Execution
22

3+
> For setup, authentication, backends, and background on `ManagedAgent`, see the
4+
> [ManagedAgent guide](../../../../docs/guides/agents/managed_agent/index.md).
5+
36
## Overview
47

58
This sample runs a `ManagedAgent` configured with the built-in **code execution**

docs/guides/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ This directory contains specific developer guides for the ADK Python implementat
77
### Agents
88
* [LlmAgent Single-Turn Mode](agents/llm_agent/single_turn.md) - Guide on using LlmAgent in single-turn mode.
99
* [LlmAgent Task Mode](agents/llm_agent/task.md) - Guide on using LlmAgent in task mode.
10+
* [ManagedAgent](agents/managed_agent/index.md) - Guide on using ManagedAgent with server-side tools.
1011

1112
### Events
1213
* [Event and NodeInfo](events/event/index.md) - Understanding Event and NodeInfo in workflows.
1314
* [RequestInput](events/request_input/index.md) - How to use RequestInput for human-in-the-loop interactions.
1415

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+
1519
### Workflows
1620
* [Workflow](workflow/workflow/index.md) - Graph-based orchestration of complex, multi-step agent interactions.
1721
* [Workflow Graphs](workflow/graph/index.md) - Understanding nodes, edges, and graph structures in workflows.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# ManagedAgent
2+
3+
## Introduction
4+
5+
`ManagedAgent` allows you to leverage managed agents backed by the Managed
6+
Agents API (`interactions.create`) via either the
7+
[Gemini Enterprise Agents Platform (GEAP, formerly Vertex)](https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/managed-agents)
8+
or the [Gemini API](https://ai.google.dev/gemini-api/docs/agents) from within
9+
your ADK flows. It is particularly useful when you want to utilize Google's
10+
powerful first-party, out-of-the-box agents (like the Antigravity agent) that
11+
have specialized server-side execution environments built-in without requiring
12+
client-side function declarations.
13+
14+
This solves the developer problem of needing a robust, server-hosted environment
15+
for agents that require specialized built-in capabilities, rather than managing
16+
sandbox environments and Python code execution locally. `ManagedAgent` can be
17+
used as a standalone agent, integrated directly into a workflow, or encapsulated
18+
as a tool via `AgentTool` so that a coordinating `LlmAgent` can delegate
19+
specialized tasks to it.
20+
21+
## Prerequisites
22+
23+
The `ManagedAgent` supports two distinct backends: the Gemini API backend and
24+
the Gemini Enterprise Agents Platform (GEAP) backend. Depending on which backend
25+
you intend to use, you must satisfy the corresponding prerequisites for
26+
authentication and obtaining an Agent ID.
27+
28+
### Option 1: Gemini API Backend
29+
30+
* **Authentication**: You must obtain a Gemini API key. Set this as the
31+
`GEMINI_API_KEY` environment variable.
32+
* **Agent ID**: You need an `agent_id` to connect to. You can either:
33+
* Create a new agent by following the
34+
[Gemini API Agents documentation](https://ai.google.dev/gemini-api/docs/agents).
35+
* Use an out-of-the-box agent ID, such as `antigravity-preview-05-2026`,
36+
which is commonly used in our examples.
37+
38+
### Option 2: Gemini Enterprise Agents Platform (GEAP) Backend
39+
40+
* **Authentication**: GEAP (formerly Vertex) requires Google Cloud
41+
credentials. Follow the
42+
[GEAP setup instructions](https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/managed-agents/create-manage#before-you-begin)
43+
to authenticate your local environment (e.g., using `gcloud auth
44+
application-default login`).
45+
* **Agent ID**: Similar to the Gemini API, you need an `agent_id`. You can
46+
either:
47+
* Create a new agent via the
48+
[GEAP Managed Agents guide](https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/managed-agents).
49+
* Use an out-of-the-box agent ID if available to your project.
50+
51+
## Get started
52+
53+
Here is a minimal implementation of `ManagedAgent` demonstrating its use.
54+
55+
```python
56+
import os
57+
from google.adk.agents import ManagedAgent
58+
from google.adk.tools import google_search
59+
from google.genai import types
60+
61+
# Ensure you have the MANAGED_AGENT_ID and the proper environment config
62+
_AGENT_ID = os.environ.get('MANAGED_AGENT_ID', 'antigravity-preview-05-2026')
63+
64+
managed_search_agent = ManagedAgent(
65+
name='managed_search_agent',
66+
description='Answers questions that need fresh, grounded information from the web.',
67+
agent_id=_AGENT_ID,
68+
environment={'type': 'remote'},
69+
tools=[google_search],
70+
)
71+
72+
# A managed code execution agent using raw types.Tool
73+
managed_code_execution_agent = ManagedAgent(
74+
name='managed_code_execution_agent',
75+
description='Solves computational questions by running code server-side.',
76+
agent_id=_AGENT_ID,
77+
environment={'type': 'remote'},
78+
tools=[types.Tool(code_execution=types.ToolCodeExecution())],
79+
)
80+
```
81+
82+
To see an orchestrator pattern using this code, you could wrap them using
83+
`AgentTool`:
84+
85+
```python
86+
from google.adk.agents import LlmAgent
87+
from google.adk.tools.agent_tool import AgentTool
88+
89+
# The local coordinator delegates tasks to the server-backed agents
90+
root_agent = LlmAgent(
91+
name='managed_tool_coordinator',
92+
description='Calls managed specialists as tools and composes the answer.',
93+
tools=[
94+
AgentTool(agent=managed_search_agent),
95+
AgentTool(agent=managed_code_execution_agent),
96+
],
97+
)
98+
```
99+
100+
## How it works
101+
102+
The `ManagedAgent` implements the `BaseAgent` contract but bypasses standard
103+
`generate_content` calls, instead sending interactions via
104+
`_create_interactions` with `background=True`. It natively streams partial
105+
events and terminal events in real-time back to the ADK `Runner` or parent flow.
106+
107+
When using the GEAP backend, it enforces a connection to the `global` location
108+
since the Managed Agents API is solely available globally. Because it runs
109+
remotely, tools are translated into standard `ToolParam` formats for
110+
interactions; any raw `google.genai.types.Tool` configs are passed through to
111+
the backend, enabling server-side code execution or remote google search
112+
seamlessly.
113+
114+
### State: local session vs. remote
115+
116+
`ManagedAgent` keeps almost no state locally. The ADK session only persists two
117+
values on the events it emits: the `previous_interaction_id` and the sandbox
118+
`environment_id`. On each new turn the agent recovers both by scanning prior
119+
session events, then reuses them so the conversation and its sandbox continue.
120+
121+
Everything else lives server-side. The Managed Agents API owns the sandbox
122+
environment and the full interaction history, and that remote interaction — not
123+
the local session — is the source of truth for continuing a conversation.
124+
Response text appears in both places (the local ADK events and the remote
125+
interaction history), but ADK stores only the ids it needs to recover and reuse
126+
the remote state; it never re-sends prior turns.
127+
128+
## Advanced applications
129+
130+
### Tool encapsulation for orchestration
131+
132+
* **Problem solved**: Sometimes a single LLM request needs to compose results
133+
from multiple independent, robust specialists without losing control of the
134+
execution turn.
135+
* **Implementation**: Encapsulate each `ManagedAgent` instance within its own
136+
separate `AgentTool` and provide them as a list of tools to an `LlmAgent`
137+
coordinator. The coordinator will invoke the managed agents (which run their
138+
sandboxed logic server-side), collect the results, and then compose the
139+
final synthesized response natively.
140+
141+
## Limitations
142+
143+
* **Location pinned (GEAP only)**: For the GEAP backend, the Managed Agents
144+
API is currently only served from the `global` location. Enterprise clients
145+
using regional endpoints will raise an error.
146+
* **Server-side tools only**: Client-executed tools (Python functions,
147+
callables) and MCP tools are not supported. Providing these will raise a
148+
`NotImplementedError`.
149+
* **Streaming only**: The agent only supports streaming interactions.
150+
Background-polling execution or strictly non-streaming connections are not
151+
yet fully supported (it natively uses `stream=True` and yields events).
152+
153+
## Related samples
154+
155+
* [Managed Agent Basic](../../../../contributing/samples/managed_agent/basic)
156+
* [Managed Agent Code Execution](../../../../contributing/samples/managed_agent/code_execution)
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)

src/google/adk/agents/llm_agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async def _convert_tool_union_to_tools(
147147

148148
# Wrap google_search tool with AgentTool if there are multiple tools because
149149
# the built-in tools cannot be used together with other tools.
150-
# TODO(b/448114567): Remove once the workaround is no longer needed.
150+
# TODO: Remove once the workaround is no longer needed.
151151
if multiple_tools and isinstance(tool_union, GoogleSearchTool):
152152
from ..tools.google_search_agent_tool import create_google_search_agent
153153
from ..tools.google_search_agent_tool import GoogleSearchAgentTool
@@ -159,7 +159,7 @@ async def _convert_tool_union_to_tools(
159159
# Replace VertexAiSearchTool with DiscoveryEngineSearchTool if there are
160160
# multiple tools because the built-in tools cannot be used together with
161161
# other tools.
162-
# TODO(b/448114567): Remove once the workaround is no longer needed.
162+
# TODO: Remove once the workaround is no longer needed.
163163
if multiple_tools and isinstance(tool_union, VertexAiSearchTool):
164164
from ..tools.discovery_engine_search_tool import DiscoveryEngineSearchTool
165165

@@ -742,7 +742,7 @@ async def canonical_tools(
742742
"""
743743
# We may need to wrap some built-in tools if there are other tools
744744
# because the built-in tools cannot be used together with other tools.
745-
# TODO(b/448114567): Remove once the workaround is no longer needed.
745+
# TODO: Remove once the workaround is no longer needed.
746746
multiple_tools = len(self.tools) > 1
747747
model = self.canonical_model
748748

src/google/adk/agents/run_config.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ class RunConfig(BaseModel):
242242
realtime_input_config: Optional[types.RealtimeInputConfig] = None
243243
"""Realtime input config for live agents with audio input from user."""
244244

245+
explicit_vad_signal: Optional[bool] = None
246+
"""Whether to enable explicit voice activity detection (VAD) signals from the model."""
247+
245248
translation_config: Optional[types.TranslationConfig] = None
246249
"""Configures real-time speech-to-speech translation.
247250
@@ -379,6 +382,14 @@ class RunConfig(BaseModel):
379382
callers provide per-turn context without changing the conversation history.
380383
"""
381384

385+
include_thoughts_from_other_agents: bool = False
386+
"""Whether to include other agents' thought parts in LLM context.
387+
388+
By default, thoughts from other agents are excluded when their messages are
389+
reformatted as user context for the current agent. Enable this only when
390+
agents are expected to share internal reasoning with one another.
391+
"""
392+
382393
@model_validator(mode='before')
383394
@classmethod
384395
def check_for_deprecated_save_live_audio(cls, data: Any) -> Any:

0 commit comments

Comments
 (0)