From 73e74f8459467456768ea921f379c94f4224a1ab Mon Sep 17 00:00:00 2001 From: Jesus Terrazas Date: Wed, 19 Nov 2025 16:58:00 -0800 Subject: [PATCH 1/5] working google adk sample on terminal --- python/google-adk/sample-agent/.env.template | 4 + .../sample-agent/ToolingManifest.json | 8 ++ python/google-adk/sample-agent/agent.py | 65 ++++++++++++++ .../mcp_tool_registration_service.py | 90 +++++++++++++++++++ python/google-adk/sample-agent/pyproject.toml | 65 ++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 python/google-adk/sample-agent/.env.template create mode 100644 python/google-adk/sample-agent/ToolingManifest.json create mode 100644 python/google-adk/sample-agent/agent.py create mode 100644 python/google-adk/sample-agent/mcp_tool_registration_service.py create mode 100644 python/google-adk/sample-agent/pyproject.toml diff --git a/python/google-adk/sample-agent/.env.template b/python/google-adk/sample-agent/.env.template new file mode 100644 index 00000000..db31fae3 --- /dev/null +++ b/python/google-adk/sample-agent/.env.template @@ -0,0 +1,4 @@ +GOOGLE_GENAI_USE_VERTEXAI=FALSE +GOOGLE_API_KEY= + +BEARER_TOKEN= \ No newline at end of file diff --git a/python/google-adk/sample-agent/ToolingManifest.json b/python/google-adk/sample-agent/ToolingManifest.json new file mode 100644 index 00000000..9d5cacf2 --- /dev/null +++ b/python/google-adk/sample-agent/ToolingManifest.json @@ -0,0 +1,8 @@ +{ + "mcpServers": [ + { + "mcpServerName": "mcp_MailTools", + "mcpServerUniqueName": "mcp_MailTools" + } + ] +} \ No newline at end of file diff --git a/python/google-adk/sample-agent/agent.py b/python/google-adk/sample-agent/agent.py new file mode 100644 index 00000000..d9b1b4f0 --- /dev/null +++ b/python/google-adk/sample-agent/agent.py @@ -0,0 +1,65 @@ +import asyncio +import os +from google.adk.agents import Agent +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +from mcp_tool_registration_service import McpToolRegistrationService + +from google.adk.runners import Runner +from google.adk.sessions.in_memory_session_service import InMemorySessionService + + +async def main(): + # Google ADK expects root_agent to be defined at module level + # Create the base agent synchronously + my_agent = Agent( + name="my_agent", + model="gemini-2.0-flash", + description=( + "Agent to test Mcp tools." + ), + instruction=( + "You are a helpful agent who can use tools. If you encounter any errors, please provide the exact error message you encounter." + ), + ) + + toolService = McpToolRegistrationService() + + my_agent = await toolService.add_tool_servers_to_agent( + agent=my_agent, + agentic_app_id=os.getenv("AGENTIC_APP_ID", "agent123"), + auth=None, + context=None, + auth_token=os.getenv("BEARER_TOKEN", ""), + ) + + # Create runner + runner = Runner( + app_name="agents", + agent=my_agent, + session_service=InMemorySessionService(), + ) + + # Run agent + try: + _ = await runner.run_debug( + user_messages=["Send alias@example.com an email with a dad joke."] + ) + finally: + agentTools = my_agent.tools + for tool in agentTools: + if hasattr(tool, "close"): + await tool.close() + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + print("\nShutting down gracefully...") + except Exception as e: + # Ignore cleanup errors during shutdown + if "cancel scope" not in str(e) and "RuntimeError" not in type(e).__name__: + raise \ No newline at end of file diff --git a/python/google-adk/sample-agent/mcp_tool_registration_service.py b/python/google-adk/sample-agent/mcp_tool_registration_service.py new file mode 100644 index 00000000..ae72a233 --- /dev/null +++ b/python/google-adk/sample-agent/mcp_tool_registration_service.py @@ -0,0 +1,90 @@ +# Copyright (c) Microsoft. All rights reserved. + +from typing import Optional +import logging + +from google.adk.agents import Agent +from google.adk.tools.mcp_tool.mcp_toolset import McpToolset, StreamableHTTPConnectionParams + +from microsoft_agents.hosting.core import Authorization, TurnContext + +from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import ( + McpToolServerConfigurationService, +) + +from microsoft_agents_a365.tooling.utils.utility import ( + get_mcp_platform_authentication_scope, +) + +class McpToolRegistrationService: + """Service for managing MCP tools and servers for an agent""" + + def __init__(self, logger: Optional[logging.Logger] = None): + """ + Initialize the MCP Tool Registration Service for OpenAI. + + Args: + logger: Logger instance for logging operations. + """ + self._logger = logger or logging.getLogger(self.__class__.__name__) + self.config_service = McpToolServerConfigurationService(logger=self._logger) + + async def add_tool_servers_to_agent( + self, + agent: Agent, + agentic_app_id: str, + auth: Authorization, + context: TurnContext, + auth_token: Optional[str] = None, + ): + """ + Add new MCP servers to the agent by creating a new Agent instance. + + Note: This method creates a new Agent instance with MCP servers configured. + + Args: + agent: The existing agent to add servers to + agentic_app_id: Agentic App ID for the agent + auth_token: Authentication token to access the MCP servers + + Returns: + New Agent instance with all MCP servers, or original agent if no new servers + """ + + if not auth_token: + scopes = get_mcp_platform_authentication_scope() + authToken = await auth.exchange_token(context, scopes, "AGENTIC") + auth_token = authToken.token + + self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}") + mcp_server_configs = await self.config_service.list_tool_servers( + agentic_app_id=agentic_app_id, + auth_token=auth_token + ) + + self._logger.info(f"Loaded {len(mcp_server_configs)} MCP server configurations") + + # Convert MCP server configs to MCPServerInfo objects + mcp_servers_info = [] + mcp_server_headers = { + "Authorization": f"Bearer {auth_token}" + } + + for server_config in mcp_server_configs: + server_info = McpToolset( + connection_params=StreamableHTTPConnectionParams( + url=server_config.mcp_server_unique_name, + headers=mcp_server_headers + ) + ) + + mcp_servers_info.append(server_info) + + allTools = agent.tools + mcp_servers_info + + return Agent( + name=agent.name, + model=agent.model, + description=agent.description, + tools=allTools, + ) diff --git a/python/google-adk/sample-agent/pyproject.toml b/python/google-adk/sample-agent/pyproject.toml new file mode 100644 index 00000000..653c00a0 --- /dev/null +++ b/python/google-adk/sample-agent/pyproject.toml @@ -0,0 +1,65 @@ +[project] +name = "sample-google-adk" +version = "0.1.0" +description = "Sample Google ADK Agent using Microsoft Agent 365 SDK" +authors = [ + { name = "Microsoft", email = "support@microsoft.com" } +] +dependencies = [ + # Google ADK -- official package + "google-adk", + + # Microsoft Agents SDK - Official packages for hosting and integration + "microsoft-agents-hosting-aiohttp", + "microsoft-agents-hosting-core", + "microsoft-agents-authentication-msal", + "microsoft-agents-activity", + + # Core dependencies + "python-dotenv", + "aiohttp", + + # HTTP server support for MCP servers + "uvicorn[standard]>=0.20.0", + "fastapi>=0.100.0", + + # HTTP client + "httpx>=0.24.0", + + # Data validation + "pydantic>=2.0.0", + + # Additional utilities + "typing-extensions>=4.0.0", + + # Microsoft Agent 365 SDK packages + "microsoft_agents_a365_tooling >= 0.1.0", +] +requires-python = ">=3.11" + +# Package index configuration +# PyPI is the default/primary source, local packages are fallback +[[tool.uv.index]] +name = "pypi" +url = "https://pypi.org/simple" +default = true + +[project.optional-dependencies] +dev = [ + # For development and testing + "pytest>=7.0.0", + "pytest-asyncio>=0.21.0", +] + +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +# Don't include any Python modules in the package since this is a sample/script collection +py-modules = [] + +[tool.setuptools.packages.find] +where = ["."] +include = ["*"] +exclude = ["build*", "dist*", "venv*"] From ade85539bfbe2490a5c74e1cbabbedb2e2404c9c Mon Sep 17 00:00:00 2001 From: Jesus Terrazas Date: Wed, 19 Nov 2025 17:59:01 -0800 Subject: [PATCH 2/5] add observability --- python/google-adk/sample-agent/agent.py | 18 +++++++++++++++--- python/google-adk/sample-agent/pyproject.toml | 3 ++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/python/google-adk/sample-agent/agent.py b/python/google-adk/sample-agent/agent.py index d9b1b4f0..129b9cbf 100644 --- a/python/google-adk/sample-agent/agent.py +++ b/python/google-adk/sample-agent/agent.py @@ -8,6 +8,11 @@ from mcp_tool_registration_service import McpToolRegistrationService +from microsoft_agents_a365.observability.core.config import configure +from microsoft_agents_a365.observability.core.middleware.baggage_builder import ( + BaggageBuilder, +) + from google.adk.runners import Runner from google.adk.sessions.in_memory_session_service import InMemorySessionService @@ -45,9 +50,11 @@ async def main(): # Run agent try: - _ = await runner.run_debug( - user_messages=["Send alias@example.com an email with a dad joke."] - ) + user_message = "Send alias@example.com an email with a dad joke." + with BaggageBuilder().tenant_id("your-tenant-id").agent_id("agent123").build(): + _ = await runner.run_debug( + user_messages=[user_message] + ) finally: agentTools = my_agent.tools for tool in agentTools: @@ -55,6 +62,11 @@ async def main(): await tool.close() if __name__ == "__main__": + configure( + service_name="GoogleADKSampleAgent", + service_namespace="GoogleADKTesting", + ) + try: asyncio.run(main()) except KeyboardInterrupt: diff --git a/python/google-adk/sample-agent/pyproject.toml b/python/google-adk/sample-agent/pyproject.toml index 653c00a0..8c7b240e 100644 --- a/python/google-adk/sample-agent/pyproject.toml +++ b/python/google-adk/sample-agent/pyproject.toml @@ -34,11 +34,12 @@ dependencies = [ # Microsoft Agent 365 SDK packages "microsoft_agents_a365_tooling >= 0.1.0", + "microsoft_agents_a365_observability_core >= 0.1.0", ] requires-python = ">=3.11" # Package index configuration -# PyPI is the default/primary source, local packages are fallback +# PyPI is the default/primary source [[tool.uv.index]] name = "pypi" url = "https://pypi.org/simple" From fd0b7dc6085fc07aac7e1f904583e9fdfc10d3b7 Mon Sep 17 00:00:00 2001 From: Jesus Terrazas Date: Wed, 19 Nov 2025 18:12:06 -0800 Subject: [PATCH 3/5] add agentic auth (no bearer token) --- python/google-adk/sample-agent/.env.template | 21 +++++++++++- python/google-adk/sample-agent/agent.py | 34 ++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/python/google-adk/sample-agent/.env.template b/python/google-adk/sample-agent/.env.template index db31fae3..941e996f 100644 --- a/python/google-adk/sample-agent/.env.template +++ b/python/google-adk/sample-agent/.env.template @@ -1,4 +1,23 @@ GOOGLE_GENAI_USE_VERTEXAI=FALSE GOOGLE_API_KEY= -BEARER_TOKEN= \ No newline at end of file +BEARER_TOKEN= + +# Agent365 Agentic Authentication Configuration +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID= +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET= +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID= +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__SCOPES=https://api.botframework.com/.default + +AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__TYPE=AgenticUserAuthorization +AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__SCOPES=https://graph.microsoft.com/.default +AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__ALTERNATEBLUEPRINTCONNECTIONNAME=https://graph.microsoft.com/.default + +CONNECTIONSMAP__0__SERVICEURL=* +CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION + +AGENTIC_UPN= +AGENTIC_NAME= +AGENTIC_USER_ID= +AGENTIC_APP_ID= +AGENTIC_TENANT_ID= \ No newline at end of file diff --git a/python/google-adk/sample-agent/agent.py b/python/google-adk/sample-agent/agent.py index 129b9cbf..d7bc945f 100644 --- a/python/google-adk/sample-agent/agent.py +++ b/python/google-adk/sample-agent/agent.py @@ -16,6 +16,12 @@ from google.adk.runners import Runner from google.adk.sessions.in_memory_session_service import InMemorySessionService +from microsoft_agents.activity import load_configuration_from_env, Activity, ChannelAccount, ActivityTypes +from microsoft_agents.hosting.core import Authorization, MemoryStorage, TurnContext +from microsoft_agents.hosting.aiohttp import CloudAdapter +from microsoft_agents.authentication.msal import MsalConnectionManager + +agents_sdk_config = load_configuration_from_env(os.environ) async def main(): # Google ADK expects root_agent to be defined at module level @@ -31,13 +37,35 @@ async def main(): ), ) + auth = Authorization( + storage=MemoryStorage(), + connection_manager=MsalConnectionManager(**agents_sdk_config), + **agents_sdk_config + ) + + turnContext = TurnContext( + adapter_or_context=CloudAdapter(), + request=Activity( + type=ActivityTypes.message, + text="", + recipient=ChannelAccount( + id=os.getenv("AGENTIC_UPN", ""), + name=os.getenv("AGENTIC_NAME", ""), + agentic_user_id=os.getenv("AGENTIC_USER_ID", ""), + agentic_app_id=os.getenv("AGENTIC_APP_ID", ""), + tenant_id=os.getenv("AGENTIC_TENANT_ID", ""), + role="agenticUser" + ) + ) + ) + toolService = McpToolRegistrationService() my_agent = await toolService.add_tool_servers_to_agent( agent=my_agent, agentic_app_id=os.getenv("AGENTIC_APP_ID", "agent123"), - auth=None, - context=None, + auth=auth, + context=turnContext, auth_token=os.getenv("BEARER_TOKEN", ""), ) @@ -50,7 +78,7 @@ async def main(): # Run agent try: - user_message = "Send alias@example.com an email with a dad joke." + user_message = input("Enter your message to the agent: ") with BaggageBuilder().tenant_id("your-tenant-id").agent_id("agent123").build(): _ = await runner.run_debug( user_messages=[user_message] From 747cb4284503f398aa9d6bae0f6221e36b8328a2 Mon Sep 17 00:00:00 2001 From: Jesus Terrazas Date: Wed, 19 Nov 2025 18:17:06 -0800 Subject: [PATCH 4/5] copilot suggestions --- python/google-adk/sample-agent/.env.template | 1 + python/google-adk/sample-agent/agent.py | 10 ++++++---- .../mcp_tool_registration_service.py | 20 ++++++++++--------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/python/google-adk/sample-agent/.env.template b/python/google-adk/sample-agent/.env.template index 941e996f..1b81bd68 100644 --- a/python/google-adk/sample-agent/.env.template +++ b/python/google-adk/sample-agent/.env.template @@ -16,6 +16,7 @@ AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__ALTERNATEBLUEP CONNECTIONSMAP__0__SERVICEURL=* CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION +# These values are expected to be in the activity's recipient field AGENTIC_UPN= AGENTIC_NAME= AGENTIC_USER_ID= diff --git a/python/google-adk/sample-agent/agent.py b/python/google-adk/sample-agent/agent.py index d7bc945f..acf9e59d 100644 --- a/python/google-adk/sample-agent/agent.py +++ b/python/google-adk/sample-agent/agent.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + import asyncio import os from google.adk.agents import Agent @@ -59,9 +61,9 @@ async def main(): ) ) - toolService = McpToolRegistrationService() + tool_service = McpToolRegistrationService() - my_agent = await toolService.add_tool_servers_to_agent( + my_agent = await tool_service.add_tool_servers_to_agent( agent=my_agent, agentic_app_id=os.getenv("AGENTIC_APP_ID", "agent123"), auth=auth, @@ -84,8 +86,8 @@ async def main(): user_messages=[user_message] ) finally: - agentTools = my_agent.tools - for tool in agentTools: + agent_tools = my_agent.tools + for tool in agent_tools: if hasattr(tool, "close"): await tool.close() diff --git a/python/google-adk/sample-agent/mcp_tool_registration_service.py b/python/google-adk/sample-agent/mcp_tool_registration_service.py index ae72a233..08ee24c6 100644 --- a/python/google-adk/sample-agent/mcp_tool_registration_service.py +++ b/python/google-adk/sample-agent/mcp_tool_registration_service.py @@ -21,7 +21,7 @@ class McpToolRegistrationService: def __init__(self, logger: Optional[logging.Logger] = None): """ - Initialize the MCP Tool Registration Service for OpenAI. + Initialize the MCP Tool Registration Service for Google ADK. Args: logger: Logger instance for logging operations. @@ -43,18 +43,20 @@ async def add_tool_servers_to_agent( Note: This method creates a new Agent instance with MCP servers configured. Args: - agent: The existing agent to add servers to - agentic_app_id: Agentic App ID for the agent - auth_token: Authentication token to access the MCP servers + agent: The existing agent to add servers to. + agentic_app_id: Agentic App ID for the agent. + auth: Authorization object used to exchange tokens for MCP server access. + context: TurnContext object representing the current turn/session context. + auth_token: Authentication token to access the MCP servers. If not provided, will be obtained using `auth` and `context`. Returns: - New Agent instance with all MCP servers, or original agent if no new servers + New Agent instance with all MCP servers """ if not auth_token: scopes = get_mcp_platform_authentication_scope() - authToken = await auth.exchange_token(context, scopes, "AGENTIC") - auth_token = authToken.token + auth_token_obj = await auth.exchange_token(context, scopes, "AGENTIC") + auth_token = auth_token_obj.token self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}") mcp_server_configs = await self.config_service.list_tool_servers( @@ -80,11 +82,11 @@ async def add_tool_servers_to_agent( mcp_servers_info.append(server_info) - allTools = agent.tools + mcp_servers_info + all_tools = agent.tools + mcp_servers_info return Agent( name=agent.name, model=agent.model, description=agent.description, - tools=allTools, + tools=all_tools, ) From b7062036a7fdbff15aa84c78adb0d2f05e77f8f1 Mon Sep 17 00:00:00 2001 From: Jesus Terrazas Date: Wed, 19 Nov 2025 18:34:44 -0800 Subject: [PATCH 5/5] Add agentic auth --- python/google-adk/sample-agent/.env.template | 2 -- python/google-adk/sample-agent/agent.py | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/python/google-adk/sample-agent/.env.template b/python/google-adk/sample-agent/.env.template index 1b81bd68..8e9911aa 100644 --- a/python/google-adk/sample-agent/.env.template +++ b/python/google-adk/sample-agent/.env.template @@ -1,8 +1,6 @@ GOOGLE_GENAI_USE_VERTEXAI=FALSE GOOGLE_API_KEY= -BEARER_TOKEN= - # Agent365 Agentic Authentication Configuration CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID= CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET= diff --git a/python/google-adk/sample-agent/agent.py b/python/google-adk/sample-agent/agent.py index acf9e59d..7f5e85e0 100644 --- a/python/google-adk/sample-agent/agent.py +++ b/python/google-adk/sample-agent/agent.py @@ -19,7 +19,7 @@ from google.adk.sessions.in_memory_session_service import InMemorySessionService from microsoft_agents.activity import load_configuration_from_env, Activity, ChannelAccount, ActivityTypes -from microsoft_agents.hosting.core import Authorization, MemoryStorage, TurnContext +from microsoft_agents.hosting.core import Authorization, MemoryStorage, TurnContext, ClaimsIdentity, AuthenticationConstants from microsoft_agents.hosting.aiohttp import CloudAdapter from microsoft_agents.authentication.msal import MsalConnectionManager @@ -50,6 +50,10 @@ async def main(): request=Activity( type=ActivityTypes.message, text="", + from_property=ChannelAccount( + id='user1', + name='User One' + ), recipient=ChannelAccount( id=os.getenv("AGENTIC_UPN", ""), name=os.getenv("AGENTIC_NAME", ""), @@ -58,9 +62,21 @@ async def main(): tenant_id=os.getenv("AGENTIC_TENANT_ID", ""), role="agenticUser" ) + ), + identity=ClaimsIdentity( + { + AuthenticationConstants.AUDIENCE_CLAIM: "anonymous", + AuthenticationConstants.APP_ID_CLAIM: "anonymous-app", + }, + False, + "Anonymous", ) ) + if not (await auth._start_or_continue_sign_in(turnContext, None, 'AGENTIC')).sign_in_complete(): + print("Sign-in required. Exiting.") + return + tool_service = McpToolRegistrationService() my_agent = await tool_service.add_tool_servers_to_agent(