1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+
16+ from unittest .mock import AsyncMock
1517from unittest .mock import MagicMock
1618from unittest .mock import patch
1719
1820from a2a .types import TransportProtocol as A2ATransport
1921from google .adk .agents .remote_a2a_agent import RemoteA2aAgent
2022from google .adk .integrations .agent_registry import _ProtocolType
2123from google .adk .integrations .agent_registry import AgentRegistry
24+ from google .adk .telemetry .tracing import GCP_MCP_SERVER_DESTINATION_ID
2225from google .adk .tools .mcp_tool .mcp_toolset import McpToolset
2326import httpx
27+ from mcp import ClientSession
28+ from mcp .types import ListToolsResult
29+ from mcp .types import Tool
2430import pytest
2531
2632
@@ -31,6 +37,130 @@ def registry(self):
3137 with patch ("google.auth.default" , return_value = (MagicMock (), "project-id" )):
3238 return AgentRegistry (project_id = "test-project" , location = "global" )
3339
40+ @pytest .mark .asyncio
41+ @patch ("httpx.Client" )
42+ @patch (
43+ "google.adk.tools.mcp_tool.mcp_session_manager.MCPSessionManager.create_session" ,
44+ new_callable = AsyncMock ,
45+ )
46+ async def test_get_mcp_toolset_adds_destination_id (
47+ self , mock_create_session , mock_httpx , registry
48+ ):
49+ """Test that tools from get_mcp_toolset have the destination ID."""
50+ # Arrange
51+ mcp_server_name = "test-mcp-server"
52+ mock_api_response = MagicMock ()
53+ mock_api_response .json .return_value = {
54+ "displayName" : "TestPrefix" ,
55+ "mcpServerId" : (
56+ "urn:mcp:googleapis.com:projects:1234:locations:global:bigquery"
57+ ),
58+ "interfaces" : [{
59+ "url" : "https://mcp.com" ,
60+ "protocolBinding" : A2ATransport .jsonrpc ,
61+ }],
62+ }
63+ mock_httpx .return_value .__enter__ .return_value .get .return_value = (
64+ mock_api_response
65+ )
66+
67+ registry ._credentials .token = "token"
68+ registry ._credentials .refresh = MagicMock ()
69+
70+ mock_session = AsyncMock (spec = ClientSession )
71+ mock_create_session .return_value = mock_session
72+
73+ # Mock the tools returned by list_tools
74+ mock_session .list_tools .return_value = ListToolsResult (
75+ tools = [
76+ Tool (
77+ name = "tool1" ,
78+ description = "d1" ,
79+ inputs = {},
80+ outputs = {},
81+ inputSchema = {},
82+ ),
83+ Tool (
84+ name = "tool2" ,
85+ description = "d2" ,
86+ inputs = {},
87+ outputs = {},
88+ inputSchema = {},
89+ ),
90+ ]
91+ )
92+
93+ # Act
94+ toolset = registry .get_mcp_toolset (mcp_server_name )
95+ tools = await toolset .get_tools ()
96+
97+ # Assert
98+ assert isinstance (toolset , McpToolset )
99+ mock_session .list_tools .assert_called_once_with ()
100+ assert len (tools ) == 2
101+ for tool in tools :
102+ assert tool .custom_metadata is not None
103+ assert (
104+ tool .custom_metadata .get (GCP_MCP_SERVER_DESTINATION_ID )
105+ == "urn:mcp:googleapis.com:projects:1234:locations:global:bigquery"
106+ )
107+
108+ @pytest .mark .asyncio
109+ @patch ("httpx.Client" )
110+ @patch (
111+ "google.adk.tools.mcp_tool.mcp_session_manager.MCPSessionManager.create_session" ,
112+ new_callable = AsyncMock ,
113+ )
114+ async def test_get_mcp_toolset_handles_missing_destination_id (
115+ self , mock_create_session , mock_httpx , registry
116+ ):
117+ """Test get_mcp_toolset when the destination ID is missing."""
118+ # Arrange
119+ mcp_server_name = "test-mcp-server"
120+ mock_api_response = MagicMock ()
121+ mock_api_response .json .return_value = {
122+ "displayName" : "TestPrefix" ,
123+ # "mcpServerId" is intentionally omitted
124+ "interfaces" : [{
125+ "url" : "https://mcp.com" ,
126+ "protocolBinding" : A2ATransport .jsonrpc ,
127+ }],
128+ }
129+ mock_httpx .return_value .__enter__ .return_value .get .return_value = (
130+ mock_api_response
131+ )
132+
133+ registry ._credentials .token = "token"
134+ registry ._credentials .refresh = MagicMock ()
135+
136+ mock_session = AsyncMock (spec = ClientSession )
137+ mock_create_session .return_value = mock_session
138+
139+ # Mock the tools returned by list_tools
140+ mock_session .list_tools .return_value = ListToolsResult (
141+ tools = [
142+ Tool (
143+ name = "tool1" ,
144+ description = "d1" ,
145+ inputs = {},
146+ outputs = {},
147+ inputSchema = {},
148+ ),
149+ ]
150+ )
151+
152+ # Act
153+ toolset = registry .get_mcp_toolset (mcp_server_name )
154+ tools = await toolset .get_tools ()
155+
156+ # Assert
157+ assert isinstance (toolset , McpToolset )
158+ mock_session .list_tools .assert_called_once_with ()
159+ assert len (tools ) == 1
160+ for tool in tools :
161+ # The custom_metadata shouldn't have been added
162+ assert tool .custom_metadata is None
163+
34164 def test_init_raises_value_error_if_params_missing (self ):
35165 with pytest .raises (
36166 ValueError , match = "project_id and location must be provided"
0 commit comments