Skip to content

Commit b7d992b

Browse files
authored
Merge branch 'main' into docs/openapi-session-endpoint-docs-update
2 parents 3c84ae8 + f973673 commit b7d992b

File tree

15 files changed

+936
-17
lines changed

15 files changed

+936
-17
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Local Environment Sample
2+
3+
This sample demonstrates how to use the `LocalEnvironment` with the `EnvironmentToolset` to allow an agent to interact with the local filesystem and execute commands.
4+
5+
## Description
6+
7+
The agent is configured with the `EnvironmentToolset`, which provides tools for file I/O (reading, writing) and command execution within a local environment. This allows the agent to perform tasks that involve creating files, modifying them, and running local scripts or commands.
8+
9+
## Sample Usage
10+
11+
You can interact with the agent by providing prompts that require file operations and command execution.
12+
13+
### Example Prompt
14+
15+
> "Write a Python file named `hello.py` to the working directory that prints 'Hello from ADK!'. Then read the file to verify its contents, and finally execute it using a command."
16+
17+
### Expected Behavior
18+
19+
1. **Write File**: The agent uses a tool to write `hello.py` with the content `print("Hello from ADK!")`.
20+
2. **Read File**: The agent uses a tool to read `hello.py` and verify the content.
21+
3. **Execute Command**: The agent uses a tool to run `python3 hello.py` and returns the output.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from google.adk import Agent
17+
from google.adk.environment import LocalEnvironment
18+
from google.adk.tools.environment import EnvironmentToolset
19+
20+
root_agent = Agent(
21+
model="gemini-2.5-pro",
22+
name="local_environment_agent",
23+
description="A simple agent that demonstrates local environment usage.",
24+
instruction="""
25+
You are a helpful AI assistant that can use the local environment to
26+
execute commands and file I/O. Follow the rules of the environment and the
27+
user's instructions.
28+
""",
29+
tools=[
30+
EnvironmentToolset(
31+
environment=LocalEnvironment(),
32+
),
33+
],
34+
)

contributing/samples/toolbox_agent/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Run the following command to download and install the MCP Toolbox binary.
1919
2020
```bash
2121
export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, or windows/amd64
22-
curl -O https://storage.googleapis.com/genai-toolbox/v0.28.0/$OS/toolbox
22+
curl -O https://storage.googleapis.com/genai-toolbox/v0.31.0/$OS/toolbox
2323
chmod +x toolbox
2424
```
2525

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,12 @@ extensions = [
165165
"llama-index-embeddings-google-genai>=0.3.0", # For files retrieval using LlamaIndex.
166166
"lxml>=5.3.0", # For load_web_page tool.
167167
"pypika>=0.50.0", # For crewai->chromadb dependency
168-
"toolbox-adk>=0.7.0, <0.8.0", # For tools.toolbox_toolset.ToolboxToolset
168+
"toolbox-adk>=1.0.0, <2.0.0", # For tools.toolbox_toolset.ToolboxToolset
169169
]
170170

171171
otel-gcp = ["opentelemetry-instrumentation-google-genai>=0.6b0, <1.0.0"]
172172

173-
toolbox = ["toolbox-adk>=0.7.0, <0.8.0"]
173+
toolbox = ["toolbox-adk>=1.0.0, <2.0.0"]
174174

175175
slack = ["slack-bolt>=1.22.0"]
176176

src/google/adk/integrations/agent_registry/agent_registry.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,70 @@
3737
from a2a.types import TransportProtocol as A2ATransport
3838
from google.adk.agents.readonly_context import ReadonlyContext
3939
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
40+
from google.adk.telemetry.tracing import GCP_MCP_SERVER_DESTINATION_ID
41+
from google.adk.tools.base_tool import BaseTool
42+
from google.adk.tools.mcp_tool.mcp_session_manager import SseConnectionParams
43+
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
4044
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
4145
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
4246
import google.auth
4347
import google.auth.transport.requests
4448
import httpx
49+
from mcp import StdioServerParameters
50+
from typing_extensions import override
4551

4652
logger = logging.getLogger("google_adk." + __name__)
4753

4854
AGENT_REGISTRY_BASE_URL = "https://agentregistry.googleapis.com/v1alpha"
4955

5056

