|
18 | 18 | from typing import Any |
19 | 19 | from unittest import mock |
20 | 20 |
|
21 | | -import pytest |
22 | | - |
23 | | -pytest.importorskip( |
24 | | - "google.cloud.iamconnectorcredentials_v1alpha", |
25 | | - reason="Requires google-cloud-iamconnectorcredentials", |
26 | | -) |
27 | | - |
28 | 21 | from google.adk import Agent |
29 | 22 | from google.adk import Runner |
30 | 23 | from google.adk.auth.auth_tool import AuthConfig |
|
34 | 27 | from google.adk.integrations.agent_identity import GcpAuthProviderScheme |
35 | 28 | from google.adk.sessions.in_memory_session_service import InMemorySessionService |
36 | 29 | from google.adk.tools.base_authenticated_tool import BaseAuthenticatedTool |
| 30 | +from google.adk.tools.mcp_tool.mcp_tool import McpTool |
37 | 31 | from google.cloud.iamconnectorcredentials_v1alpha import RetrieveCredentialsRequest |
38 | 32 | from google.cloud.iamconnectorcredentials_v1alpha import RetrieveCredentialsResponse |
39 | 33 | from google.genai import types |
| 34 | +from mcp.types import Tool as McpBaseTool |
| 35 | +import pytest |
40 | 36 |
|
41 | 37 | from tests.unittests import testing_utils |
42 | 38 |
|
@@ -200,3 +196,73 @@ async def test_gcp_agent_identity_2lo_gets_token() -> None: |
200 | 196 |
|
201 | 197 | assert function_response.name == "dummy_tool" |
202 | 198 | assert DUMMY_TOKEN in str(function_response.response) |
| 199 | + |
| 200 | + |
| 201 | +@pytest.mark.parametrize("llm_backend", ["GOOGLE_AI"], indirect=True) |
| 202 | +@pytest.mark.asyncio |
| 203 | +async def test_gcp_agent_identity_2lo_sends_authorization_header_to_mcp_session( |
| 204 | + llm_backend: Any, |
| 205 | +) -> None: |
| 206 | + """Ensures a 2LO token from GCP is correctly passed into the outbound MCP session headers.""" |
| 207 | + CredentialManager._auth_provider_registry._providers.clear() |
| 208 | + CredentialManager.register_auth_provider(GcpAuthProvider()) |
| 209 | + |
| 210 | + mock_operation = _DummyOperation() |
| 211 | + with mock.patch.object( |
| 212 | + gcp_auth_provider, "Client", autospec=True |
| 213 | + ) as mock_gcp: |
| 214 | + mock_gcp.return_value.retrieve_credentials.return_value = mock_operation |
| 215 | + |
| 216 | + mock_session_mgr = mock.AsyncMock() |
| 217 | + mock_session_mgr.create_session.return_value.call_tool.return_value = ( |
| 218 | + mock.MagicMock() |
| 219 | + ) |
| 220 | + |
| 221 | + mcp_tool = McpTool( |
| 222 | + mcp_tool=McpBaseTool( |
| 223 | + name="dummy_mcp", |
| 224 | + description="Dummy MCP tool for testing.", |
| 225 | + inputSchema={"type": "object", "properties": {}}, |
| 226 | + ), |
| 227 | + mcp_session_manager=mock_session_mgr, |
| 228 | + auth_scheme=GcpAuthProviderScheme( |
| 229 | + name=TEST_CONNECTOR_2LO, scopes=["test-scope"] |
| 230 | + ), |
| 231 | + ) |
| 232 | + |
| 233 | + agent = Agent( |
| 234 | + name="test_agent", |
| 235 | + model=testing_utils.MockModel.create( |
| 236 | + responses=[ |
| 237 | + types.Part.from_function_call(name="dummy_mcp", args={}), |
| 238 | + "Tool executed successfully.", |
| 239 | + ] |
| 240 | + ), |
| 241 | + instruction="Use dummy_mcp tool.", |
| 242 | + tools=[mcp_tool], |
| 243 | + ) |
| 244 | + |
| 245 | + async for _ in Runner( |
| 246 | + app_name="test_mcp_header_app", |
| 247 | + agent=agent, |
| 248 | + session_service=InMemorySessionService(), |
| 249 | + auto_create_session=True, |
| 250 | + ).run_async( |
| 251 | + user_id="test_user", |
| 252 | + session_id="session-id-2", |
| 253 | + new_message=types.UserContent(parts=[types.Part(text="Run tool.")]), |
| 254 | + ): |
| 255 | + pass |
| 256 | + |
| 257 | + mock_gcp.return_value.retrieve_credentials.assert_called_once_with( |
| 258 | + RetrieveCredentialsRequest( |
| 259 | + connector=TEST_CONNECTOR_2LO, |
| 260 | + user_id="test_user", |
| 261 | + scopes=["test-scope"], |
| 262 | + force_refresh=False, |
| 263 | + ) |
| 264 | + ) |
| 265 | + |
| 266 | + assert mock_session_mgr.create_session.call_args.kwargs.get("headers") == { |
| 267 | + "Authorization": f"Bearer {DUMMY_TOKEN}" |
| 268 | + } |
0 commit comments