Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions python/google-adk/sample-agent/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
GOOGLE_GENAI_USE_VERTEXAI=FALSE
GOOGLE_API_KEY=

# 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

# These values are expected to be in the activity's recipient field
AGENTIC_UPN=
AGENTIC_NAME=
AGENTIC_USER_ID=
AGENTIC_APP_ID=
AGENTIC_TENANT_ID=
8 changes: 8 additions & 0 deletions python/google-adk/sample-agent/ToolingManifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": [
{
"mcpServerName": "mcp_MailTools",
"mcpServerUniqueName": "mcp_MailTools"
}
]
}
123 changes: 123 additions & 0 deletions python/google-adk/sample-agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Copyright (c) Microsoft. All rights reserved.

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 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

from microsoft_agents.activity import load_configuration_from_env, Activity, ChannelAccount, ActivityTypes
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

agents_sdk_config = load_configuration_from_env(os.environ)

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."
),
)

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="",
from_property=ChannelAccount(
id='user1',
name='User One'
),
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"
)
),
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(
agent=my_agent,
agentic_app_id=os.getenv("AGENTIC_APP_ID", "agent123"),
auth=auth,
context=turnContext,
auth_token=os.getenv("BEARER_TOKEN", ""),
)

# Create runner
runner = Runner(
app_name="agents",
agent=my_agent,
session_service=InMemorySessionService(),
)

# Run agent
try:
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]
)
finally:
agent_tools = my_agent.tools
for tool in agent_tools:
if hasattr(tool, "close"):
await tool.close()

if __name__ == "__main__":
configure(
service_name="GoogleADKSampleAgent",
service_namespace="GoogleADKTesting",
)

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
92 changes: 92 additions & 0 deletions python/google-adk/sample-agent/mcp_tool_registration_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 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 Google ADK.

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: 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
"""

if not auth_token:
scopes = get_mcp_platform_authentication_scope()
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(
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)

all_tools = agent.tools + mcp_servers_info

return Agent(
name=agent.name,
model=agent.model,
description=agent.description,
tools=all_tools,
)
66 changes: 66 additions & 0 deletions python/google-adk/sample-agent/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[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",
"microsoft_agents_a365_observability_core >= 0.1.0",
]
requires-python = ">=3.11"

# Package index configuration
# PyPI is the default/primary source
[[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*"]