57+
# An MCPToolset for a single registered MCP server. Adds the special
58+
# gcp.mcp.server.destination.id custom_metadata key on each returned tool. This special key is
59+
# added to execute_tool spans in google.adk.telemetry.tracing
60+
class AgentRegistrySingleMcpToolset(McpToolset):
61+
62+
def __init__(
63+
self,
64+
*,
65+
destination_resource_id: str | None,
66+
connection_params: (
67+
StdioServerParameters
68+
| StdioConnectionParams
69+
| SseConnectionParams
70+
| StreamableHTTPConnectionParams
71+
),
72+
tool_name_prefix: str | None = None,
73+
header_provider: (
74+
Callable[[ReadonlyContext], Dict[str, str]] | None
75+
) = None,
76+
):
77+
super().__init__(
78+
connection_params=connection_params,
79+
tool_name_prefix=tool_name_prefix,
80+
header_provider=header_provider,
81+
)
82+
self.destination_resource_id = destination_resource_id
83+
84+
@override
85+
async def get_tools(
86+
self, readonly_context: ReadonlyContext | None = None
87+
) -> List[BaseTool]:
88+
tools = await super().get_tools(readonly_context)
89+
90+
# Noop if there is no destination_resource_id
91+
if self.destination_resource_id is None:
92+
return tools
93+
94+
for tool in tools:
95+
if not tool.custom_metadata:
96+
tool.custom_metadata = {}
97+
98+
tool.custom_metadata[GCP_MCP_SERVER_DESTINATION_ID] = (
99+
self.destination_resource_id
100+
)
101+
return tools
102+
103+
51104
class _ProtocolType(str, Enum):
52105
"""Supported agent protocol types."""
53106

@@ -196,6 +249,9 @@ def get_mcp_toolset(self, mcp_server_name: str) -> McpToolset:
196249
"""Constructs an McpToolset instance from a registered MCP Server."""
197250
server_details = self.get_mcp_server(mcp_server_name)
198251
name = self._clean_name(server_details.get("displayName", mcp_server_name))
252+
mcp_server_id = server_details.get("mcpServerId")
253+
if not isinstance(mcp_server_id, str):
254+
mcp_server_id = None
199255

200256
endpoint_uri = self._get_connection_uri(
201257
server_details, protocol_binding=A2ATransport.jsonrpc
@@ -210,7 +266,8 @@ def get_mcp_toolset(self, mcp_server_name: str) -> McpToolset:
210266
connection_params = StreamableHTTPConnectionParams(
211267
url=endpoint_uri, headers=self._get_auth_headers()
212268
)
213-
return McpToolset(
269+
return AgentRegistrySingleMcpToolset(
270+
destination_resource_id=mcp_server_id,
214271
connection_params=connection_params,
215272
tool_name_prefix=name,
216273
header_provider=self._header_provider,

src/google/adk/memory/vertex_ai_memory_bank_service.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,9 @@ def _get_api_client(self) -> vertexai.AsyncClient:
397397
"""
398398
import vertexai
399399

400-
return vertexai.Client(
401-
project=self._project,
402-
location=self._location,
403-
api_key=self._express_mode_api_key,
404-
).aio
400+
if self._express_mode_api_key:
401+
return vertexai.Client(api_key=self._express_mode_api_key).aio
402+
return vertexai.Client(project=self._project, location=self._location).aio
405403

406404

407405
def _should_filter_out_event(content: types.Content) -> bool:

src/google/adk/sessions/vertex_ai_session_service.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,11 +376,15 @@ def _get_api_client(self) -> vertexai.AsyncClient:
376376
"""
377377
import vertexai
378378

379+
if self._express_mode_api_key:
380+
return vertexai.Client(
381+
http_options=self._api_client_http_options_override(),
382+
api_key=self._express_mode_api_key,
383+
).aio
379384
return vertexai.Client(
380385
project=self._project,
381386
location=self._location,
382387
http_options=self._api_client_http_options_override(),
383-
api_key=self._express_mode_api_key,
384388
).aio
385389

386390

src/google/adk/telemetry/tracing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@
8383

8484
USER_CONTENT_ELIDED = '<elided>'
8585

86+
# Used to associate a span with a destination resource for AppHub. Tools with
87+
# this key in their BaseTool.custom_metadata will have the mapping added as a
88+
# span attribute
89+
GCP_MCP_SERVER_DESTINATION_ID = 'gcp.mcp.server.destination.id'
90+
8691
# Needed to avoid circular imports
8792
if TYPE_CHECKING:
8893
from ..agents.base_agent import BaseAgent
@@ -190,6 +195,14 @@ def trace_tool_call(
190195
else:
191196
span.set_attribute(ERROR_TYPE, type(error).__name__)
192197

198+
# Special case for client side association with a remote tool call
199+
if (
200+
tool.custom_metadata
201+
and GCP_MCP_SERVER_DESTINATION_ID in tool.custom_metadata
202+
):
203+
destination_id = tool.custom_metadata[GCP_MCP_SERVER_DESTINATION_ID]
204+
span.set_attribute(GCP_MCP_SERVER_DESTINATION_ID, destination_id)
205+
193206
# Setting empty llm request and response (as UI expect these) while not
194207
# applicable for tool_response.
195208
span.set_attribute('gcp.vertex.agent.llm_request', '{}')
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Environment toolset for command execution and file I/O."""
16+
17+
from __future__ import annotations
18+
19+
from ._environment_toolset import EnvironmentToolset
20+
21+
__all__ = [
22+
'EnvironmentToolset',
23+
]

0 commit comments

Comments
 (0)