Skip to content

Commit 4d40f52

Browse files
JesuTerrazJesus TerrazasCopilot
authored
New Google ADK Tooling Extension (#156)
* initial commit * update pyproject.toml * test additions * remove non-central versioning * format * format x2 * ignore E402 * remove duplicate folder * remove extra newline * Fix Google ADK extension documentation and linting config (#157) * Initial plan * Fix design.md and add ruff copyright config per review comments Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> * Align Google ADK extension with tooling extension conventions (#158) * Initial plan * Fix setup.py consistency and add @pytest.mark.unit decorators Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> * Modify agent instead of creating a new instance * Fix Google ADK extension documentation and log messages (#160) * Initial plan * Fix documentation and log message for in-place agent modification Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> * Remove redundant comment from documentation example Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JesuTerraz <96103167+JesuTerraz@users.noreply.github.com> --------- Co-authored-by: Jesus Terrazas <jterrazas@microsoft.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 66e7be6 commit 4d40f52

12 files changed

Lines changed: 3284 additions & 1290 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# microsoft-agents-a365-tooling-extensions-googleadk
2+
3+
[![PyPI](https://img.shields.io/pypi/v/microsoft-agents-a365-tooling-extensions-googleadk?label=PyPI&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling-extensions-googleadk)
4+
[![PyPI Downloads](https://img.shields.io/pypi/dm/microsoft-agents-a365-tooling-extensions-googleadk?label=Downloads&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling-extensions-googleadk)
5+
6+
Google ADK (Agent Development Kit) specific tools and services for AI agent development. Provides MCP (Model Context Protocol) tool registration service for dynamically adding MCP servers to Google ADK-based agents.
7+
8+
## Installation
9+
10+
```bash
11+
pip install microsoft-agents-a365-tooling-extensions-googleadk
12+
```
13+
14+
## Usage
15+
16+
For usage examples and detailed documentation, see the [Tooling documentation](https://learn.microsoft.com/microsoft-agent-365/developer/tooling?tabs=python) on Microsoft Learn.
17+
18+
## Support
19+
20+
For issues, questions, or feedback:
21+
22+
- File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
23+
- See the [main documentation](../../README.md) for more information
24+
25+
## Trademarks
26+
27+
*Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653.*
28+
29+
## License
30+
31+
Copyright (c) Microsoft Corporation. All rights reserved.
32+
33+
Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Tooling Extensions - Google ADK - Design Document
2+
3+
This document describes the architecture and design of the `microsoft-agents-a365-tooling-extensions-googleadk` package.
4+
5+
## Overview
6+
7+
This extension adapts MCP tool server configurations to Google's Agent Development Kit (ADK), enabling Google ADK-based agents to use MCP tools through the `McpToolset` interface.
8+
9+
## Key Components
10+
11+
### McpToolRegistrationService
12+
13+
The main service for registering MCP tools with Google ADK agents.
14+
15+
```python
16+
from microsoft_agents_a365.tooling.extensions.googleadk import McpToolRegistrationService
17+
18+
service = McpToolRegistrationService()
19+
20+
# Add MCP tools to existing agent (modified in place)
21+
await service.add_tool_servers_to_agent(
22+
agent=existing_agent,
23+
auth=auth_context,
24+
auth_handler_name="graph",
25+
context=turn_context,
26+
)
27+
```
28+
29+
### Integration Flow
30+
31+
```
32+
McpToolServerConfigurationService
33+
34+
35+
List MCPServerConfig objects
36+
37+
38+
McpToolRegistrationService.add_tool_servers_to_agent()
39+
40+
├── Exchange token for MCP scope
41+
├── Create McpToolset for each server
42+
└── Modify agent in place to add all tools
43+
44+
45+
Google ADK Agent with MCP tools
46+
```
47+
48+
### add_tool_servers_to_agent Parameters
49+
50+
| Parameter | Type | Description |
51+
|-----------|------|-------------|
52+
| `agent` | `Agent` | The existing Google ADK agent |
53+
| `auth` | `Authorization` | Auth context for token exchange |
54+
| `auth_handler_name` | `str` | Name of auth handler |
55+
| `context` | `TurnContext` | Conversation context |
56+
| `auth_token` | `str \| None` | Optional pre-obtained token |
57+
58+
### McpToolset Creation
59+
60+
For each MCP server configuration:
61+
62+
```python
63+
server_info = McpToolset(
64+
connection_params=StreamableHTTPConnectionParams(
65+
url=config.url,
66+
headers={
67+
"Authorization": f"Bearer {token}"
68+
}
69+
)
70+
)
71+
```
72+
73+
## File Structure
74+
75+
```
76+
microsoft_agents_a365/tooling/extensions/googleadk/
77+
├── __init__.py
78+
└── services/
79+
├── __init__.py
80+
└── mcp_tool_registration_service.py
81+
```
82+
83+
## Dependencies
84+
85+
- `google-adk` - Google Agent Development Kit
86+
- `microsoft-agents-a365-tooling` - Core tooling service
87+
- `microsoft-agents-hosting-core` - Authorization and TurnContext
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""
5+
Google ADK extensions for Microsoft Agent 365 Tooling SDK
6+
7+
Google ADK (Agent Development Kit) specific tools and services for AI agent development.
8+
Provides MCP (Model Context Protocol) tool registration service for dynamically
9+
adding MCP servers to Google ADK-based agents.
10+
11+
Main Service:
12+
- McpToolRegistrationService: Add MCP tool servers to Google ADK agents
13+
14+
This module includes implementations for:
15+
- Google ADK agent creation with MCP (Model Context Protocol) server support
16+
- MCP tool registration service for dynamically adding MCP servers to agents
17+
- Authentication and authorization patterns for MCP server discovery
18+
"""
19+
20+
__version__ = "1.0.0"
21+
22+
# Import services from the services module
23+
from .services import McpToolRegistrationService
24+
25+
__all__ = [
26+
"McpToolRegistrationService",
27+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""
5+
Services module for Google ADK tooling.
6+
7+
This package contains service implementations for MCP tool registration
8+
and management within the Google ADK framework.
9+
"""
10+
11+
from .mcp_tool_registration_service import McpToolRegistrationService
12+
13+
__all__ = [
14+
"McpToolRegistrationService",
15+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""
5+
MCP Tool Registration Service for Google ADK.
6+
7+
This service provides MCP (Model Context Protocol) tool registration
8+
capabilities for Google ADK-based agents.
9+
"""
10+
11+
# Standard library imports
12+
import logging
13+
from typing import List, Optional
14+
15+
# Third-party imports
16+
from google.adk.agents import Agent
17+
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset, StreamableHTTPConnectionParams
18+
19+
# Local imports
20+
from microsoft_agents.hosting.core import Authorization, TurnContext
21+
from microsoft_agents_a365.runtime.utility import Utility
22+
from microsoft_agents_a365.tooling.models import ToolOptions
23+
from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import (
24+
McpToolServerConfigurationService,
25+
)
26+
from microsoft_agents_a365.tooling.utils.constants import Constants
27+
from microsoft_agents_a365.tooling.utils.utility import (
28+
get_mcp_platform_authentication_scope,
29+
)
30+
31+
32+
class McpToolRegistrationService:
33+
"""
34+
Provides MCP tool registration services for Google ADK agents.
35+
36+
This service handles registration and management of MCP (Model Context Protocol)
37+
tool servers with Google ADK agents.
38+
"""
39+
40+
_orchestrator_name: str = "GoogleADK"
41+
42+
def __init__(self, logger: Optional[logging.Logger] = None):
43+
"""
44+
Initialize the MCP Tool Registration Service for Google ADK.
45+
46+
Args:
47+
logger: Logger instance for logging operations.
48+
"""
49+
self._logger = logger or logging.getLogger(self.__class__.__name__)
50+
self._mcp_server_configuration_service = McpToolServerConfigurationService(
51+
logger=self._logger
52+
)
53+
self._connected_servers: List[McpToolset] = []
54+
55+
async def add_tool_servers_to_agent(
56+
self,
57+
agent: Agent,
58+
auth: Authorization,
59+
auth_handler_name: str,
60+
context: TurnContext,
61+
auth_token: Optional[str] = None,
62+
) -> None:
63+
"""
64+
Add new MCP servers to the agent from MCP Platform.
65+
66+
Note: Modifies the provided agent in place to add new MCP tool servers.
67+
68+
Args:
69+
agent: The existing agent to add servers to.
70+
auth: Authorization object used to exchange tokens for MCP server access.
71+
auth_handler_name: Name of the authorization handler.
72+
context: TurnContext object representing the current turn/session context.
73+
auth_token: Authentication token to access the MCP servers.
74+
If not provided, will be obtained using `auth` and `context`.
75+
76+
Returns:
77+
None
78+
"""
79+
if not auth_token:
80+
scopes = get_mcp_platform_authentication_scope()
81+
auth_token_obj = await auth.exchange_token(context, scopes, auth_handler_name)
82+
auth_token = auth_token_obj.token
83+
84+
agentic_app_id = Utility.resolve_agent_identity(context, auth_token)
85+
self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")
86+
87+
options = ToolOptions(orchestrator_name=self._orchestrator_name)
88+
mcp_server_configs = await self._mcp_server_configuration_service.list_tool_servers(
89+
agentic_app_id=agentic_app_id,
90+
auth_token=auth_token,
91+
options=options,
92+
)
93+
94+
self._logger.info(f"Loaded {len(mcp_server_configs)} MCP server configurations")
95+
96+
# Collect existing server URLs to prevent duplicates (use set for O(1) lookup)
97+
existing_server_urls = set()
98+
for tool in agent.tools:
99+
# Check if the tool is an McpToolset and has a connection_params.url
100+
if hasattr(tool, "connection_params") and hasattr(tool.connection_params, "url"):
101+
existing_server_urls.add(tool.connection_params.url)
102+
103+
self._logger.debug(f"Found {len(existing_server_urls)} existing MCP servers in agent")
104+
105+
# Convert MCP server configs to McpToolset objects (only new ones)
106+
mcp_servers_info = []
107+
mcp_server_headers = {
108+
Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}",
109+
Constants.Headers.USER_AGENT: Utility.get_user_agent_header(self._orchestrator_name),
110+
}
111+
112+
for server_config in mcp_server_configs:
113+
# Skip if server URL already exists
114+
if server_config.url in existing_server_urls:
115+
self._logger.debug(
116+
f"Skipping MCP server '{server_config.mcp_server_name}' "
117+
f"at {server_config.url} - already exists in agent"
118+
)
119+
continue
120+
121+
try:
122+
server_info = McpToolset(
123+
connection_params=StreamableHTTPConnectionParams(
124+
url=server_config.url,
125+
headers=mcp_server_headers,
126+
)
127+
)
128+
129+
mcp_servers_info.append(server_info)
130+
self._connected_servers.append(server_info)
131+
existing_server_urls.add(server_config.url)
132+
self._logger.info(
133+
f"Created MCP toolset for '{server_config.mcp_server_name}' "
134+
f"at {server_config.url}"
135+
)
136+
137+
except (ConnectionError, TimeoutError, ValueError) as tool_ex:
138+
# Expected connection/configuration errors
139+
self._logger.warning(
140+
f"Failed to create MCP toolset for '{server_config.mcp_server_name}': {tool_ex}"
141+
)
142+
continue
143+
except Exception as tool_ex:
144+
# Unexpected errors - log at ERROR level with full traceback
145+
self._logger.error(
146+
f"Unexpected error creating MCP toolset for '{server_config.mcp_server_name}': {tool_ex}",
147+
exc_info=True,
148+
)
149+
continue
150+
151+
# Only modify agent.tools if we have new servers to add
152+
if mcp_servers_info:
153+
all_tools = list(agent.tools) + mcp_servers_info
154+
agent.tools = all_tools
155+
self._logger.info(
156+
f"Successfully configured agent with {len(mcp_servers_info)} new MCP tool servers "
157+
f"(total tools: {len(all_tools)})"
158+
)
159+
else:
160+
self._logger.info("No new MCP servers to add to agent")
161+
162+
async def cleanup(self):
163+
"""Clean up any resources used by the service."""
164+
try:
165+
for toolset in self._connected_servers:
166+
try:
167+
if hasattr(toolset, "close"):
168+
await toolset.close()
169+
except Exception as cleanup_ex:
170+
self._logger.debug(f"Error during cleanup: {cleanup_ex}")
171+
self._connected_servers.clear()
172+
except Exception as ex:
173+
self._logger.debug(f"Error during service cleanup: {ex}")

0 commit comments

Comments
 (0)