@@ -345,6 +345,130 @@ async def test_add_tool_servers_handles_toolset_creation_error(
345345 # Existing tools should still be present
346346 assert existing_tool in mock_agent .tools
347347
348+ @pytest .mark .asyncio
349+ @pytest .mark .unit
350+ async def test_add_tool_servers_skips_duplicate_server_urls (
351+ self , mock_agent , mock_authorization , mock_turn_context
352+ ):
353+ """Test that duplicate server URLs are not added multiple times."""
354+ with (
355+ patch (
356+ "microsoft_agents_a365.tooling.extensions.googleadk.services.mcp_tool_registration_service.McpToolServerConfigurationService"
357+ ) as mock_config_service_class ,
358+ patch (
359+ "microsoft_agents_a365.tooling.extensions.googleadk.services.mcp_tool_registration_service.Utility"
360+ ) as mock_utility ,
361+ patch (
362+ "microsoft_agents_a365.tooling.extensions.googleadk.services.mcp_tool_registration_service.McpToolset"
363+ ) as mock_toolset_class ,
364+ ):
365+ # Setup mocks
366+ mock_utility .resolve_agent_identity .return_value = "agent-123"
367+ mock_utility .get_user_agent_header .return_value = "Agent365SDK/1.0"
368+
369+ # Create two server configs with the same URL
370+ mock_server_config1 = MagicMock ()
371+ mock_server_config1 .mcp_server_name = "server-1"
372+ mock_server_config1 .url = "https://test-server.example.com/mcp"
373+
374+ mock_server_config2 = MagicMock ()
375+ mock_server_config2 .mcp_server_name = "server-2"
376+ mock_server_config2 .url = "https://test-server.example.com/mcp" # Same URL
377+
378+ mock_config_service = AsyncMock ()
379+ mock_config_service .list_tool_servers = AsyncMock (
380+ return_value = [mock_server_config1 , mock_server_config2 ]
381+ )
382+ mock_config_service_class .return_value = mock_config_service
383+
384+ # Create mock toolsets
385+ mock_toolset1 = MagicMock ()
386+ mock_toolset1 .connection_params = MagicMock ()
387+ mock_toolset1 .connection_params .url = "https://test-server.example.com/mcp"
388+
389+ mock_toolset_class .return_value = mock_toolset1
390+
391+ from microsoft_agents_a365 .tooling .extensions .googleadk import (
392+ McpToolRegistrationService ,
393+ )
394+
395+ service = McpToolRegistrationService ()
396+
397+ # Set up agent with no existing tools
398+ mock_agent .tools = []
399+
400+ # Act
401+ await service .add_tool_servers_to_agent (
402+ agent = mock_agent ,
403+ auth = mock_authorization ,
404+ auth_handler_name = "graph" ,
405+ context = mock_turn_context ,
406+ auth_token = "test-token" ,
407+ )
408+
409+ # Assert - toolset should be created only once despite two configs
410+ assert mock_toolset_class .call_count == 1
411+ assert len (service ._connected_servers ) == 1
412+ assert len (mock_agent .tools ) == 1
413+
414+ @pytest .mark .asyncio
415+ @pytest .mark .unit
416+ async def test_add_tool_servers_skips_existing_servers_in_agent (
417+ self , mock_agent , mock_authorization , mock_turn_context
418+ ):
419+ """Test that servers already in the agent are not added again."""
420+ with (
421+ patch (
422+ "microsoft_agents_a365.tooling.extensions.googleadk.services.mcp_tool_registration_service.McpToolServerConfigurationService"
423+ ) as mock_config_service_class ,
424+ patch (
425+ "microsoft_agents_a365.tooling.extensions.googleadk.services.mcp_tool_registration_service.Utility"
426+ ) as mock_utility ,
427+ patch (
428+ "microsoft_agents_a365.tooling.extensions.googleadk.services.mcp_tool_registration_service.McpToolset"
429+ ) as mock_toolset_class ,
430+ ):
431+ # Setup mocks
432+ mock_utility .resolve_agent_identity .return_value = "agent-123"
433+ mock_utility .get_user_agent_header .return_value = "Agent365SDK/1.0"
434+
435+ # Create server config
436+ mock_server_config = MagicMock ()
437+ mock_server_config .mcp_server_name = "existing-server"
438+ mock_server_config .url = "https://existing-server.example.com/mcp"
439+
440+ mock_config_service = AsyncMock ()
441+ mock_config_service .list_tool_servers = AsyncMock (return_value = [mock_server_config ])
442+ mock_config_service_class .return_value = mock_config_service
443+
444+ from microsoft_agents_a365 .tooling .extensions .googleadk import (
445+ McpToolRegistrationService ,
446+ )
447+
448+ service = McpToolRegistrationService ()
449+
450+ # Set up agent with existing toolset that has the same URL
451+ existing_toolset = MagicMock ()
452+ existing_toolset .connection_params = MagicMock ()
453+ existing_toolset .connection_params .url = "https://existing-server.example.com/mcp"
454+ mock_agent .tools = [existing_toolset ]
455+
456+ # Act
457+ await service .add_tool_servers_to_agent (
458+ agent = mock_agent ,
459+ auth = mock_authorization ,
460+ auth_handler_name = "graph" ,
461+ context = mock_turn_context ,
462+ auth_token = "test-token" ,
463+ )
464+
465+ # Assert - no new toolset should be created
466+ mock_toolset_class .assert_not_called ()
467+ assert len (service ._connected_servers ) == 0
468+ # Agent should still have the one existing tool
469+ assert len (mock_agent .tools ) == 1
470+ assert mock_agent .tools [0 ] == existing_toolset
471+
348472
349473class TestCleanup :
350474 """Tests for cleanup method."""
0 commit comments