diff --git a/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py b/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py index bd5341b9..9a26ccc1 100644 --- a/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py +++ b/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py @@ -86,7 +86,7 @@ def resolve_agent_identity(context: Any, auth_token: Optional[str]) -> str: return Utility.get_app_id_from_token(auth_token) @staticmethod - def get_user_agent_header(orchestrator: str = "") -> str: + def get_user_agent_header(orchestrator: Optional[str] = None) -> str: """ Generates a User-Agent header string for SDK requests. diff --git a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py index 2b2ea04f..267680d6 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/microsoft_agents_a365/tooling/extensions/agentframework/services/mcp_tool_registration_service.py @@ -13,6 +13,7 @@ from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import ( McpToolServerConfigurationService, ) +from microsoft_agents_a365.tooling.models import ToolOptions from microsoft_agents_a365.tooling.utils.constants import Constants from microsoft_agents_a365.tooling.utils.utility import ( @@ -28,6 +29,8 @@ class McpToolRegistrationService: tool servers with Agent Framework agents. """ + _orchestrator_name: str = "AgentFramework" + def __init__(self, logger: Optional[logging.Logger] = None): """ Initialize the MCP Tool Registration Service for Agent Framework. @@ -77,10 +80,13 @@ async def add_tool_servers_to_agent( self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}") + options = ToolOptions(orchestrator_name=self._orchestrator_name) + # Get MCP server configurations server_configs = await self._mcp_server_configuration_service.list_tool_servers( agentic_app_id=agentic_app_id, auth_token=auth_token, + options=options, ) self._logger.info(f"Loaded {len(server_configs)} MCP server configurations") @@ -105,6 +111,10 @@ async def add_tool_servers_to_agent( f"{Constants.Headers.BEARER_PREFIX} {auth_token}" ) + headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header( + self._orchestrator_name + ) + server_name = getattr(config, "mcp_server_name", "Unknown") # Create and configure MCPStreamableHTTPTool diff --git a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py index 22816ce1..1342b870 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/microsoft_agents_a365/tooling/extensions/azureaifoundry/services/mcp_tool_registration_service.py @@ -21,6 +21,7 @@ from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import ( McpToolServerConfigurationService, ) +from microsoft_agents_a365.tooling.models import ToolOptions from microsoft_agents_a365.tooling.utils.constants import Constants from microsoft_agents_a365.tooling.utils.utility import get_mcp_platform_authentication_scope @@ -45,6 +46,8 @@ class McpToolRegistrationService: >>> service.add_tool_servers_to_agent(project_client, agent_id, token) """ + _orchestrator_name: str = "AzureAIFoundry" + def __init__( self, logger: Optional[logging.Logger] = None, @@ -139,9 +142,10 @@ async def _get_mcp_tool_definitions_and_resources( return ([], None) # Get MCP server configurations + options = ToolOptions(orchestrator_name=self._orchestrator_name) try: servers = await self._mcp_server_configuration_service.list_tool_servers( - agentic_app_id, auth_token + agentic_app_id, auth_token, options ) except Exception as ex: self._logger.error( @@ -189,6 +193,10 @@ async def _get_mcp_tool_definitions_and_resources( ) mcp_tool.update_headers(Constants.Headers.AUTHORIZATION, header_value) + mcp_tool.update_headers( + Constants.Headers.USER_AGENT, Utility.get_user_agent_header(self._orchestrator_name) + ) + # Add to collections tool_definitions.extend(mcp_tool.definitions) if mcp_tool.resources and mcp_tool.resources.mcp: diff --git a/libraries/microsoft-agents-a365-tooling-extensions-openai/microsoft_agents_a365/tooling/extensions/openai/mcp_tool_registration_service.py b/libraries/microsoft-agents-a365-tooling-extensions-openai/microsoft_agents_a365/tooling/extensions/openai/mcp_tool_registration_service.py index eca9bede..1d6e6cc9 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-openai/microsoft_agents_a365/tooling/extensions/openai/mcp_tool_registration_service.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-openai/microsoft_agents_a365/tooling/extensions/openai/mcp_tool_registration_service.py @@ -17,6 +17,8 @@ McpToolServerConfigurationService, ) +from microsoft_agents_a365.tooling.models import ToolOptions +from microsoft_agents_a365.tooling.utils.constants import Constants from microsoft_agents_a365.tooling.utils.utility import ( get_mcp_platform_authentication_scope, ) @@ -38,6 +40,8 @@ class MCPServerInfo: class McpToolRegistrationService: """Service for managing MCP tools and servers for an agent""" + _orchestrator_name: str = "OpenAI" + def __init__(self, logger: Optional[logging.Logger] = None): """ Initialize the MCP Tool Registration Service for OpenAI. @@ -83,11 +87,13 @@ async def add_tool_servers_to_agent( # mcp_server_configs = [] # TODO: radevika: Update once the common project is merged. + options = ToolOptions(orchestrator_name=self._orchestrator_name) agentic_app_id = Utility.resolve_agent_identity(context, auth_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, + options=options, ) self._logger.info(f"Loaded {len(mcp_server_configs)} MCP server configurations") @@ -132,7 +138,13 @@ async def add_tool_servers_to_agent( # Prepare headers with authorization headers = si.headers or {} if auth_token: - headers["Authorization"] = f"Bearer {auth_token}" + headers[Constants.Headers.AUTHORIZATION] = ( + f"{Constants.Headers.BEARER_PREFIX} {auth_token}" + ) + + headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header( + self._orchestrator_name + ) # Create MCPServerStreamableHttpParams with proper configuration params = MCPServerStreamableHttpParams(url=si.url, headers=headers) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/microsoft_agents_a365/tooling/extensions/semantickernel/services/mcp_tool_registration_service.py b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/microsoft_agents_a365/tooling/extensions/semantickernel/services/mcp_tool_registration_service.py index 836b27a1..e0b43e8c 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/microsoft_agents_a365/tooling/extensions/semantickernel/services/mcp_tool_registration_service.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/microsoft_agents_a365/tooling/extensions/semantickernel/services/mcp_tool_registration_service.py @@ -20,7 +20,7 @@ from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import ( McpToolServerConfigurationService, ) -from microsoft_agents_a365.tooling.models.mcp_server_config import MCPServerConfig +from microsoft_agents_a365.tooling.models import MCPServerConfig, ToolOptions from microsoft_agents_a365.tooling.utils.constants import Constants from microsoft_agents_a365.tooling.utils.utility import ( get_tools_mode, @@ -36,6 +36,8 @@ class McpToolRegistrationService: tool servers with Semantic Kernel agents. """ + _orchestrator_name: str = "SemanticKernel" + def __init__( self, logger: Optional[logging.Logger] = None, @@ -107,8 +109,9 @@ async def add_tool_servers_to_agent( self._validate_inputs(kernel, agentic_app_id, auth_token) # Get and process servers + options = ToolOptions(orchestrator_name=self._orchestrator_name) servers = await self._mcp_server_configuration_service.list_tool_servers( - agentic_app_id, auth_token + agentic_app_id, auth_token, options ) self._logger.info(f"🔧 Adding MCP tools from {len(servers)} servers") @@ -132,6 +135,10 @@ async def add_tool_servers_to_agent( Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}", } + headers[Constants.Headers.USER_AGENT] = Utility.get_user_agent_header( + self._orchestrator_name + ) + plugin = MCPStreamableHttpPlugin( name=server.mcp_server_name, url=server.mcp_server_unique_name, diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/__init__.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/__init__.py index fc83626d..23785962 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/__init__.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/__init__.py @@ -7,5 +7,6 @@ """ from .mcp_server_config import MCPServerConfig +from .tool_options import ToolOptions -__all__ = ["MCPServerConfig"] +__all__ = ["MCPServerConfig", "ToolOptions"] diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/tool_options.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/tool_options.py new file mode 100644 index 00000000..9c5c7604 --- /dev/null +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/tool_options.py @@ -0,0 +1,16 @@ +# Copyright (c) Microsoft. All rights reserved. + +""" +Tooling Options model. +""" + +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class ToolOptions: + """Configuration options for tooling operations.""" + + #: Gets or sets the name of the orchestrator. + orchestrator_name: Optional[str] diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py index df6c1830..aec53f2e 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py @@ -28,10 +28,13 @@ import aiohttp # Local imports -from ..models import MCPServerConfig +from ..models import MCPServerConfig, ToolOptions from ..utils import Constants from ..utils.utility import get_tooling_gateway_for_digital_worker, build_mcp_server_url +# Runtime Imports +from microsoft_agents_a365.runtime.utility import Utility as RuntimeUtility + # ============================================================================== # MAIN SERVICE CLASS @@ -66,7 +69,7 @@ def __init__(self, logger: Optional[logging.Logger] = None): # -------------------------------------------------------------------------- async def list_tool_servers( - self, agentic_app_id: str, auth_token: str + self, agentic_app_id: str, auth_token: str, options: Optional[ToolOptions] = None ) -> List[MCPServerConfig]: """ Gets the list of MCP Servers that are configured for the agent. @@ -74,6 +77,7 @@ async def list_tool_servers( Args: agentic_app_id: Agentic App ID for the agent. auth_token: Authentication token to access the MCP servers. + options: Optional ToolOptions instance containing optional parameters. Returns: List[MCPServerConfig]: Returns the list of MCP Servers that are configured. @@ -85,13 +89,17 @@ async def list_tool_servers( # Validate input parameters self._validate_input_parameters(agentic_app_id, auth_token) + # Use default options if none provided + if options is None: + options = ToolOptions(orchestrator_name=None) + self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}") # Determine configuration source based on environment if self._is_development_scenario(): return self._load_servers_from_manifest() else: - return await self._load_servers_from_gateway(agentic_app_id, auth_token) + return await self._load_servers_from_gateway(agentic_app_id, auth_token, options) # -------------------------------------------------------------------------- # ENVIRONMENT DETECTION @@ -275,7 +283,7 @@ def _log_manifest_search_failure(self) -> None: # -------------------------------------------------------------------------- async def _load_servers_from_gateway( - self, agentic_app_id: str, auth_token: str + self, agentic_app_id: str, auth_token: str, options: ToolOptions ) -> List[MCPServerConfig]: """ Reads MCP server configurations from tooling gateway endpoint for production scenario. @@ -283,6 +291,7 @@ async def _load_servers_from_gateway( Args: agentic_app_id: Agentic App ID for the agent. auth_token: Authentication token to access the tooling gateway. + options: ToolOptions instance containing optional parameters. Returns: List[MCPServerConfig]: List of MCP server configurations from tooling gateway. @@ -294,7 +303,7 @@ async def _load_servers_from_gateway( try: config_endpoint = get_tooling_gateway_for_digital_worker(agentic_app_id) - headers = self._prepare_gateway_headers(auth_token) + headers = self._prepare_gateway_headers(auth_token, options) self._logger.info(f"Calling tooling gateway endpoint: {config_endpoint}") @@ -323,18 +332,22 @@ async def _load_servers_from_gateway( return mcp_servers - def _prepare_gateway_headers(self, auth_token: str) -> Dict[str, str]: + def _prepare_gateway_headers(self, auth_token: str, options: ToolOptions) -> Dict[str, str]: """ Prepares headers for tooling gateway requests. Args: auth_token: Authentication token. + options: ToolOptions instance containing optional parameters. Returns: Dictionary of HTTP headers. """ return { - "Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}", + Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}", + Constants.Headers.USER_AGENT: RuntimeUtility.get_user_agent_header( + options.orchestrator_name + ), } async def _parse_gateway_response( diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/constants.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/constants.py index eabb0f2d..46f3e03c 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/constants.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/constants.py @@ -20,3 +20,6 @@ class Headers: #: The prefix used for Bearer authentication tokens in HTTP headers. BEARER_PREFIX = "Bearer" + + #: The header name for User-Agent information. + USER_AGENT = "User-Agent"