From d3fbcec61a12e44793f0b40b6350be65631d116c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:13:42 +0000 Subject: [PATCH 01/26] Initial plan From 7468ea0ec9d93370431478d02ebcf44bf4624f94 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:20:24 +0000 Subject: [PATCH 02/26] Add support for custom MCP server URLs Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 9 ++-- .../services/mcp_tool_registration_service.py | 5 ++- .../openai/mcp_tool_registration_service.py | 6 ++- .../services/mcp_tool_registration_service.py | 5 ++- .../tooling/models/mcp_server_config.py | 5 +++ .../mcp_tool_server_configuration_service.py | 44 ++++++++++++++++--- 6 files changed, 61 insertions(+), 13 deletions(-) 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 267680d6..c2c01116 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 @@ -97,9 +97,8 @@ async def add_tool_servers_to_agent( # Add servers as MCPStreamableHTTPTool instances for config in server_configs: try: - server_url = getattr(config, "server_url", None) or getattr( - config, "mcp_server_unique_name", None - ) + # Use custom URL if provided, otherwise use the unique name + server_url = config.url if config.url else config.mcp_server_unique_name if not server_url: self._logger.warning(f"MCP server config missing server_url: {config}") continue @@ -115,7 +114,7 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) - server_name = getattr(config, "mcp_server_name", "Unknown") + server_name = config.mcp_server_name # Create and configure MCPStreamableHTTPTool mcp_tools = MCPStreamableHTTPTool( @@ -134,7 +133,7 @@ async def add_tool_servers_to_agent( self._logger.info(f"Added MCP plugin '{server_name}' to agent tools") except Exception as tool_ex: - server_name = getattr(config, "mcp_server_name", "Unknown") + server_name = config.mcp_server_name if hasattr(config, "mcp_server_name") else "Unknown" self._logger.warning( f"Failed to create MCP plugin for {server_name}: {tool_ex}" ) 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 1342b870..a4debde3 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 @@ -178,8 +178,11 @@ async def _get_mcp_tool_definitions_and_resources( else server.mcp_server_name ) + # Use custom URL if provided, otherwise use the unique name + server_url = server.url if server.url else server.mcp_server_unique_name + # Create MCP tool using Azure Foundry SDK - mcp_tool = McpTool(server_label=server_label, server_url=server.mcp_server_unique_name) + mcp_tool = McpTool(server_label=server_label, server_url=server_url) # Configure the tool mcp_tool.set_approval_mode("never") 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 1d6e6cc9..8025dede 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 @@ -101,9 +101,13 @@ async def add_tool_servers_to_agent( # Convert MCP server configs to MCPServerInfo objects mcp_servers_info = [] for server_config in mcp_server_configs: + # Use custom URL if provided, otherwise use the unique name + server_url = ( + server_config.url if server_config.url else server_config.mcp_server_unique_name + ) server_info = MCPServerInfo( name=server_config.mcp_server_name, - url=server_config.mcp_server_unique_name, + url=server_url, ) mcp_servers_info.append(server_info) 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 e4180319..e567699f 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 @@ -125,9 +125,12 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) + # Use custom URL if provided, otherwise use the unique name + server_url = server.url if server.url else server.mcp_server_unique_name + plugin = MCPStreamableHttpPlugin( name=server.mcp_server_name, - url=server.mcp_server_unique_name, + url=server_url, headers=headers, ) diff --git a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/mcp_server_config.py b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/mcp_server_config.py index b8526c13..b099851d 100644 --- a/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/mcp_server_config.py +++ b/libraries/microsoft-agents-a365-tooling/microsoft_agents_a365/tooling/models/mcp_server_config.py @@ -5,6 +5,7 @@ """ from dataclasses import dataclass +from typing import Optional @dataclass @@ -19,6 +20,10 @@ class MCPServerConfig: #: Gets or sets the unique name of the MCP server. mcp_server_unique_name: str + #: Gets or sets the custom URL for the MCP server. If provided, this URL will be used + #: instead of constructing the URL from the base URL and unique name. + url: Optional[str] = None + def __post_init__(self): """Validate the configuration after initialization.""" if not self.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 aec53f2e..1ae59315 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 @@ -398,10 +398,18 @@ def _parse_manifest_server_config( if not self._validate_server_strings(name, server_name): return None - # Construct full URL using environment utilities - full_url = build_mcp_server_url(server_name) - - return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=full_url) + # Check if a custom URL is provided + custom_url = self._extract_server_url(server_element) + + # If custom URL is provided, use it directly; otherwise construct from base URL + if custom_url and custom_url.strip(): + return MCPServerConfig( + mcp_server_name=name, mcp_server_unique_name=server_name, url=custom_url + ) + else: + # Construct full URL using environment utilities + full_url = build_mcp_server_url(server_name) + return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=full_url) except Exception: return None @@ -425,7 +433,16 @@ def _parse_gateway_server_config( if not self._validate_server_strings(name, endpoint): return None - return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=endpoint) + # Check if a custom URL is provided by the gateway + custom_url = self._extract_server_url(server_element) + + # If custom URL is provided, use it; otherwise use the endpoint as-is + if custom_url and custom_url.strip(): + return MCPServerConfig( + mcp_server_name=name, mcp_server_unique_name=endpoint, url=custom_url + ) + else: + return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=endpoint) except Exception: return None @@ -480,6 +497,23 @@ def _extract_server_unique_name(self, server_element: Dict[str, Any]) -> Optiona return server_element["mcpServerUniqueName"] return None + def _extract_server_url(self, server_element: Dict[str, Any]) -> Optional[str]: + """ + Extracts custom server URL from configuration element. + + Args: + server_element: Configuration dictionary. + + Returns: + Server URL string or None. + """ + # Check for 'mcpServerUrl' (manifest) or 'url' (gateway) + if "mcpServerUrl" in server_element and isinstance(server_element["mcpServerUrl"], str): + return server_element["mcpServerUrl"] + if "url" in server_element and isinstance(server_element["url"], str): + return server_element["url"] + return None + def _validate_server_strings(self, name: Optional[str], unique_name: Optional[str]) -> bool: """ Validates that server name and unique name are valid strings. From dba9456cae070b475021536d5d1b0979a7ab3862 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:23:40 +0000 Subject: [PATCH 03/26] Add comprehensive tests for custom MCP server URL support Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- tests/tooling/__init__.py | 4 + .../tooling/test_mcp_server_configuration.py | 236 ++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 tests/tooling/__init__.py create mode 100644 tests/tooling/test_mcp_server_configuration.py diff --git a/tests/tooling/__init__.py b/tests/tooling/__init__.py new file mode 100644 index 00000000..e29b546c --- /dev/null +++ b/tests/tooling/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +"""Tests for tooling components.""" diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py new file mode 100644 index 00000000..7ffbba74 --- /dev/null +++ b/tests/tooling/test_mcp_server_configuration.py @@ -0,0 +1,236 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +"""Unit tests for MCP Server Configuration Service.""" + +import json +import os +from pathlib import Path +from typing import Dict, Any +from unittest.mock import Mock, patch, AsyncMock, MagicMock +import pytest +import aiohttp + +from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import ( + McpToolServerConfigurationService, +) +from microsoft_agents_a365.tooling.models import MCPServerConfig + + +class TestMCPServerConfig: + """Tests for MCPServerConfig model.""" + + def test_mcp_server_config_with_custom_url(self): + """Test that MCPServerConfig can be created with a custom URL.""" + config = MCPServerConfig( + mcp_server_name="TestServer", + mcp_server_unique_name="test_server", + url="https://custom.mcp.server/endpoint", + ) + + assert config.mcp_server_name == "TestServer" + assert config.mcp_server_unique_name == "test_server" + assert config.url == "https://custom.mcp.server/endpoint" + + def test_mcp_server_config_without_custom_url(self): + """Test that MCPServerConfig works without a custom URL.""" + config = MCPServerConfig( + mcp_server_name="TestServer", + mcp_server_unique_name="test_server", + ) + + assert config.mcp_server_name == "TestServer" + assert config.mcp_server_unique_name == "test_server" + assert config.url is None + + def test_mcp_server_config_validation(self): + """Test that MCPServerConfig validates required fields.""" + with pytest.raises(ValueError, match="mcp_server_name cannot be empty"): + MCPServerConfig(mcp_server_name="", mcp_server_unique_name="test") + + with pytest.raises(ValueError, match="mcp_server_unique_name cannot be empty"): + MCPServerConfig(mcp_server_name="test", mcp_server_unique_name="") + + +class TestMcpToolServerConfigurationService: + """Tests for McpToolServerConfigurationService.""" + + @pytest.fixture + def service(self): + """Create a service instance for testing.""" + return McpToolServerConfigurationService() + + @pytest.fixture + def mock_manifest_data(self) -> Dict[str, Any]: + """Create mock manifest data.""" + return { + "mcpServers": [ + { + "mcpServerName": "TestServer1", + "mcpServerUniqueName": "test_server_1", + }, + { + "mcpServerName": "TestServer2", + "mcpServerUniqueName": "test_server_2", + "mcpServerUrl": "https://custom.server.com/mcp", + }, + ] + } + + def test_extract_server_url_from_manifest(self, service): + """Test extracting custom URL from manifest element.""" + # Test with mcpServerUrl field + element = {"mcpServerUrl": "https://custom.url.com"} + url = service._extract_server_url(element) + assert url == "https://custom.url.com" + + # Test with url field + element = {"url": "https://another.url.com"} + url = service._extract_server_url(element) + assert url == "https://another.url.com" + + # Test with no URL + element = {} + url = service._extract_server_url(element) + assert url is None + + def test_parse_manifest_server_config_with_custom_url(self, service): + """Test parsing manifest config with custom URL.""" + server_element = { + "mcpServerName": "CustomServer", + "mcpServerUniqueName": "custom_server", + "mcpServerUrl": "https://my.custom.server/mcp", + } + + config = service._parse_manifest_server_config(server_element) + + assert config is not None + assert config.mcp_server_name == "CustomServer" + assert config.mcp_server_unique_name == "custom_server" + assert config.url == "https://my.custom.server/mcp" + + @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url") + def test_parse_manifest_server_config_without_custom_url(self, mock_build_url, service): + """Test parsing manifest config without custom URL constructs URL.""" + mock_build_url.return_value = "https://default.server/agents/servers/test_server" + + server_element = { + "mcpServerName": "DefaultServer", + "mcpServerUniqueName": "test_server", + } + + config = service._parse_manifest_server_config(server_element) + + assert config is not None + assert config.mcp_server_name == "DefaultServer" + # When no custom URL, the built URL goes into mcp_server_unique_name + assert config.mcp_server_unique_name == "https://default.server/agents/servers/test_server" + mock_build_url.assert_called_once_with("test_server") + + def test_parse_gateway_server_config_with_custom_url(self, service): + """Test parsing gateway config with custom URL.""" + server_element = { + "mcpServerName": "GatewayServer", + "mcpServerUniqueName": "gateway_server_endpoint", + "url": "https://gateway.custom.url/mcp", + } + + config = service._parse_gateway_server_config(server_element) + + assert config is not None + assert config.mcp_server_name == "GatewayServer" + assert config.mcp_server_unique_name == "gateway_server_endpoint" + assert config.url == "https://gateway.custom.url/mcp" + + def test_parse_gateway_server_config_without_custom_url(self, service): + """Test parsing gateway config without custom URL.""" + server_element = { + "mcpServerName": "GatewayServer", + "mcpServerUniqueName": "https://gateway.default/endpoint", + } + + config = service._parse_gateway_server_config(server_element) + + assert config is not None + assert config.mcp_server_name == "GatewayServer" + assert config.mcp_server_unique_name == "https://gateway.default/endpoint" + assert config.url is None + + @patch.dict(os.environ, {"ENVIRONMENT": "Development"}) + def test_is_development_scenario(self, service): + """Test development scenario detection.""" + assert service._is_development_scenario() is True + + @patch.dict(os.environ, {"ENVIRONMENT": "Production"}) + def test_is_production_scenario(self, service): + """Test production scenario detection.""" + assert service._is_development_scenario() is False + + @patch.object(McpToolServerConfigurationService, "_load_servers_from_manifest") + @patch.dict(os.environ, {"ENVIRONMENT": "Development"}) + @pytest.mark.asyncio + async def test_list_tool_servers_development(self, mock_load_manifest, service): + """Test listing servers in development mode.""" + mock_servers = [ + MCPServerConfig( + mcp_server_name="DevServer", + mcp_server_unique_name="dev_server", + url="https://dev.server/mcp", + ) + ] + mock_load_manifest.return_value = mock_servers + + servers = await service.list_tool_servers( + agentic_app_id="test-app-id", auth_token="test-token" + ) + + assert servers == mock_servers + mock_load_manifest.assert_called_once() + + @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.get_tooling_gateway_for_digital_worker") + @patch.dict(os.environ, {"ENVIRONMENT": "Production"}) + @pytest.mark.asyncio + async def test_list_tool_servers_production_with_custom_url(self, mock_gateway_url, service): + """Test listing servers in production mode with custom URL.""" + mock_gateway_url.return_value = "https://gateway.test/agents/test-app-id/mcpServers" + + # Mock aiohttp response + mock_response_data = { + "mcpServers": [ + { + "mcpServerName": "ProdServer", + "mcpServerUniqueName": "prod_server", + "url": "https://prod.custom.url/mcp", + } + ] + } + + with patch("aiohttp.ClientSession") as mock_session_class: + # Create proper async context managers + mock_response = MagicMock() + mock_response.status = 200 + mock_response.text = AsyncMock(return_value=json.dumps(mock_response_data)) + + # Create async context manager for response + mock_response_cm = MagicMock() + mock_response_cm.__aenter__ = AsyncMock(return_value=mock_response) + mock_response_cm.__aexit__ = AsyncMock(return_value=None) + + # Create async context manager for session + mock_session = MagicMock() + mock_session.get = MagicMock(return_value=mock_response_cm) + + mock_session_cm = MagicMock() + mock_session_cm.__aenter__ = AsyncMock(return_value=mock_session) + mock_session_cm.__aexit__ = AsyncMock(return_value=None) + + mock_session_class.return_value = mock_session_cm + + servers = await service.list_tool_servers( + agentic_app_id="test-app-id", auth_token="test-token" + ) + + assert len(servers) == 1 + assert servers[0].mcp_server_name == "ProdServer" + assert servers[0].mcp_server_unique_name == "prod_server" + assert servers[0].url == "https://prod.custom.url/mcp" From fe26a987c0e259944edf976b17705ec21e1ae2a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:24:48 +0000 Subject: [PATCH 04/26] Remove unnecessary hasattr check in exception handler Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../agentframework/services/mcp_tool_registration_service.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 c2c01116..aff27d18 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 @@ -133,9 +133,8 @@ async def add_tool_servers_to_agent( self._logger.info(f"Added MCP plugin '{server_name}' to agent tools") except Exception as tool_ex: - server_name = config.mcp_server_name if hasattr(config, "mcp_server_name") else "Unknown" self._logger.warning( - f"Failed to create MCP plugin for {server_name}: {tool_ex}" + f"Failed to create MCP plugin for {config.mcp_server_name}: {tool_ex}" ) continue From 31f547a2457003c5096984ce45a6e6c49f031959 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:26:21 +0000 Subject: [PATCH 05/26] Simplify custom URL checks by removing redundant strip calls Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../tooling/services/mcp_tool_server_configuration_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 1ae59315..0f9cbf2b 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 @@ -402,7 +402,7 @@ def _parse_manifest_server_config( custom_url = self._extract_server_url(server_element) # If custom URL is provided, use it directly; otherwise construct from base URL - if custom_url and custom_url.strip(): + if custom_url: return MCPServerConfig( mcp_server_name=name, mcp_server_unique_name=server_name, url=custom_url ) @@ -437,7 +437,7 @@ def _parse_gateway_server_config( custom_url = self._extract_server_url(server_element) # If custom URL is provided, use it; otherwise use the endpoint as-is - if custom_url and custom_url.strip(): + if custom_url: return MCPServerConfig( mcp_server_name=name, mcp_server_unique_name=endpoint, url=custom_url ) From 967d25a06266a0d661e74615e6aa575bcc34a8de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:27:50 +0000 Subject: [PATCH 06/26] Improve exception handling and clarify test comments Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 6 +++++- tests/tooling/test_mcp_server_configuration.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) 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 aff27d18..4fca5acb 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 @@ -133,8 +133,12 @@ async def add_tool_servers_to_agent( self._logger.info(f"Added MCP plugin '{server_name}' to agent tools") except Exception as tool_ex: + try: + server_name = config.mcp_server_name + except AttributeError: + server_name = "Unknown" self._logger.warning( - f"Failed to create MCP plugin for {config.mcp_server_name}: {tool_ex}" + f"Failed to create MCP plugin for {server_name}: {tool_ex}" ) continue diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 7ffbba74..b7b2fcd0 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -123,7 +123,7 @@ def test_parse_manifest_server_config_without_custom_url(self, mock_build_url, s assert config is not None assert config.mcp_server_name == "DefaultServer" - # When no custom URL, the built URL goes into mcp_server_unique_name + # Without a custom URL, build_mcp_server_url constructs the full URL and stores it in mcp_server_unique_name assert config.mcp_server_unique_name == "https://default.server/agents/servers/test_server" mock_build_url.assert_called_once_with("test_server") From 7ce0f25bbbd8f72e403bd032e7f4c088f23201b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 17:30:49 +0000 Subject: [PATCH 07/26] Refactor to consistently store URLs in the url field Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 6 +---- .../services/mcp_tool_registration_service.py | 2 +- .../mcp_tool_server_configuration_service.py | 26 +++++++------------ .../tooling/test_mcp_server_configuration.py | 8 +++--- 4 files changed, 17 insertions(+), 25 deletions(-) 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 4fca5acb..aff27d18 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 @@ -133,12 +133,8 @@ async def add_tool_servers_to_agent( self._logger.info(f"Added MCP plugin '{server_name}' to agent tools") except Exception as tool_ex: - try: - server_name = config.mcp_server_name - except AttributeError: - server_name = "Unknown" self._logger.warning( - f"Failed to create MCP plugin for {server_name}: {tool_ex}" + f"Failed to create MCP plugin for {config.mcp_server_name}: {tool_ex}" ) continue 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 e567699f..43f3dcaa 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 @@ -125,7 +125,7 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) - # Use custom URL if provided, otherwise use the unique name + # Use custom URL if provided, otherwise use the unique name server_url = server.url if server.url else server.mcp_server_unique_name plugin = MCPStreamableHttpPlugin( 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 0f9cbf2b..b4b757d8 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 @@ -401,15 +401,12 @@ def _parse_manifest_server_config( # Check if a custom URL is provided custom_url = self._extract_server_url(server_element) - # If custom URL is provided, use it directly; otherwise construct from base URL - if custom_url: - return MCPServerConfig( - mcp_server_name=name, mcp_server_unique_name=server_name, url=custom_url - ) - else: - # Construct full URL using environment utilities - full_url = build_mcp_server_url(server_name) - return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=full_url) + # Determine the final URL: use custom URL if provided, otherwise construct it + final_url = custom_url if custom_url else build_mcp_server_url(server_name) + + return MCPServerConfig( + mcp_server_name=name, mcp_server_unique_name=server_name, url=final_url + ) except Exception: return None @@ -436,13 +433,10 @@ def _parse_gateway_server_config( # Check if a custom URL is provided by the gateway custom_url = self._extract_server_url(server_element) - # If custom URL is provided, use it; otherwise use the endpoint as-is - if custom_url: - return MCPServerConfig( - mcp_server_name=name, mcp_server_unique_name=endpoint, url=custom_url - ) - else: - return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=endpoint) + # Determine the final URL: use custom URL if provided, otherwise use endpoint + final_url = custom_url if custom_url else endpoint + + return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=endpoint, url=final_url) except Exception: return None diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index b7b2fcd0..057cfd87 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -123,8 +123,9 @@ def test_parse_manifest_server_config_without_custom_url(self, mock_build_url, s assert config is not None assert config.mcp_server_name == "DefaultServer" - # Without a custom URL, build_mcp_server_url constructs the full URL and stores it in mcp_server_unique_name - assert config.mcp_server_unique_name == "https://default.server/agents/servers/test_server" + assert config.mcp_server_unique_name == "test_server" + # Without a custom URL, build_mcp_server_url constructs the full URL and stores it in the url field + assert config.url == "https://default.server/agents/servers/test_server" mock_build_url.assert_called_once_with("test_server") def test_parse_gateway_server_config_with_custom_url(self, service): @@ -154,7 +155,8 @@ def test_parse_gateway_server_config_without_custom_url(self, service): assert config is not None assert config.mcp_server_name == "GatewayServer" assert config.mcp_server_unique_name == "https://gateway.default/endpoint" - assert config.url is None + # Without a custom URL, the endpoint is used as the url + assert config.url == "https://gateway.default/endpoint" @patch.dict(os.environ, {"ENVIRONMENT": "Development"}) def test_is_development_scenario(self, service): From ccaa2942c30dfee0a956398404cb4709fc44d6cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 19:32:31 +0000 Subject: [PATCH 08/26] Use 'url' field consistently in both manifest and gateway responses Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../mcp_tool_server_configuration_service.py | 4 +--- tests/tooling/test_mcp_server_configuration.py | 13 ++++--------- 2 files changed, 5 insertions(+), 12 deletions(-) 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 b4b757d8..a0e06aed 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 @@ -501,9 +501,7 @@ def _extract_server_url(self, server_element: Dict[str, Any]) -> Optional[str]: Returns: Server URL string or None. """ - # Check for 'mcpServerUrl' (manifest) or 'url' (gateway) - if "mcpServerUrl" in server_element and isinstance(server_element["mcpServerUrl"], str): - return server_element["mcpServerUrl"] + # Check for 'url' field in both manifest and gateway responses if "url" in server_element and isinstance(server_element["url"], str): return server_element["url"] return None diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 057cfd87..e8cd6656 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -72,22 +72,17 @@ def mock_manifest_data(self) -> Dict[str, Any]: { "mcpServerName": "TestServer2", "mcpServerUniqueName": "test_server_2", - "mcpServerUrl": "https://custom.server.com/mcp", + "url": "https://custom.server.com/mcp", }, ] } def test_extract_server_url_from_manifest(self, service): """Test extracting custom URL from manifest element.""" - # Test with mcpServerUrl field - element = {"mcpServerUrl": "https://custom.url.com"} - url = service._extract_server_url(element) - assert url == "https://custom.url.com" - # Test with url field - element = {"url": "https://another.url.com"} + element = {"url": "https://custom.url.com"} url = service._extract_server_url(element) - assert url == "https://another.url.com" + assert url == "https://custom.url.com" # Test with no URL element = {} @@ -99,7 +94,7 @@ def test_parse_manifest_server_config_with_custom_url(self, service): server_element = { "mcpServerName": "CustomServer", "mcpServerUniqueName": "custom_server", - "mcpServerUrl": "https://my.custom.server/mcp", + "url": "https://my.custom.server/mcp", } config = service._parse_manifest_server_config(server_element) From ec8930234da116821efeed75be9a5ffaa5eb4cc4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 20:50:48 +0000 Subject: [PATCH 09/26] Refactor gateway parsing to match manifest logic and improve variable naming Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../mcp_tool_server_configuration_service.py | 10 +++++----- tests/tooling/test_mcp_server_configuration.py | 14 +++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) 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 a0e06aed..5da1551b 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 @@ -425,18 +425,18 @@ def _parse_gateway_server_config( """ try: name = self._extract_server_name(server_element) - endpoint = self._extract_server_unique_name(server_element) + mcp_server_unique_name = self._extract_server_unique_name(server_element) - if not self._validate_server_strings(name, endpoint): + if not self._validate_server_strings(name, mcp_server_unique_name): return None # Check if a custom URL is provided by the gateway custom_url = self._extract_server_url(server_element) - # Determine the final URL: use custom URL if provided, otherwise use endpoint - final_url = custom_url if custom_url else endpoint + # Determine the final URL: use custom URL if provided, otherwise construct it + final_url = custom_url if custom_url else build_mcp_server_url(mcp_server_unique_name) - return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=endpoint, url=final_url) + return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=mcp_server_unique_name, url=final_url) except Exception: return None diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index e8cd6656..8aa691ba 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -138,20 +138,24 @@ def test_parse_gateway_server_config_with_custom_url(self, service): assert config.mcp_server_unique_name == "gateway_server_endpoint" assert config.url == "https://gateway.custom.url/mcp" - def test_parse_gateway_server_config_without_custom_url(self, service): + @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url") + def test_parse_gateway_server_config_without_custom_url(self, mock_build_url, service): """Test parsing gateway config without custom URL.""" + mock_build_url.return_value = "https://default.server/agents/servers/gateway_server" + server_element = { "mcpServerName": "GatewayServer", - "mcpServerUniqueName": "https://gateway.default/endpoint", + "mcpServerUniqueName": "gateway_server", } config = service._parse_gateway_server_config(server_element) assert config is not None assert config.mcp_server_name == "GatewayServer" - assert config.mcp_server_unique_name == "https://gateway.default/endpoint" - # Without a custom URL, the endpoint is used as the url - assert config.url == "https://gateway.default/endpoint" + assert config.mcp_server_unique_name == "gateway_server" + # Without a custom URL, build_mcp_server_url constructs the full URL and stores it in the url field + assert config.url == "https://default.server/agents/servers/gateway_server" + mock_build_url.assert_called_once_with("gateway_server") @patch.dict(os.environ, {"ENVIRONMENT": "Development"}) def test_is_development_scenario(self, service): From 5f056f1fc0b2390cf8fa1b6ea082373ab24c1640 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 20:52:11 +0000 Subject: [PATCH 10/26] Fix formatting: remove extra blank line in test Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- tests/tooling/test_mcp_server_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 8aa691ba..1470ca40 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -142,7 +142,7 @@ def test_parse_gateway_server_config_with_custom_url(self, service): def test_parse_gateway_server_config_without_custom_url(self, mock_build_url, service): """Test parsing gateway config without custom URL.""" mock_build_url.return_value = "https://default.server/agents/servers/gateway_server" - + server_element = { "mcpServerName": "GatewayServer", "mcpServerUniqueName": "gateway_server", From cb3f7b8e870be87a6912d9fdcf99d28a0d8ca312 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:05:51 +0000 Subject: [PATCH 11/26] Rename custom_url to endpoint for better clarity Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_server_configuration_service.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 5da1551b..3a8e4ea5 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 @@ -399,10 +399,10 @@ def _parse_manifest_server_config( return None # Check if a custom URL is provided - custom_url = self._extract_server_url(server_element) + endpoint = self._extract_server_url(server_element) # Determine the final URL: use custom URL if provided, otherwise construct it - final_url = custom_url if custom_url else build_mcp_server_url(server_name) + final_url = endpoint if endpoint else build_mcp_server_url(server_name) return MCPServerConfig( mcp_server_name=name, mcp_server_unique_name=server_name, url=final_url @@ -431,10 +431,10 @@ def _parse_gateway_server_config( return None # Check if a custom URL is provided by the gateway - custom_url = self._extract_server_url(server_element) + endpoint = self._extract_server_url(server_element) # Determine the final URL: use custom URL if provided, otherwise construct it - final_url = custom_url if custom_url else build_mcp_server_url(mcp_server_unique_name) + final_url = endpoint if endpoint else build_mcp_server_url(mcp_server_unique_name) return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=mcp_server_unique_name, url=final_url) From 6ce6e4f3bcb47556673852c1e5c5d15f40e25089 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:11:20 +0000 Subject: [PATCH 12/26] Remove fallback logic and use config.url directly in all tool registration services Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 6 +++--- .../services/mcp_tool_registration_service.py | 4 ++-- .../extensions/openai/mcp_tool_registration_service.py | 6 ++---- .../services/mcp_tool_registration_service.py | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) 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 aff27d18..17080942 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 @@ -97,8 +97,8 @@ async def add_tool_servers_to_agent( # Add servers as MCPStreamableHTTPTool instances for config in server_configs: try: - # Use custom URL if provided, otherwise use the unique name - server_url = config.url if config.url else config.mcp_server_unique_name + # Use the URL from config (always populated by the configuration service) + server_url = config.url if not server_url: self._logger.warning(f"MCP server config missing server_url: {config}") continue @@ -114,7 +114,7 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) - server_name = config.mcp_server_name + server_name = config.mcp_server_name if config.mcp_server_name else config.mcp_server_unique_name # Create and configure MCPStreamableHTTPTool mcp_tools = 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 a4debde3..6c4b5766 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 @@ -178,8 +178,8 @@ async def _get_mcp_tool_definitions_and_resources( else server.mcp_server_name ) - # Use custom URL if provided, otherwise use the unique name - server_url = server.url if server.url else server.mcp_server_unique_name + # Use the URL from server (always populated by the configuration service) + server_url = server.url # Create MCP tool using Azure Foundry SDK mcp_tool = McpTool(server_label=server_label, server_url=server_url) 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 8025dede..9fcadb13 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 @@ -101,10 +101,8 @@ async def add_tool_servers_to_agent( # Convert MCP server configs to MCPServerInfo objects mcp_servers_info = [] for server_config in mcp_server_configs: - # Use custom URL if provided, otherwise use the unique name - server_url = ( - server_config.url if server_config.url else server_config.mcp_server_unique_name - ) + # Use the URL from config (always populated by the configuration service) + server_url = server_config.url server_info = MCPServerInfo( name=server_config.mcp_server_name, url=server_url, 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 43f3dcaa..fa1a7504 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 @@ -125,8 +125,8 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) - # Use custom URL if provided, otherwise use the unique name - server_url = server.url if server.url else server.mcp_server_unique_name + # Use the URL from server (always populated by the configuration service) + server_url = server.url plugin = MCPStreamableHttpPlugin( name=server.mcp_server_name, From 463d9bfb709cfef809a17c1f613413e6de9c2cdf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:13:14 +0000 Subject: [PATCH 13/26] Remove redundant null check for server_url in Agent Framework service Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) 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 17080942..db32f81d 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 @@ -97,12 +97,6 @@ async def add_tool_servers_to_agent( # Add servers as MCPStreamableHTTPTool instances for config in server_configs: try: - # Use the URL from config (always populated by the configuration service) - server_url = config.url - if not server_url: - self._logger.warning(f"MCP server config missing server_url: {config}") - continue - # Prepare auth headers headers = {} if auth_token: @@ -119,7 +113,7 @@ async def add_tool_servers_to_agent( # Create and configure MCPStreamableHTTPTool mcp_tools = MCPStreamableHTTPTool( name=server_name, - url=server_url, + url=config.url, headers=headers, description=f"MCP tools from {server_name}", ) From ad365b0fc4c4d31fb3143e0eec4f89de451d19ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:15:11 +0000 Subject: [PATCH 14/26] Use 'or' operator for cleaner fallback logic in server_name assignment Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../agentframework/services/mcp_tool_registration_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 db32f81d..f9f2338b 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 @@ -108,7 +108,7 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) - server_name = config.mcp_server_name if config.mcp_server_name else config.mcp_server_unique_name + server_name = config.mcp_server_name or config.mcp_server_unique_name # Create and configure MCPStreamableHTTPTool mcp_tools = MCPStreamableHTTPTool( From 32039fc7ab170d4d509cad374d8aa0b5ca0c98bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:16:37 +0000 Subject: [PATCH 15/26] Add clarifying comment for server_name fallback logic Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../agentframework/services/mcp_tool_registration_service.py | 1 + 1 file changed, 1 insertion(+) 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 f9f2338b..a0331265 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 @@ -108,6 +108,7 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) + # Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name server_name = config.mcp_server_name or config.mcp_server_unique_name # Create and configure MCPStreamableHTTPTool From cce7219d291d9ad8e4d172113466dafdf1872977 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:18:10 +0000 Subject: [PATCH 16/26] Fix undefined variable reference in log statement Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../agentframework/services/mcp_tool_registration_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a0331265..4721ee1c 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 @@ -120,7 +120,7 @@ async def add_tool_servers_to_agent( ) # Let Agent Framework handle the connection automatically - self._logger.info(f"Created MCP plugin for '{server_name}' at {server_url}") + self._logger.info(f"Created MCP plugin for '{server_name}' at {config.url}") all_tools.append(mcp_tools) self._connected_servers.append(mcp_tools) From 4a2bd660bfa18ad2d9dfaf9c6b0b418d0645a896 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:22:40 +0000 Subject: [PATCH 17/26] Move server_name assignment outside try block to ensure it's always available in exception handler Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 4721ee1c..18ccc74f 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 @@ -96,6 +96,9 @@ async def add_tool_servers_to_agent( # Add servers as MCPStreamableHTTPTool instances for config in server_configs: + # Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name + server_name = config.mcp_server_name or config.mcp_server_unique_name + try: # Prepare auth headers headers = {} @@ -108,9 +111,6 @@ async def add_tool_servers_to_agent( self._orchestrator_name ) - # Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name - server_name = config.mcp_server_name or config.mcp_server_unique_name - # Create and configure MCPStreamableHTTPTool mcp_tools = MCPStreamableHTTPTool( name=server_name, @@ -129,7 +129,7 @@ async def add_tool_servers_to_agent( except Exception as tool_ex: self._logger.warning( - f"Failed to create MCP plugin for {config.mcp_server_name}: {tool_ex}" + f"Failed to create MCP plugin for {server_name}: {tool_ex}" ) continue From 1cf6acc60913f2a6ba9eeed301b502d285ad94fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:24:23 +0000 Subject: [PATCH 18/26] Clarify comment for server_name fallback logic Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../agentframework/services/mcp_tool_registration_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 18ccc74f..69599f97 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 @@ -96,7 +96,7 @@ async def add_tool_servers_to_agent( # Add servers as MCPStreamableHTTPTool instances for config in server_configs: - # Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name + # Use mcp_server_name if available (not None or empty), otherwise fall back to mcp_server_unique_name server_name = config.mcp_server_name or config.mcp_server_unique_name try: From b5eeb7ebaa40576f32924d9bc6bda2330e10fc67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:30:10 +0000 Subject: [PATCH 19/26] Add server_name fallback logic in OpenAI MCP tool registration service Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../extensions/openai/mcp_tool_registration_service.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 9fcadb13..362c3234 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 @@ -101,10 +101,12 @@ async def add_tool_servers_to_agent( # Convert MCP server configs to MCPServerInfo objects mcp_servers_info = [] for server_config in mcp_server_configs: + # Use mcp_server_name if available (not None or empty), otherwise fall back to mcp_server_unique_name + server_name = server_config.mcp_server_name or server_config.mcp_server_unique_name # Use the URL from config (always populated by the configuration service) server_url = server_config.url server_info = MCPServerInfo( - name=server_config.mcp_server_name, + name=server_name, url=server_url, ) mcp_servers_info.append(server_info) From 6eb790155fa259c56261ed33bc26c030a0278e37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:35:51 +0000 Subject: [PATCH 20/26] Improve variable naming consistency and add server_name fallback in Semantic Kernel service Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 5 ++++- .../mcp_tool_server_configuration_service.py | 20 +++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) 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 fa1a7504..c1485cb8 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 @@ -128,8 +128,11 @@ async def add_tool_servers_to_agent( # Use the URL from server (always populated by the configuration service) server_url = server.url + # Use mcp_server_name if available (not None or empty), otherwise fall back to mcp_server_unique_name + server_name = server.mcp_server_name or server.mcp_server_unique_name + plugin = MCPStreamableHttpPlugin( - name=server.mcp_server_name, + name=server_name, url=server_url, headers=headers, ) 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 3a8e4ea5..a4d13dd8 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 @@ -392,20 +392,20 @@ def _parse_manifest_server_config( MCPServerConfig object or None if parsing fails. """ try: - name = self._extract_server_name(server_element) - server_name = self._extract_server_unique_name(server_element) + mcp_server_name = self._extract_server_name(server_element) + mcp_server_unique_name = self._extract_server_unique_name(server_element) - if not self._validate_server_strings(name, server_name): + if not self._validate_server_strings(mcp_server_name, mcp_server_unique_name): return None - # Check if a custom URL is provided + # Check if a URL is provided endpoint = self._extract_server_url(server_element) # Determine the final URL: use custom URL if provided, otherwise construct it - final_url = endpoint if endpoint else build_mcp_server_url(server_name) + final_url = endpoint if endpoint else build_mcp_server_url(mcp_server_unique_name) return MCPServerConfig( - mcp_server_name=name, mcp_server_unique_name=server_name, url=final_url + mcp_server_name=mcp_server_name, mcp_server_unique_name=mcp_server_unique_name, url=final_url ) except Exception: @@ -424,19 +424,19 @@ def _parse_gateway_server_config( MCPServerConfig object or None if parsing fails. """ try: - name = self._extract_server_name(server_element) + mcp_server_name = self._extract_server_name(server_element) mcp_server_unique_name = self._extract_server_unique_name(server_element) - if not self._validate_server_strings(name, mcp_server_unique_name): + if not self._validate_server_strings(mcp_server_name, mcp_server_unique_name): return None - # Check if a custom URL is provided by the gateway + # Check if a URL is provided by the gateway endpoint = self._extract_server_url(server_element) # Determine the final URL: use custom URL if provided, otherwise construct it final_url = endpoint if endpoint else build_mcp_server_url(mcp_server_unique_name) - return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=mcp_server_unique_name, url=final_url) + return MCPServerConfig(mcp_server_name=mcp_server_name, mcp_server_unique_name=mcp_server_unique_name, url=final_url) except Exception: return None From 0195f7360fef27a5090882305f2a2f06ea69200e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:38:07 +0000 Subject: [PATCH 21/26] Fix inconsistent server_name usage in Semantic Kernel service Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../semantickernel/services/mcp_tool_registration_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c1485cb8..5d7f5bf5 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 @@ -141,7 +141,7 @@ async def add_tool_servers_to_agent( await plugin.connect() # Add plugin to kernel - kernel.add_plugin(plugin, server.mcp_server_name) + kernel.add_plugin(plugin, server_name) # Store reference to keep plugin alive throughout application lifecycle # By storing plugin references in _connected_plugins, we prevent Python's garbage collector from cleaning up the plugin objects From 5cec8d5318e50e7faa0883775ddaefd6543e14f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:50:28 +0000 Subject: [PATCH 22/26] Add server_name fallback logic in configuration service URL construction Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../mcp_tool_server_configuration_service.py | 10 ++++++++-- tests/tooling/test_mcp_server_configuration.py | 14 ++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) 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 a4d13dd8..fbacd57e 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 @@ -401,8 +401,11 @@ def _parse_manifest_server_config( # Check if a URL is provided endpoint = self._extract_server_url(server_element) + # Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name for URL construction + server_name = mcp_server_name or mcp_server_unique_name + # Determine the final URL: use custom URL if provided, otherwise construct it - final_url = endpoint if endpoint else build_mcp_server_url(mcp_server_unique_name) + final_url = endpoint if endpoint else build_mcp_server_url(server_name) return MCPServerConfig( mcp_server_name=mcp_server_name, mcp_server_unique_name=mcp_server_unique_name, url=final_url @@ -433,8 +436,11 @@ def _parse_gateway_server_config( # Check if a URL is provided by the gateway endpoint = self._extract_server_url(server_element) + # Use mcp_server_name if available, otherwise fall back to mcp_server_unique_name for URL construction + server_name = mcp_server_name or mcp_server_unique_name + # Determine the final URL: use custom URL if provided, otherwise construct it - final_url = endpoint if endpoint else build_mcp_server_url(mcp_server_unique_name) + final_url = endpoint if endpoint else build_mcp_server_url(server_name) return MCPServerConfig(mcp_server_name=mcp_server_name, mcp_server_unique_name=mcp_server_unique_name, url=final_url) diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 1470ca40..712aab5d 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -107,7 +107,7 @@ def test_parse_manifest_server_config_with_custom_url(self, service): @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url") def test_parse_manifest_server_config_without_custom_url(self, mock_build_url, service): """Test parsing manifest config without custom URL constructs URL.""" - mock_build_url.return_value = "https://default.server/agents/servers/test_server" + mock_build_url.return_value = "https://default.server/agents/servers/DefaultServer" server_element = { "mcpServerName": "DefaultServer", @@ -120,8 +120,9 @@ def test_parse_manifest_server_config_without_custom_url(self, mock_build_url, s assert config.mcp_server_name == "DefaultServer" assert config.mcp_server_unique_name == "test_server" # Without a custom URL, build_mcp_server_url constructs the full URL and stores it in the url field - assert config.url == "https://default.server/agents/servers/test_server" - mock_build_url.assert_called_once_with("test_server") + # Uses mcp_server_name if available, otherwise falls back to mcp_server_unique_name + assert config.url == "https://default.server/agents/servers/DefaultServer" + mock_build_url.assert_called_once_with("DefaultServer") def test_parse_gateway_server_config_with_custom_url(self, service): """Test parsing gateway config with custom URL.""" @@ -141,7 +142,7 @@ def test_parse_gateway_server_config_with_custom_url(self, service): @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url") def test_parse_gateway_server_config_without_custom_url(self, mock_build_url, service): """Test parsing gateway config without custom URL.""" - mock_build_url.return_value = "https://default.server/agents/servers/gateway_server" + mock_build_url.return_value = "https://default.server/agents/servers/GatewayServer" server_element = { "mcpServerName": "GatewayServer", @@ -154,8 +155,9 @@ def test_parse_gateway_server_config_without_custom_url(self, mock_build_url, se assert config.mcp_server_name == "GatewayServer" assert config.mcp_server_unique_name == "gateway_server" # Without a custom URL, build_mcp_server_url constructs the full URL and stores it in the url field - assert config.url == "https://default.server/agents/servers/gateway_server" - mock_build_url.assert_called_once_with("gateway_server") + # Uses mcp_server_name if available, otherwise falls back to mcp_server_unique_name + assert config.url == "https://default.server/agents/servers/GatewayServer" + mock_build_url.assert_called_once_with("GatewayServer") @patch.dict(os.environ, {"ENVIRONMENT": "Development"}) def test_is_development_scenario(self, service): From 76790f1d2b5504626914bca4a40762598226d6ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 22:10:22 +0000 Subject: [PATCH 23/26] Run ruff formatter to fix formatting issues Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- .../services/mcp_tool_registration_service.py | 2 +- .../mcp_tool_server_configuration_service.py | 10 ++++++++-- tests/tooling/test_mcp_server_configuration.py | 12 +++++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) 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 69599f97..f774ee95 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 @@ -98,7 +98,7 @@ async def add_tool_servers_to_agent( for config in server_configs: # Use mcp_server_name if available (not None or empty), otherwise fall back to mcp_server_unique_name server_name = config.mcp_server_name or config.mcp_server_unique_name - + try: # Prepare auth headers headers = {} 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 fbacd57e..9a18f5e5 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 @@ -408,7 +408,9 @@ def _parse_manifest_server_config( final_url = endpoint if endpoint else build_mcp_server_url(server_name) return MCPServerConfig( - mcp_server_name=mcp_server_name, mcp_server_unique_name=mcp_server_unique_name, url=final_url + mcp_server_name=mcp_server_name, + mcp_server_unique_name=mcp_server_unique_name, + url=final_url, ) except Exception: @@ -442,7 +444,11 @@ def _parse_gateway_server_config( # Determine the final URL: use custom URL if provided, otherwise construct it final_url = endpoint if endpoint else build_mcp_server_url(server_name) - return MCPServerConfig(mcp_server_name=mcp_server_name, mcp_server_unique_name=mcp_server_unique_name, url=final_url) + return MCPServerConfig( + mcp_server_name=mcp_server_name, + mcp_server_unique_name=mcp_server_unique_name, + url=final_url, + ) except Exception: return None diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 712aab5d..bfae2052 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -104,7 +104,9 @@ def test_parse_manifest_server_config_with_custom_url(self, service): assert config.mcp_server_unique_name == "custom_server" assert config.url == "https://my.custom.server/mcp" - @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url") + @patch( + "microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url" + ) def test_parse_manifest_server_config_without_custom_url(self, mock_build_url, service): """Test parsing manifest config without custom URL constructs URL.""" mock_build_url.return_value = "https://default.server/agents/servers/DefaultServer" @@ -139,7 +141,9 @@ def test_parse_gateway_server_config_with_custom_url(self, service): assert config.mcp_server_unique_name == "gateway_server_endpoint" assert config.url == "https://gateway.custom.url/mcp" - @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url") + @patch( + "microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.build_mcp_server_url" + ) def test_parse_gateway_server_config_without_custom_url(self, mock_build_url, service): """Test parsing gateway config without custom URL.""" mock_build_url.return_value = "https://default.server/agents/servers/GatewayServer" @@ -190,7 +194,9 @@ async def test_list_tool_servers_development(self, mock_load_manifest, service): assert servers == mock_servers mock_load_manifest.assert_called_once() - @patch("microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.get_tooling_gateway_for_digital_worker") + @patch( + "microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service.get_tooling_gateway_for_digital_worker" + ) @patch.dict(os.environ, {"ENVIRONMENT": "Production"}) @pytest.mark.asyncio async def test_list_tool_servers_production_with_custom_url(self, mock_gateway_url, service): From b7a904456c19ecabf6e2806b01031341b85404c7 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Tue, 30 Dec 2025 19:47:27 -0800 Subject: [PATCH 24/26] Update tests/tooling/test_mcp_server_configuration.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/tooling/test_mcp_server_configuration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index bfae2052..1d0cefab 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -5,7 +5,6 @@ import json import os -from pathlib import Path from typing import Dict, Any from unittest.mock import Mock, patch, AsyncMock, MagicMock import pytest From 025fb53413b0c6428d11497c4d0c9b30462045ca Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Tue, 30 Dec 2025 19:47:50 -0800 Subject: [PATCH 25/26] Update tests/tooling/test_mcp_server_configuration.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/tooling/test_mcp_server_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 1d0cefab..596e6a2b 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -6,7 +6,7 @@ import json import os from typing import Dict, Any -from unittest.mock import Mock, patch, AsyncMock, MagicMock +from unittest.mock import patch, AsyncMock, MagicMock import pytest import aiohttp From 2621e1250988d02c542817b58a4db2cdf4675073 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Tue, 30 Dec 2025 19:48:00 -0800 Subject: [PATCH 26/26] Update tests/tooling/test_mcp_server_configuration.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/tooling/test_mcp_server_configuration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tooling/test_mcp_server_configuration.py b/tests/tooling/test_mcp_server_configuration.py index 596e6a2b..34c60097 100644 --- a/tests/tooling/test_mcp_server_configuration.py +++ b/tests/tooling/test_mcp_server_configuration.py @@ -8,7 +8,6 @@ from typing import Dict, Any from unittest.mock import patch, AsyncMock, MagicMock import pytest -import aiohttp from microsoft_agents_a365.tooling.services.mcp_tool_server_configuration_service import ( McpToolServerConfigurationService,