ec2b666284
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
169 lines
5.2 KiB
Python
169 lines
5.2 KiB
Python
# Copyright 2026 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Test script for MCP Toolset OAuth Authentication Flow.
|
|
|
|
This script demonstrates the two-phase tool discovery flow:
|
|
1. First invocation: Agent tries to get tools, auth is required, returns auth
|
|
request event (adk_request_credential)
|
|
2. User provides OAuth credentials (simulated)
|
|
3. Second invocation: Agent has credentials, can list and call tools
|
|
|
|
Usage:
|
|
# Start the MCP server first (in another terminal):
|
|
PYTHONPATH=src python contributing/samples/mcp_toolset_auth/oauth_mcp_server.py
|
|
|
|
# Run the demo:
|
|
PYTHONPATH=src python contributing/samples/mcp_toolset_auth/main.py
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
from agent import auth_credential
|
|
from agent import auth_scheme
|
|
from agent import mcp_toolset
|
|
from agent import root_agent
|
|
from google.adk.auth.auth_credential import AuthCredential
|
|
from google.adk.auth.auth_credential import AuthCredentialTypes
|
|
from google.adk.auth.auth_credential import OAuth2Auth
|
|
from google.adk.auth.auth_tool import AuthConfig
|
|
from google.adk.runners import Runner
|
|
from google.adk.sessions.in_memory_session_service import InMemorySessionService
|
|
from google.genai import types
|
|
|
|
|
|
async def run_demo():
|
|
"""Run demo with real MCP server."""
|
|
print('=' * 60)
|
|
print('MCP Toolset OAuth Authentication Demo')
|
|
print('=' * 60)
|
|
print('\nNote: Make sure the MCP server is running:')
|
|
print(' python oauth_mcp_server.py\n')
|
|
|
|
# Create session service and runner
|
|
session_service = InMemorySessionService()
|
|
runner = Runner(
|
|
agent=root_agent,
|
|
app_name='toolset_auth_demo',
|
|
session_service=session_service,
|
|
)
|
|
|
|
# Create a session
|
|
session = await session_service.create_session(
|
|
app_name='toolset_auth_demo',
|
|
user_id='test_user',
|
|
)
|
|
|
|
print(f'Session created: {session.id}')
|
|
print('\n--- Phase 1: Initial request (no credentials) ---\n')
|
|
|
|
# First invocation - should trigger auth request
|
|
user_message = 'List all users'
|
|
print(f'User: {user_message}')
|
|
|
|
events = []
|
|
auth_function_call_id = None
|
|
max_events = 10
|
|
|
|
try:
|
|
async for event in runner.run_async(
|
|
session_id=session.id,
|
|
user_id='test_user',
|
|
new_message=types.Content(
|
|
role='user',
|
|
parts=[types.Part(text=user_message)],
|
|
),
|
|
):
|
|
events.append(event)
|
|
print(f'\nEvent from {event.author}:')
|
|
if event.content and event.content.parts:
|
|
for part in event.content.parts:
|
|
if part.text:
|
|
print(f' Text: {part.text}')
|
|
if part.function_call:
|
|
print(f' Function call: {part.function_call.name}')
|
|
if part.function_call.name == 'adk_request_credential':
|
|
auth_function_call_id = part.function_call.id
|
|
|
|
if len(events) >= max_events:
|
|
print(f'\n** SAFETY LIMIT ({max_events} events) **')
|
|
break
|
|
|
|
except Exception as e:
|
|
print(f'\nError: {e}')
|
|
print('Make sure the MCP server is running!')
|
|
await mcp_toolset.close()
|
|
return
|
|
|
|
if auth_function_call_id:
|
|
print('\n** Auth request detected! **')
|
|
print('\n--- Phase 2: Provide OAuth credentials ---\n')
|
|
|
|
# Simulate user providing OAuth credentials after completing OAuth flow
|
|
auth_response = AuthConfig(
|
|
auth_scheme=auth_scheme,
|
|
raw_auth_credential=auth_credential,
|
|
exchanged_auth_credential=AuthCredential(
|
|
auth_type=AuthCredentialTypes.OAUTH2,
|
|
oauth2=OAuth2Auth(
|
|
access_token='test_access_token_12345',
|
|
),
|
|
),
|
|
)
|
|
|
|
print('Providing access token: test_access_token_12345')
|
|
|
|
auth_response_message = types.Content(
|
|
role='user',
|
|
parts=[
|
|
types.Part(
|
|
function_response=types.FunctionResponse(
|
|
name='adk_request_credential',
|
|
id=auth_function_call_id,
|
|
response=auth_response.model_dump(exclude_none=True),
|
|
)
|
|
)
|
|
],
|
|
)
|
|
|
|
async for event in runner.run_async(
|
|
session_id=session.id,
|
|
user_id='test_user',
|
|
new_message=auth_response_message,
|
|
):
|
|
print(f'\nEvent from {event.author}:')
|
|
if event.content and event.content.parts:
|
|
for part in event.content.parts:
|
|
if part.text:
|
|
text = (
|
|
part.text[:200] + '...' if len(part.text) > 200 else part.text
|
|
)
|
|
print(f' Text: {text}')
|
|
if part.function_call:
|
|
print(f' Function call: {part.function_call.name}')
|
|
else:
|
|
print('\n** No auth request - credentials may already be available **')
|
|
|
|
print('\n' + '=' * 60)
|
|
print('Demo completed')
|
|
print('=' * 60)
|
|
|
|
await mcp_toolset.close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
asyncio.run(run_demo())
|