From c6473e7561bcbc13ebb129a7fac75b0ee2a09538 Mon Sep 17 00:00:00 2001 From: Josh Oratz Date: Thu, 30 Oct 2025 12:49:59 -0700 Subject: [PATCH 1/5] Remove environment id usage --- .../README.md | 1 - .../services/mcp_tool_registration_service.py | 16 +++---- .../README.md | 1 - .../openai/mcp_tool_registration_service.py | 6 +-- .../README.md | 1 - .../services/mcp_tool_registration_service.py | 16 ++----- .../microsoft-agents-a365-tooling/README.md | 1 - .../mcp_tool_server_configuration_service.py | 42 +++++++------------ .../tooling/utils/constants.py | 3 -- .../tooling/utils/utility.py | 15 +++---- 10 files changed, 30 insertions(+), 72 deletions(-) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md index 382ec1cc..53016086 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md +++ b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md @@ -64,7 +64,6 @@ registration_service = McpToolRegistrationService() await registration_service.add_tool_servers_to_agent( project_client=project_client, agent_id="your-agent-id", - environment_id="prod", auth_token="your-auth-token" ) ``` 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 4facda83..2c912c2c 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 @@ -44,7 +44,7 @@ class McpToolRegistrationService: Example: >>> service = McpToolRegistrationService() - >>> service.add_tool_servers_to_agent(project_client, agent_id, env_id, token) + >>> service.add_tool_servers_to_agent(project_client, agent_id, token) """ def __init__( @@ -73,7 +73,6 @@ async def add_tool_servers_to_agent( self, project_client: "AIProjectClient", agent_user_id: str, - environment_id: str, auth: Authorization, context: TurnContext, auth_token: Optional[str] = None, @@ -84,7 +83,6 @@ async def add_tool_servers_to_agent( Args: project_client: The Azure Foundry AIProjectClient instance. agent_user_id: Agent User ID for the agent. - environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Raises: @@ -102,7 +100,7 @@ async def add_tool_servers_to_agent( try: # Get the tool definitions and resources using the async implementation tool_definitions, tool_resources = await self._get_mcp_tool_definitions_and_resources( - agent_user_id, environment_id, auth_token or "" + agent_user_id, auth_token or "" ) # Update the agent with the tools @@ -121,7 +119,7 @@ async def add_tool_servers_to_agent( raise async def _get_mcp_tool_definitions_and_resources( - self, agent_user_id: str, environment_id: str, auth_token: str + self, agent_user_id: str, auth_token: str ) -> Tuple[List[McpTool], Optional[ToolResources]]: """ Internal method to get MCP tool definitions and resources. @@ -130,7 +128,6 @@ async def _get_mcp_tool_definitions_and_resources( Args: agent_user_id: Agent User ID for the agent. - environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Returns: @@ -143,7 +140,7 @@ async def _get_mcp_tool_definitions_and_resources( # Get MCP server configurations try: servers = await self._mcp_server_configuration_service.list_tool_servers( - agent_user_id, environment_id, auth_token + agent_user_id, auth_token ) except Exception as ex: self._logger.error( @@ -153,7 +150,7 @@ async def _get_mcp_tool_definitions_and_resources( if len(servers) == 0: self._logger.info( - f"No MCP servers configured for AgentUserId={agent_user_id}, EnvironmentId={environment_id}" + f"No MCP servers configured for AgentUserId={agent_user_id}" ) return ([], None) @@ -193,9 +190,6 @@ async def _get_mcp_tool_definitions_and_resources( ) mcp_tool.update_headers(Constants.Headers.AUTHORIZATION, header_value) - # Set environment ID header - mcp_tool.update_headers(Constants.Headers.ENVIRONMENT_ID, environment_id) - # 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/README.md b/libraries/microsoft-agents-a365-tooling-extensions-openai/README.md index 5198f310..c9b6e876 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-openai/README.md +++ b/libraries/microsoft-agents-a365-tooling-extensions-openai/README.md @@ -54,7 +54,6 @@ registration_service = McpToolRegistrationService() await registration_service.add_tool_servers_to_agent( agent=your_openai_agent, agent_user_id="user-123", - environment_id="prod", auth=authorization_context, context=turn_context, auth_token="your-auth-token" 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 bfbd8de5..544e2f4b 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 @@ -39,7 +39,6 @@ async def add_tool_servers_to_agent( self, agent: Agent, agent_user_id: str, - environment_id: str, auth: Authorization, context: TurnContext, auth_token: Optional[str] = None, @@ -54,7 +53,6 @@ async def add_tool_servers_to_agent( Args: agent: The existing agent to add servers to agent_user_id: Agent User ID for the agent - environment_id: Environment ID for the environment auth_token: Authentication token to access the MCP servers Returns: @@ -70,7 +68,7 @@ async def add_tool_servers_to_agent( # mcp_server_configs = [] # TODO: radevika: Update once the common project is merged. mcp_server_configs = await self.config_service.list_tool_servers( - agent_user_id=agent_user_id, environment_id=environment_id, auth_token=auth_token + agent_user_id=agent_user_id, auth_token=auth_token ) # Convert MCP server configs to MCPServerInfo objects @@ -114,8 +112,6 @@ async def add_tool_servers_to_agent( headers = si.headers or {} if auth_token: headers["Authorization"] = f"Bearer {auth_token}" - if environment_id: - headers["x-ms-environment-id"] = environment_id # Create MCPServerStreamableHttpParams with proper configuration params = MCPServerStreamableHttpParams(url=si.url, headers=headers) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md index 58b72af5..01ea4629 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md +++ b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md @@ -56,7 +56,6 @@ registration_service = McpToolRegistrationService() await registration_service.add_tool_servers_to_kernel( kernel=kernel, agent_user_id="user-123", - environment_id="prod", auth_token="your-auth-token" ) ``` 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 92e56d9d..fd9208cf 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 @@ -84,7 +84,6 @@ async def add_tool_servers_to_agent( self, kernel: sk.Kernel, agent_user_id: str, - environment_id: str, auth: Authorization, context: TurnContext, auth_token: Optional[str] = None, @@ -95,7 +94,6 @@ async def add_tool_servers_to_agent( Args: kernel: The Semantic Kernel instance to which the tools will be added. agent_user_id: Agent User ID for the agent. - environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Raises: @@ -108,7 +106,7 @@ async def add_tool_servers_to_agent( authToken = await auth.exchange_token(context, scopes, "AGENTIC") auth_token = authToken.token - self._validate_inputs(kernel, agent_user_id, environment_id, auth_token) + self._validate_inputs(kernel, agent_user_id, auth_token) if self._mcp_server_configuration_service is None: raise ValueError( @@ -117,7 +115,7 @@ async def add_tool_servers_to_agent( # Get and process servers servers = await self._mcp_server_configuration_service.list_tool_servers( - agent_user_id, environment_id, auth_token + agent_user_id, auth_token ) self._logger.info(f"🔧 Adding MCP tools from {len(servers)} servers") @@ -134,16 +132,12 @@ async def add_tool_servers_to_agent( headers = {} if tools_mode == "MockMCPServer": - # Mock server does not require bearer auth, but still forward environment id if available. - if environment_id: - headers[Constants.Headers.ENVIRONMENT_ID] = environment_id - + # Use mock authorization header if provided if mock_auth_header := os.getenv("MOCK_MCP_AUTHORIZATION"): headers[Constants.Headers.AUTHORIZATION] = mock_auth_header else: headers = { Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}", - Constants.Headers.ENVIRONMENT_ID: environment_id, } plugin = MCPStreamableHttpPlugin( @@ -178,15 +172,13 @@ async def add_tool_servers_to_agent( # ============================================================================ def _validate_inputs( - self, kernel: Any, agent_user_id: str, environment_id: str, auth_token: str + self, kernel: Any, agent_user_id: str, auth_token: str ) -> None: """Validate all required inputs.""" if kernel is None: raise ValueError("kernel cannot be None") if not agent_user_id or not agent_user_id.strip(): raise ValueError("agent_user_id cannot be null or empty") - if not environment_id or not environment_id.strip(): - raise ValueError("environment_id cannot be null or empty") if not auth_token or not auth_token.strip(): raise ValueError("auth_token cannot be null or empty") diff --git a/libraries/microsoft-agents-a365-tooling/README.md b/libraries/microsoft-agents-a365-tooling/README.md index 2392d33b..56ec6671 100644 --- a/libraries/microsoft-agents-a365-tooling/README.md +++ b/libraries/microsoft-agents-a365-tooling/README.md @@ -59,7 +59,6 @@ mcp_config = MCPServerConfig( config_service = McpToolServerConfigurationService() mcp_servers = await config_service.list_tool_servers( agent_user_id="agent-123", - environment_id="prod", auth_token="your-auth-token" ) ``` 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 562e8c08..03989cfe 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 @@ -66,14 +66,13 @@ def __init__(self, logger: Optional[logging.Logger] = None): # -------------------------------------------------------------------------- async def list_tool_servers( - self, agent_user_id: str, environment_id: str, auth_token: str + self, agent_user_id: str, auth_token: str ) -> List[MCPServerConfig]: """ Gets the list of MCP Servers that are configured for the agent. Args: agent_user_id: Agent User ID for the agent. - environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Returns: @@ -84,17 +83,17 @@ async def list_tool_servers( Exception: If there's an error communicating with the tooling gateway. """ # Validate input parameters - self._validate_input_parameters(agent_user_id, environment_id, auth_token) + self._validate_input_parameters(agent_user_id, auth_token) self._logger.info( - f"Listing MCP tool servers for agent {agent_user_id} in environment {environment_id}" + f"Listing MCP tool servers for agent {agent_user_id}" ) # Determine configuration source based on environment if self._is_development_scenario(): - return self._load_servers_from_manifest(environment_id) + return self._load_servers_from_manifest() else: - return await self._load_servers_from_gateway(agent_user_id, environment_id, auth_token) + return await self._load_servers_from_gateway(agent_user_id, auth_token) # -------------------------------------------------------------------------- # ENVIRONMENT DETECTION @@ -117,7 +116,7 @@ def _is_development_scenario(self) -> bool: # DEVELOPMENT: MANIFEST-BASED CONFIGURATION # -------------------------------------------------------------------------- - def _load_servers_from_manifest(self, environment_id: str) -> List[MCPServerConfig]: + def _load_servers_from_manifest(self) -> List[MCPServerConfig]: """ Reads MCP server configurations from ToolingManifest.json in the application's content root. @@ -137,9 +136,6 @@ def _load_servers_from_manifest(self, environment_id: str) -> List[MCPServerConf ] } - Args: - environment_id: Environment ID to construct full URLs. - Returns: List[MCPServerConfig]: List of MCP server configurations from manifest. @@ -153,7 +149,7 @@ def _load_servers_from_manifest(self, environment_id: str) -> List[MCPServerConf if manifest_path and manifest_path.exists(): self._logger.info(f"Loading MCP servers from: {manifest_path}") - mcp_servers = self._parse_manifest_file(manifest_path, environment_id) + mcp_servers = self._parse_manifest_file(manifest_path) else: self._log_manifest_search_failure() @@ -219,14 +215,13 @@ def _get_manifest_search_locations(self) -> List[Path]: return search_locations def _parse_manifest_file( - self, manifest_path: Path, environment_id: str + self, manifest_path: Path ) -> List[MCPServerConfig]: """ Parses the manifest file and extracts MCP server configurations. Args: manifest_path: Path to the manifest file. - environment_id: Environment ID for URL construction. Returns: List of parsed MCP server configurations. @@ -249,7 +244,7 @@ def _parse_manifest_file( for server_element in mcp_servers_data: print(f"🔧 Processing server element: {server_element}") server_config = self._parse_manifest_server_config( - server_element, environment_id + server_element ) if server_config is not None: print( @@ -286,14 +281,13 @@ def _log_manifest_search_failure(self) -> None: # -------------------------------------------------------------------------- async def _load_servers_from_gateway( - self, agent_user_id: str, environment_id: str, auth_token: str + self, agent_user_id: str, auth_token: str ) -> List[MCPServerConfig]: """ Reads MCP server configurations from tooling gateway endpoint for production scenario. Args: agent_user_id: Agent User ID for the agent. - environment_id: Environment ID for the environment. auth_token: Authentication token to access the tooling gateway. Returns: @@ -306,7 +300,7 @@ async def _load_servers_from_gateway( try: config_endpoint = get_tooling_gateway_for_digital_worker(agent_user_id) - headers = self._prepare_gateway_headers(auth_token, environment_id) + headers = self._prepare_gateway_headers(auth_token) self._logger.info(f"Calling tooling gateway endpoint: {config_endpoint}") @@ -335,20 +329,18 @@ async def _load_servers_from_gateway( return mcp_servers - def _prepare_gateway_headers(self, auth_token: str, environment_id: str) -> Dict[str, str]: + def _prepare_gateway_headers(self, auth_token: str,) -> Dict[str, str]: """ Prepares headers for tooling gateway requests. Args: auth_token: Authentication token. - environment_id: Environment ID. Returns: Dictionary of HTTP headers. """ return { "Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}", - Constants.Headers.ENVIRONMENT_ID: environment_id, } async def _parse_gateway_response( @@ -381,14 +373,13 @@ async def _parse_gateway_response( # -------------------------------------------------------------------------- def _parse_manifest_server_config( - self, server_element: Dict[str, Any], environment_id: str + self, server_element: Dict[str, Any] ) -> Optional[MCPServerConfig]: """ Parses a server configuration from manifest data, constructing full URL. Args: server_element: Dictionary containing server configuration from manifest. - environment_id: Environment ID to construct full URL. Returns: MCPServerConfig object or None if parsing fails. @@ -401,7 +392,7 @@ def _parse_manifest_server_config( return None # Construct full URL using environment utilities - full_url = build_mcp_server_url(environment_id, server_name) + full_url = build_mcp_server_url(server_name) return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=full_url) @@ -437,14 +428,13 @@ def _parse_gateway_server_config( # -------------------------------------------------------------------------- def _validate_input_parameters( - self, agent_user_id: str, environment_id: str, auth_token: str + self, agent_user_id: str, auth_token: str ) -> None: """ Validates input parameters for the main API method. Args: agent_user_id: Agent User ID to validate. - environment_id: Environment ID to validate. auth_token: Authentication token to validate. Raises: @@ -452,8 +442,6 @@ def _validate_input_parameters( """ if not agent_user_id: raise ValueError("agent_user_id cannot be empty or None") - if not environment_id: - raise ValueError("environment_id cannot be empty or None") if not auth_token: raise ValueError("auth_token cannot be empty or None") 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 02e02b2d..4dac0c98 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,6 +20,3 @@ class Headers: #: The prefix used for Bearer authentication tokens in HTTP headers. BEARER_PREFIX = "Bearer" - - #: The header name used to specify the environment identifier in HTTP requests. - ENVIRONMENT_ID = "x-ms-environment-id" diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py index 409e49e2..33199187 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py @@ -33,7 +33,7 @@ def get_tooling_gateway_for_digital_worker(agent_user_id: str) -> str: str: The tooling gateway URL for the digital worker. """ # The endpoint needs to be updated based on the environment (prod, dev, etc.) - return f"{_get_mcp_platform_base_url()}/agentGateway/agentApplicationInstances/{agent_user_id}/mcpServers" + return f"{_get_mcp_platform_base_url()}/agents/{agent_user_id}/mcpServers" def get_mcp_base_url() -> str: @@ -50,27 +50,22 @@ def get_mcp_base_url() -> str: if tools_mode == ToolsMode.MOCK_MCP_SERVER: return os.getenv("MOCK_MCP_SERVER_URL", "http://localhost:5309/mcp-mock/agents/servers") - return f"{_get_mcp_platform_base_url()}/mcp/environments" + return f"{_get_mcp_platform_base_url()}/agents/servers" -def build_mcp_server_url(environment_id: str, server_name: str) -> str: +def build_mcp_server_url(server_name: str) -> str: """ - Constructs the full MCP server URL using the base URL, environment ID, and server name. + Constructs the full MCP server URL using the base URL and server name. Args: - environment_id: The environment ID. server_name: The MCP server name. Returns: str: The full MCP server URL. """ base_url = get_mcp_base_url() - environment = _get_current_environment().lower() + return f"{base_url}/{server_name}" - if environment == "development" and base_url.endswith("servers"): - return f"{base_url}/{server_name}" - else: - return f"{base_url}/{environment_id}/servers/{server_name}" def _get_current_environment() -> str: From efcf193efdf151242ed0849e9b2fd1904851f1f8 Mon Sep 17 00:00:00 2001 From: Josh Oratz Date: Thu, 30 Oct 2025 13:00:59 -0700 Subject: [PATCH 2/5] Apply formatting fixes from ruff --- .../services/mcp_tool_registration_service.py | 4 +-- .../services/mcp_tool_registration_service.py | 4 +-- .../mcp_tool_server_configuration_service.py | 25 +++++++------------ .../tooling/utils/utility.py | 1 - 4 files changed, 11 insertions(+), 23 deletions(-) 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 2c912c2c..4ec5955c 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 @@ -149,9 +149,7 @@ async def _get_mcp_tool_definitions_and_resources( return ([], None) if len(servers) == 0: - self._logger.info( - f"No MCP servers configured for AgentUserId={agent_user_id}" - ) + self._logger.info(f"No MCP servers configured for AgentUserId={agent_user_id}") return ([], None) # Collections to build for the return value 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 fd9208cf..edf98b38 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 @@ -171,9 +171,7 @@ async def add_tool_servers_to_agent( # Private Methods - Input Validation & Processing # ============================================================================ - def _validate_inputs( - self, kernel: Any, agent_user_id: str, auth_token: str - ) -> None: + def _validate_inputs(self, kernel: Any, agent_user_id: str, auth_token: str) -> None: """Validate all required inputs.""" if kernel is None: raise ValueError("kernel cannot be None") 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 03989cfe..f5947214 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 @@ -65,9 +65,7 @@ def __init__(self, logger: Optional[logging.Logger] = None): # PUBLIC API # -------------------------------------------------------------------------- - async def list_tool_servers( - self, agent_user_id: str, auth_token: str - ) -> List[MCPServerConfig]: + async def list_tool_servers(self, agent_user_id: str, auth_token: str) -> List[MCPServerConfig]: """ Gets the list of MCP Servers that are configured for the agent. @@ -85,9 +83,7 @@ async def list_tool_servers( # Validate input parameters self._validate_input_parameters(agent_user_id, auth_token) - self._logger.info( - f"Listing MCP tool servers for agent {agent_user_id}" - ) + self._logger.info(f"Listing MCP tool servers for agent {agent_user_id}") # Determine configuration source based on environment if self._is_development_scenario(): @@ -214,9 +210,7 @@ def _get_manifest_search_locations(self) -> List[Path]: return search_locations - def _parse_manifest_file( - self, manifest_path: Path - ) -> List[MCPServerConfig]: + def _parse_manifest_file(self, manifest_path: Path) -> List[MCPServerConfig]: """ Parses the manifest file and extracts MCP server configurations. @@ -243,9 +237,7 @@ def _parse_manifest_file( print(f"📊 Processing {len(mcp_servers_data)} server entries") for server_element in mcp_servers_data: print(f"🔧 Processing server element: {server_element}") - server_config = self._parse_manifest_server_config( - server_element - ) + server_config = self._parse_manifest_server_config(server_element) if server_config is not None: print( f"✅ Created server config: {server_config.mcp_server_name} -> {server_config.mcp_server_unique_name}" @@ -329,7 +321,10 @@ 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, + ) -> Dict[str, str]: """ Prepares headers for tooling gateway requests. @@ -427,9 +422,7 @@ def _parse_gateway_server_config( # VALIDATION AND UTILITY HELPERS # -------------------------------------------------------------------------- - def _validate_input_parameters( - self, agent_user_id: str, auth_token: str - ) -> None: + def _validate_input_parameters(self, agent_user_id: str, auth_token: str) -> None: """ Validates input parameters for the main API method. diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py index 33199187..38d08874 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py @@ -67,7 +67,6 @@ def build_mcp_server_url(server_name: str) -> str: return f"{base_url}/{server_name}" - def _get_current_environment() -> str: """ Gets the current environment name. From 9c6680081005ede3724cbbca4843993024fb1223 Mon Sep 17 00:00:00 2001 From: Josh Oratz Date: Thu, 30 Oct 2025 17:19:29 -0700 Subject: [PATCH 3/5] Revert "Apply formatting fixes from ruff" This reverts commit efcf193efdf151242ed0849e9b2fd1904851f1f8. --- .../services/mcp_tool_registration_service.py | 4 ++- .../services/mcp_tool_registration_service.py | 4 ++- .../mcp_tool_server_configuration_service.py | 25 ++++++++++++------- .../tooling/utils/utility.py | 1 + 4 files changed, 23 insertions(+), 11 deletions(-) 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 4ec5955c..2c912c2c 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 @@ -149,7 +149,9 @@ async def _get_mcp_tool_definitions_and_resources( return ([], None) if len(servers) == 0: - self._logger.info(f"No MCP servers configured for AgentUserId={agent_user_id}") + self._logger.info( + f"No MCP servers configured for AgentUserId={agent_user_id}" + ) return ([], None) # Collections to build for the return value 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 edf98b38..fd9208cf 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 @@ -171,7 +171,9 @@ async def add_tool_servers_to_agent( # Private Methods - Input Validation & Processing # ============================================================================ - def _validate_inputs(self, kernel: Any, agent_user_id: str, auth_token: str) -> None: + def _validate_inputs( + self, kernel: Any, agent_user_id: str, auth_token: str + ) -> None: """Validate all required inputs.""" if kernel is None: raise ValueError("kernel cannot be None") 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 f5947214..03989cfe 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 @@ -65,7 +65,9 @@ def __init__(self, logger: Optional[logging.Logger] = None): # PUBLIC API # -------------------------------------------------------------------------- - async def list_tool_servers(self, agent_user_id: str, auth_token: str) -> List[MCPServerConfig]: + async def list_tool_servers( + self, agent_user_id: str, auth_token: str + ) -> List[MCPServerConfig]: """ Gets the list of MCP Servers that are configured for the agent. @@ -83,7 +85,9 @@ async def list_tool_servers(self, agent_user_id: str, auth_token: str) -> List[M # Validate input parameters self._validate_input_parameters(agent_user_id, auth_token) - self._logger.info(f"Listing MCP tool servers for agent {agent_user_id}") + self._logger.info( + f"Listing MCP tool servers for agent {agent_user_id}" + ) # Determine configuration source based on environment if self._is_development_scenario(): @@ -210,7 +214,9 @@ def _get_manifest_search_locations(self) -> List[Path]: return search_locations - def _parse_manifest_file(self, manifest_path: Path) -> List[MCPServerConfig]: + def _parse_manifest_file( + self, manifest_path: Path + ) -> List[MCPServerConfig]: """ Parses the manifest file and extracts MCP server configurations. @@ -237,7 +243,9 @@ def _parse_manifest_file(self, manifest_path: Path) -> List[MCPServerConfig]: print(f"📊 Processing {len(mcp_servers_data)} server entries") for server_element in mcp_servers_data: print(f"🔧 Processing server element: {server_element}") - server_config = self._parse_manifest_server_config(server_element) + server_config = self._parse_manifest_server_config( + server_element + ) if server_config is not None: print( f"✅ Created server config: {server_config.mcp_server_name} -> {server_config.mcp_server_unique_name}" @@ -321,10 +329,7 @@ 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,) -> Dict[str, str]: """ Prepares headers for tooling gateway requests. @@ -422,7 +427,9 @@ def _parse_gateway_server_config( # VALIDATION AND UTILITY HELPERS # -------------------------------------------------------------------------- - def _validate_input_parameters(self, agent_user_id: str, auth_token: str) -> None: + def _validate_input_parameters( + self, agent_user_id: str, auth_token: str + ) -> None: """ Validates input parameters for the main API method. diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py index 38d08874..33199187 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py @@ -67,6 +67,7 @@ def build_mcp_server_url(server_name: str) -> str: return f"{base_url}/{server_name}" + def _get_current_environment() -> str: """ Gets the current environment name. From 294d69a6b9161fabd8cb50832fbf32877dc7bd60 Mon Sep 17 00:00:00 2001 From: Josh Oratz Date: Thu, 30 Oct 2025 19:20:44 -0700 Subject: [PATCH 4/5] Revert "Remove environment id usage" This reverts commit c6473e7561bcbc13ebb129a7fac75b0ee2a09538. --- .../README.md | 1 + .../services/mcp_tool_registration_service.py | 16 ++++--- .../README.md | 1 + .../openai/mcp_tool_registration_service.py | 6 ++- .../README.md | 1 + .../services/mcp_tool_registration_service.py | 16 +++++-- .../microsoft-agents-a365-tooling/README.md | 1 + .../mcp_tool_server_configuration_service.py | 42 ++++++++++++------- .../tooling/utils/constants.py | 3 ++ .../tooling/utils/utility.py | 15 ++++--- 10 files changed, 72 insertions(+), 30 deletions(-) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md index 53016086..382ec1cc 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md +++ b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/README.md @@ -64,6 +64,7 @@ registration_service = McpToolRegistrationService() await registration_service.add_tool_servers_to_agent( project_client=project_client, agent_id="your-agent-id", + environment_id="prod", auth_token="your-auth-token" ) ``` 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 2c912c2c..4facda83 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 @@ -44,7 +44,7 @@ class McpToolRegistrationService: Example: >>> service = McpToolRegistrationService() - >>> service.add_tool_servers_to_agent(project_client, agent_id, token) + >>> service.add_tool_servers_to_agent(project_client, agent_id, env_id, token) """ def __init__( @@ -73,6 +73,7 @@ async def add_tool_servers_to_agent( self, project_client: "AIProjectClient", agent_user_id: str, + environment_id: str, auth: Authorization, context: TurnContext, auth_token: Optional[str] = None, @@ -83,6 +84,7 @@ async def add_tool_servers_to_agent( Args: project_client: The Azure Foundry AIProjectClient instance. agent_user_id: Agent User ID for the agent. + environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Raises: @@ -100,7 +102,7 @@ async def add_tool_servers_to_agent( try: # Get the tool definitions and resources using the async implementation tool_definitions, tool_resources = await self._get_mcp_tool_definitions_and_resources( - agent_user_id, auth_token or "" + agent_user_id, environment_id, auth_token or "" ) # Update the agent with the tools @@ -119,7 +121,7 @@ async def add_tool_servers_to_agent( raise async def _get_mcp_tool_definitions_and_resources( - self, agent_user_id: str, auth_token: str + self, agent_user_id: str, environment_id: str, auth_token: str ) -> Tuple[List[McpTool], Optional[ToolResources]]: """ Internal method to get MCP tool definitions and resources. @@ -128,6 +130,7 @@ async def _get_mcp_tool_definitions_and_resources( Args: agent_user_id: Agent User ID for the agent. + environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Returns: @@ -140,7 +143,7 @@ async def _get_mcp_tool_definitions_and_resources( # Get MCP server configurations try: servers = await self._mcp_server_configuration_service.list_tool_servers( - agent_user_id, auth_token + agent_user_id, environment_id, auth_token ) except Exception as ex: self._logger.error( @@ -150,7 +153,7 @@ async def _get_mcp_tool_definitions_and_resources( if len(servers) == 0: self._logger.info( - f"No MCP servers configured for AgentUserId={agent_user_id}" + f"No MCP servers configured for AgentUserId={agent_user_id}, EnvironmentId={environment_id}" ) return ([], None) @@ -190,6 +193,9 @@ async def _get_mcp_tool_definitions_and_resources( ) mcp_tool.update_headers(Constants.Headers.AUTHORIZATION, header_value) + # Set environment ID header + mcp_tool.update_headers(Constants.Headers.ENVIRONMENT_ID, environment_id) + # 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/README.md b/libraries/microsoft-agents-a365-tooling-extensions-openai/README.md index c9b6e876..5198f310 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-openai/README.md +++ b/libraries/microsoft-agents-a365-tooling-extensions-openai/README.md @@ -54,6 +54,7 @@ registration_service = McpToolRegistrationService() await registration_service.add_tool_servers_to_agent( agent=your_openai_agent, agent_user_id="user-123", + environment_id="prod", auth=authorization_context, context=turn_context, auth_token="your-auth-token" 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 544e2f4b..bfbd8de5 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 @@ -39,6 +39,7 @@ async def add_tool_servers_to_agent( self, agent: Agent, agent_user_id: str, + environment_id: str, auth: Authorization, context: TurnContext, auth_token: Optional[str] = None, @@ -53,6 +54,7 @@ async def add_tool_servers_to_agent( Args: agent: The existing agent to add servers to agent_user_id: Agent User ID for the agent + environment_id: Environment ID for the environment auth_token: Authentication token to access the MCP servers Returns: @@ -68,7 +70,7 @@ async def add_tool_servers_to_agent( # mcp_server_configs = [] # TODO: radevika: Update once the common project is merged. mcp_server_configs = await self.config_service.list_tool_servers( - agent_user_id=agent_user_id, auth_token=auth_token + agent_user_id=agent_user_id, environment_id=environment_id, auth_token=auth_token ) # Convert MCP server configs to MCPServerInfo objects @@ -112,6 +114,8 @@ async def add_tool_servers_to_agent( headers = si.headers or {} if auth_token: headers["Authorization"] = f"Bearer {auth_token}" + if environment_id: + headers["x-ms-environment-id"] = environment_id # Create MCPServerStreamableHttpParams with proper configuration params = MCPServerStreamableHttpParams(url=si.url, headers=headers) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md index 01ea4629..58b72af5 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md +++ b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/README.md @@ -56,6 +56,7 @@ registration_service = McpToolRegistrationService() await registration_service.add_tool_servers_to_kernel( kernel=kernel, agent_user_id="user-123", + environment_id="prod", auth_token="your-auth-token" ) ``` 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 fd9208cf..92e56d9d 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 @@ -84,6 +84,7 @@ async def add_tool_servers_to_agent( self, kernel: sk.Kernel, agent_user_id: str, + environment_id: str, auth: Authorization, context: TurnContext, auth_token: Optional[str] = None, @@ -94,6 +95,7 @@ async def add_tool_servers_to_agent( Args: kernel: The Semantic Kernel instance to which the tools will be added. agent_user_id: Agent User ID for the agent. + environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Raises: @@ -106,7 +108,7 @@ async def add_tool_servers_to_agent( authToken = await auth.exchange_token(context, scopes, "AGENTIC") auth_token = authToken.token - self._validate_inputs(kernel, agent_user_id, auth_token) + self._validate_inputs(kernel, agent_user_id, environment_id, auth_token) if self._mcp_server_configuration_service is None: raise ValueError( @@ -115,7 +117,7 @@ async def add_tool_servers_to_agent( # Get and process servers servers = await self._mcp_server_configuration_service.list_tool_servers( - agent_user_id, auth_token + agent_user_id, environment_id, auth_token ) self._logger.info(f"🔧 Adding MCP tools from {len(servers)} servers") @@ -132,12 +134,16 @@ async def add_tool_servers_to_agent( headers = {} if tools_mode == "MockMCPServer": - # Use mock authorization header if provided + # Mock server does not require bearer auth, but still forward environment id if available. + if environment_id: + headers[Constants.Headers.ENVIRONMENT_ID] = environment_id + if mock_auth_header := os.getenv("MOCK_MCP_AUTHORIZATION"): headers[Constants.Headers.AUTHORIZATION] = mock_auth_header else: headers = { Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}", + Constants.Headers.ENVIRONMENT_ID: environment_id, } plugin = MCPStreamableHttpPlugin( @@ -172,13 +178,15 @@ async def add_tool_servers_to_agent( # ============================================================================ def _validate_inputs( - self, kernel: Any, agent_user_id: str, auth_token: str + self, kernel: Any, agent_user_id: str, environment_id: str, auth_token: str ) -> None: """Validate all required inputs.""" if kernel is None: raise ValueError("kernel cannot be None") if not agent_user_id or not agent_user_id.strip(): raise ValueError("agent_user_id cannot be null or empty") + if not environment_id or not environment_id.strip(): + raise ValueError("environment_id cannot be null or empty") if not auth_token or not auth_token.strip(): raise ValueError("auth_token cannot be null or empty") diff --git a/libraries/microsoft-agents-a365-tooling/README.md b/libraries/microsoft-agents-a365-tooling/README.md index 56ec6671..2392d33b 100644 --- a/libraries/microsoft-agents-a365-tooling/README.md +++ b/libraries/microsoft-agents-a365-tooling/README.md @@ -59,6 +59,7 @@ mcp_config = MCPServerConfig( config_service = McpToolServerConfigurationService() mcp_servers = await config_service.list_tool_servers( agent_user_id="agent-123", + environment_id="prod", auth_token="your-auth-token" ) ``` 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 03989cfe..562e8c08 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 @@ -66,13 +66,14 @@ def __init__(self, logger: Optional[logging.Logger] = None): # -------------------------------------------------------------------------- async def list_tool_servers( - self, agent_user_id: str, auth_token: str + self, agent_user_id: str, environment_id: str, auth_token: str ) -> List[MCPServerConfig]: """ Gets the list of MCP Servers that are configured for the agent. Args: agent_user_id: Agent User ID for the agent. + environment_id: Environment ID for the environment. auth_token: Authentication token to access the MCP servers. Returns: @@ -83,17 +84,17 @@ async def list_tool_servers( Exception: If there's an error communicating with the tooling gateway. """ # Validate input parameters - self._validate_input_parameters(agent_user_id, auth_token) + self._validate_input_parameters(agent_user_id, environment_id, auth_token) self._logger.info( - f"Listing MCP tool servers for agent {agent_user_id}" + f"Listing MCP tool servers for agent {agent_user_id} in environment {environment_id}" ) # Determine configuration source based on environment if self._is_development_scenario(): - return self._load_servers_from_manifest() + return self._load_servers_from_manifest(environment_id) else: - return await self._load_servers_from_gateway(agent_user_id, auth_token) + return await self._load_servers_from_gateway(agent_user_id, environment_id, auth_token) # -------------------------------------------------------------------------- # ENVIRONMENT DETECTION @@ -116,7 +117,7 @@ def _is_development_scenario(self) -> bool: # DEVELOPMENT: MANIFEST-BASED CONFIGURATION # -------------------------------------------------------------------------- - def _load_servers_from_manifest(self) -> List[MCPServerConfig]: + def _load_servers_from_manifest(self, environment_id: str) -> List[MCPServerConfig]: """ Reads MCP server configurations from ToolingManifest.json in the application's content root. @@ -136,6 +137,9 @@ def _load_servers_from_manifest(self) -> List[MCPServerConfig]: ] } + Args: + environment_id: Environment ID to construct full URLs. + Returns: List[MCPServerConfig]: List of MCP server configurations from manifest. @@ -149,7 +153,7 @@ def _load_servers_from_manifest(self) -> List[MCPServerConfig]: if manifest_path and manifest_path.exists(): self._logger.info(f"Loading MCP servers from: {manifest_path}") - mcp_servers = self._parse_manifest_file(manifest_path) + mcp_servers = self._parse_manifest_file(manifest_path, environment_id) else: self._log_manifest_search_failure() @@ -215,13 +219,14 @@ def _get_manifest_search_locations(self) -> List[Path]: return search_locations def _parse_manifest_file( - self, manifest_path: Path + self, manifest_path: Path, environment_id: str ) -> List[MCPServerConfig]: """ Parses the manifest file and extracts MCP server configurations. Args: manifest_path: Path to the manifest file. + environment_id: Environment ID for URL construction. Returns: List of parsed MCP server configurations. @@ -244,7 +249,7 @@ def _parse_manifest_file( for server_element in mcp_servers_data: print(f"🔧 Processing server element: {server_element}") server_config = self._parse_manifest_server_config( - server_element + server_element, environment_id ) if server_config is not None: print( @@ -281,13 +286,14 @@ def _log_manifest_search_failure(self) -> None: # -------------------------------------------------------------------------- async def _load_servers_from_gateway( - self, agent_user_id: str, auth_token: str + self, agent_user_id: str, environment_id: str, auth_token: str ) -> List[MCPServerConfig]: """ Reads MCP server configurations from tooling gateway endpoint for production scenario. Args: agent_user_id: Agent User ID for the agent. + environment_id: Environment ID for the environment. auth_token: Authentication token to access the tooling gateway. Returns: @@ -300,7 +306,7 @@ async def _load_servers_from_gateway( try: config_endpoint = get_tooling_gateway_for_digital_worker(agent_user_id) - headers = self._prepare_gateway_headers(auth_token) + headers = self._prepare_gateway_headers(auth_token, environment_id) self._logger.info(f"Calling tooling gateway endpoint: {config_endpoint}") @@ -329,18 +335,20 @@ 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, environment_id: str) -> Dict[str, str]: """ Prepares headers for tooling gateway requests. Args: auth_token: Authentication token. + environment_id: Environment ID. Returns: Dictionary of HTTP headers. """ return { "Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}", + Constants.Headers.ENVIRONMENT_ID: environment_id, } async def _parse_gateway_response( @@ -373,13 +381,14 @@ async def _parse_gateway_response( # -------------------------------------------------------------------------- def _parse_manifest_server_config( - self, server_element: Dict[str, Any] + self, server_element: Dict[str, Any], environment_id: str ) -> Optional[MCPServerConfig]: """ Parses a server configuration from manifest data, constructing full URL. Args: server_element: Dictionary containing server configuration from manifest. + environment_id: Environment ID to construct full URL. Returns: MCPServerConfig object or None if parsing fails. @@ -392,7 +401,7 @@ def _parse_manifest_server_config( return None # Construct full URL using environment utilities - full_url = build_mcp_server_url(server_name) + full_url = build_mcp_server_url(environment_id, server_name) return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=full_url) @@ -428,13 +437,14 @@ def _parse_gateway_server_config( # -------------------------------------------------------------------------- def _validate_input_parameters( - self, agent_user_id: str, auth_token: str + self, agent_user_id: str, environment_id: str, auth_token: str ) -> None: """ Validates input parameters for the main API method. Args: agent_user_id: Agent User ID to validate. + environment_id: Environment ID to validate. auth_token: Authentication token to validate. Raises: @@ -442,6 +452,8 @@ def _validate_input_parameters( """ if not agent_user_id: raise ValueError("agent_user_id cannot be empty or None") + if not environment_id: + raise ValueError("environment_id cannot be empty or None") if not auth_token: raise ValueError("auth_token cannot be empty or None") 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 4dac0c98..02e02b2d 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 used to specify the environment identifier in HTTP requests. + ENVIRONMENT_ID = "x-ms-environment-id" diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py index 33199187..409e49e2 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py @@ -33,7 +33,7 @@ def get_tooling_gateway_for_digital_worker(agent_user_id: str) -> str: str: The tooling gateway URL for the digital worker. """ # The endpoint needs to be updated based on the environment (prod, dev, etc.) - return f"{_get_mcp_platform_base_url()}/agents/{agent_user_id}/mcpServers" + return f"{_get_mcp_platform_base_url()}/agentGateway/agentApplicationInstances/{agent_user_id}/mcpServers" def get_mcp_base_url() -> str: @@ -50,22 +50,27 @@ def get_mcp_base_url() -> str: if tools_mode == ToolsMode.MOCK_MCP_SERVER: return os.getenv("MOCK_MCP_SERVER_URL", "http://localhost:5309/mcp-mock/agents/servers") - return f"{_get_mcp_platform_base_url()}/agents/servers" + return f"{_get_mcp_platform_base_url()}/mcp/environments" -def build_mcp_server_url(server_name: str) -> str: +def build_mcp_server_url(environment_id: str, server_name: str) -> str: """ - Constructs the full MCP server URL using the base URL and server name. + Constructs the full MCP server URL using the base URL, environment ID, and server name. Args: + environment_id: The environment ID. server_name: The MCP server name. Returns: str: The full MCP server URL. """ base_url = get_mcp_base_url() - return f"{base_url}/{server_name}" + environment = _get_current_environment().lower() + if environment == "development" and base_url.endswith("servers"): + return f"{base_url}/{server_name}" + else: + return f"{base_url}/{environment_id}/servers/{server_name}" def _get_current_environment() -> str: From 3cf72c8095f65b690bde29d05389dde543ec8f5d Mon Sep 17 00:00:00 2001 From: Josh Oratz Date: Thu, 30 Oct 2025 19:31:38 -0700 Subject: [PATCH 5/5] Update to optionally use environment id --- .../services/mcp_tool_registration_service.py | 5 +++-- .../openai/mcp_tool_registration_service.py | 7 +++++-- .../services/mcp_tool_registration_service.py | 10 +++++++--- .../mcp_tool_server_configuration_service.py | 12 ++++++++++-- .../tooling/utils/utility.py | 18 +++++++++++++++++- 5 files changed, 42 insertions(+), 10 deletions(-) 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 4facda83..18f213cc 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 @@ -18,7 +18,7 @@ from azure.ai.agents.models import McpTool, ToolResources from microsoft_agents.hosting.core import Authorization, TurnContext -from ...common.utils.utility import get_ppapi_token_scope +from ...common.utils.utility import get_ppapi_token_scope, get_use_environment_id # Local imports from microsoft_kairo.tooling.common.services.mcp_tool_server_configuration_service import ( @@ -194,7 +194,8 @@ async def _get_mcp_tool_definitions_and_resources( mcp_tool.update_headers(Constants.Headers.AUTHORIZATION, header_value) # Set environment ID header - mcp_tool.update_headers(Constants.Headers.ENVIRONMENT_ID, environment_id) + if get_use_environment_id() and environment_id: + mcp_tool.update_headers(Constants.Headers.ENVIRONMENT_ID, environment_id) # Add to collections tool_definitions.extend(mcp_tool.definitions) 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 bfbd8de5..e8d5703e 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 @@ -13,7 +13,10 @@ McpToolServerConfigurationService, ) -from microsoft_agents_a365.tooling.utils.utility import get_ppapi_token_scope +from microsoft_agents_a365.tooling.utils.utility import ( + get_ppapi_token_scope, + get_use_environment_id, +) # TODO: This is not needed. Remove this. @@ -114,7 +117,7 @@ async def add_tool_servers_to_agent( headers = si.headers or {} if auth_token: headers["Authorization"] = f"Bearer {auth_token}" - if environment_id: + if get_use_environment_id() and environment_id: headers["x-ms-environment-id"] = environment_id # Create MCPServerStreamableHttpParams with proper configuration 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 92e56d9d..313a7181 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 @@ -24,7 +24,7 @@ ) from ...common.models import MCPServerConfig from ...common.utils.constants import Constants -from ...common.utils.utility import get_tools_mode, get_ppapi_token_scope +from ...common.utils.utility import get_tools_mode, get_ppapi_token_scope, get_use_environment_id from semantic_kernel.connectors.mcp import MCPStreamableHttpPlugin @@ -135,16 +135,20 @@ async def add_tool_servers_to_agent( if tools_mode == "MockMCPServer": # Mock server does not require bearer auth, but still forward environment id if available. - if environment_id: + if get_use_environment_id() and environment_id: headers[Constants.Headers.ENVIRONMENT_ID] = environment_id if mock_auth_header := os.getenv("MOCK_MCP_AUTHORIZATION"): headers[Constants.Headers.AUTHORIZATION] = mock_auth_header - else: + elif get_use_environment_id(): headers = { Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}", Constants.Headers.ENVIRONMENT_ID: environment_id, } + else: + headers = { + Constants.Headers.AUTHORIZATION: f"{Constants.Headers.BEARER_PREFIX} {auth_token}", + } plugin = MCPStreamableHttpPlugin( name=server.mcp_server_name, 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 562e8c08..a8a4b58c 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 @@ -30,7 +30,11 @@ # Local imports from ..models import MCPServerConfig from ..utils import Constants -from ..utils.utility import get_tooling_gateway_for_digital_worker, build_mcp_server_url +from ..utils.utility import ( + get_tooling_gateway_for_digital_worker, + build_mcp_server_url, + get_use_environment_id, +) # ============================================================================== @@ -346,9 +350,13 @@ def _prepare_gateway_headers(self, auth_token: str, environment_id: str) -> Dict Returns: Dictionary of HTTP headers. """ + if get_use_environment_id(): + return { + "Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}", + Constants.Headers.ENVIRONMENT_ID: environment_id, + } return { "Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}", - Constants.Headers.ENVIRONMENT_ID: environment_id, } async def _parse_gateway_response( diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py index 409e49e2..b9d07ab7 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/utils/utility.py @@ -50,6 +50,9 @@ def get_mcp_base_url() -> str: if tools_mode == ToolsMode.MOCK_MCP_SERVER: return os.getenv("MOCK_MCP_SERVER_URL", "http://localhost:5309/mcp-mock/agents/servers") + if not get_use_environment_id(): + return f"{_get_mcp_platform_base_url()}/agents/servers" + return f"{_get_mcp_platform_base_url()}/mcp/environments" @@ -67,7 +70,9 @@ def build_mcp_server_url(environment_id: str, server_name: str) -> str: base_url = get_mcp_base_url() environment = _get_current_environment().lower() - if environment == "development" and base_url.endswith("servers"): + if not get_use_environment_id() or ( + environment == "development" and base_url.endswith("servers") + ): return f"{base_url}/{server_name}" else: return f"{base_url}/{environment_id}/servers/{server_name}" @@ -96,6 +101,17 @@ def _get_mcp_platform_base_url() -> str: return MCP_PLATFORM_PROD_BASE_URL +def get_use_environment_id() -> bool: + """ + Determines whether to use environment ID in MCP server URL construction. + + Returns: + bool: True if environment ID should be used, False otherwise. + """ + use_environment = os.getenv("USE_ENVIRONMENT_ID", "true").lower() + return use_environment == "true" + + def get_tools_mode() -> ToolsMode: """ Gets the tools mode for the application.