From 596050b02423d53d34742f5d33499c91cf0b865e Mon Sep 17 00:00:00 2001 From: chelsealong Date: Fri, 18 Sep 2026 23:03:47 +0000 Subject: [PATCH] fix(agent_registry): source MCP destination ID from RuntimeReference.uri get_mcp_toolset() stamped gcp.mcp.server.destination.id on tools using mcpServerId, a urn:mcp:... value. The Agent Platform Topology view's connection matcher resolves against the App Hub resource-name URI form instead, which the Agent Registry API returns in the same response under attributes["agentregistry.googleapis.com/system/RuntimeReference"]["uri"]. Since the two never matched, no topology connection was ever drawn for an MCP server reached through AgentRegistry. Prefer RuntimeReference.uri for the destination span attribute, falling back to mcpServerId when the attribute is absent. mcpServerId is left unchanged everywhere else (e.g. IAM binding resolution), which still expects the urn form. Fixes #7196 --- .../agent_registry/agent_registry.py | 13 +++- .../agent_registry/test_agent_registry.py | 65 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/google/adk/integrations/agent_registry/agent_registry.py b/src/google/adk/integrations/agent_registry/agent_registry.py index 2a9989f2508..c5f907f0b59 100644 --- a/src/google/adk/integrations/agent_registry/agent_registry.py +++ b/src/google/adk/integrations/agent_registry/agent_registry.py @@ -457,6 +457,17 @@ def get_mcp_toolset( if not isinstance(mcp_server_id, str): mcp_server_id = None + # Prefer the App Hub resource-name URI (`RuntimeReference.uri`) for the + # destination span attribute, since that is the identifier form the + # Agent Platform Topology view's connection matcher resolves against. + # Fall back to `mcpServerId` if the attribute is absent. + runtime_reference = (server_details.get("attributes") or {}).get( + "agentregistry.googleapis.com/system/RuntimeReference" + ) or {} + destination_resource_id = runtime_reference.get("uri") + if not isinstance(destination_resource_id, str): + destination_resource_id = mcp_server_id + endpoint_uri, _, _ = self._get_connection_uri( server_details, protocol_binding=_compat.TP_JSONRPC ) @@ -491,7 +502,7 @@ def combined_header_provider(context: ReadonlyContext) -> Dict[str, str]: return headers return AgentRegistrySingleMcpToolset( - destination_resource_id=mcp_server_id, + destination_resource_id=destination_resource_id, connection_params=connection_params, tool_name_prefix=name, header_provider=combined_header_provider, diff --git a/tests/unittests/integrations/agent_registry/test_agent_registry.py b/tests/unittests/integrations/agent_registry/test_agent_registry.py index b178b1b8b79..84825fbce5d 100644 --- a/tests/unittests/integrations/agent_registry/test_agent_registry.py +++ b/tests/unittests/integrations/agent_registry/test_agent_registry.py @@ -175,6 +175,71 @@ async def test_get_mcp_toolset_adds_destination_id( == "urn:mcp:googleapis.com:projects:1234:locations:global:bigquery" ) + @pytest.mark.asyncio + @patch( + "google.adk.tools.mcp_tool.mcp_session_manager.MCPSessionManager.create_session", + new_callable=AsyncMock, + ) + async def test_get_mcp_toolset_prefers_runtime_reference_uri( + self, mock_create_session, registry + ): + """destination ID should come from RuntimeReference.uri, not mcpServerId. + + App Hub / Agent Platform Topology matches on the RuntimeReference URI + form, not the mcpServerId urn form, so the two must not be conflated. + """ + # Arrange + mcp_server_name = "test-mcp-server" + mock_api_response = MagicMock() + mock_api_response.json.return_value = { + "displayName": "TestPrefix", + "mcpServerId": ( + "urn:mcp:googleapis.com:projects:1234:locations:global:bigquery" + ), + "attributes": { + "agentregistry.googleapis.com/system/RuntimeReference": { + "uri": ( + "//agentregistry.googleapis.com/projects/1234/locations/" + "global/services/bigquery" + ), + }, + }, + "interfaces": [{ + "url": "https://mcp.com", + "protocolBinding": "JSONRPC", + }], + } + registry._session.get.return_value = mock_api_response + + registry._credentials.token = "token" + registry._credentials.refresh = MagicMock() + + mock_session = AsyncMock(spec=ClientSession) + mock_create_session.return_value = mock_session + + mock_session.list_tools.return_value = ListToolsResult( + tools=[ + Tool( + name="tool1", + description="d1", + inputs={}, + outputs={}, + inputSchema={}, + ), + ] + ) + + # Act + toolset = registry.get_mcp_toolset(mcp_server_name) + tools = await toolset.get_tools() + + # Assert + assert len(tools) == 1 + assert tools[0].custom_metadata.get(GCP_MCP_SERVER_DESTINATION_ID) == ( + "//agentregistry.googleapis.com/projects/1234/locations/global/" + "services/bigquery" + ) + @pytest.mark.asyncio @patch( "google.adk.tools.mcp_tool.mcp_session_manager.MCPSessionManager.create_session",