# 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. from __future__ import annotations import os from google.adk.agents import Agent from google.adk.apps import App from google.adk.auth.auth_credential import AuthCredential from google.adk.auth.auth_tool import AuthConfig from google.adk.auth.credential_manager import CredentialManager from google.adk.integrations.agent_identity import GcpAuthProvider from google.adk.integrations.agent_identity import GcpAuthProviderScheme from google.adk.tools.authenticated_function_tool import AuthenticatedFunctionTool from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams from google.adk.tools.mcp_tool.mcp_toolset import McpToolset import httpx PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT") LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION") MAPS_API_AUTH_PROVIDER_ID = os.environ.get("MAPS_API_AUTH_PROVIDER_ID") SPOTIFY_2LO_AUTH_PROVIDER_ID = os.environ.get("SPOTIFY_2LO_AUTH_PROVIDER_ID") SPOTIFY_3LO_AUTH_PROVIDER_ID = os.environ.get("SPOTIFY_3LO_AUTH_PROVIDER_ID") MAPS_API_AUTH_PROVIDER = ( f"projects/{PROJECT_ID}/locations/{LOCATION}/connectors/" f"{MAPS_API_AUTH_PROVIDER_ID}" ) SPOTIFY_2LO_AUTH_PROVIDER = ( f"projects/{PROJECT_ID}/locations/{LOCATION}/connectors/" f"{SPOTIFY_2LO_AUTH_PROVIDER_ID}" ) SPOTIFY_3LO_AUTH_PROVIDER = ( f"projects/{PROJECT_ID}/locations/{LOCATION}/connectors/" f"{SPOTIFY_3LO_AUTH_PROVIDER_ID}" ) MAPS_MCP_ENDPOINT = "https://mapstools.googleapis.com/mcp" CONTINUE_URI = "http://localhost:8080/commit" MODEL = "gemini-2.5-flash" async def spotify_search_track( credential: AuthCredential, query: str ) -> str | list: """Searches for a track on Spotify and returns its details.""" headers = {} if http := credential.http: if http.scheme and http.credentials and (token := http.credentials.token): headers["Authorization"] = f"{http.scheme.title()} {token}" if http.additional_headers: headers.update(http.additional_headers) if not headers: return "Error: No authentication token available." async with httpx.AsyncClient() as client: response = await client.get( "https://api.spotify.com/v1/search", headers=headers, params={"q": query, "type": "track", "limit": 1}, ) if response.status_code != 200: return f"Error from Spotify API: {response.status_code} - {response.text}" data = response.json() items = data.get("tracks", {}).get("items", []) if not items: return f"No track found for query '{query}'." return items async def spotify_get_playlists(credential: AuthCredential) -> str | list: """Fetches the current user's private playlists on Spotify.""" headers = {} if http := credential.http: if http.scheme and http.credentials and (token := http.credentials.token): headers["Authorization"] = f"{http.scheme.title()} {token}" if http.additional_headers: headers.update(http.additional_headers) if not headers: return "Error: No authentication token available." async with httpx.AsyncClient() as client: response = await client.get( "https://api.spotify.com/v1/me/playlists", headers=headers, params={"limit": 10}, ) if response.status_code != 200: return f"Error from Spotify API: {response.status_code} - {response.text}" data = response.json() items = data.get("items", []) if not items: return "No playlists found for the current user." # Extract useful information return [ { "name": item.get("name"), "public": item.get("public"), "total_tracks": item.get("tracks", {}).get("total"), } for item in items if item ] spotify_auth_config_2lo = AuthConfig( auth_scheme=GcpAuthProviderScheme(name=SPOTIFY_2LO_AUTH_PROVIDER) ) spotify_search_track_tool = AuthenticatedFunctionTool( func=spotify_search_track, auth_config=spotify_auth_config_2lo, ) spotify_auth_config_3lo = AuthConfig( auth_scheme=GcpAuthProviderScheme( name=SPOTIFY_3LO_AUTH_PROVIDER, scopes=["playlist-read-private"], continue_uri=CONTINUE_URI, ) ) spotify_get_playlist_tool = AuthenticatedFunctionTool( func=spotify_get_playlists, auth_config=spotify_auth_config_3lo, ) maps_tools = McpToolset( connection_params=StreamableHTTPConnectionParams(url=MAPS_MCP_ENDPOINT), auth_scheme=GcpAuthProviderScheme(name=MAPS_API_AUTH_PROVIDER), errlog=None, # Required for agent freezing (pickling) ) CredentialManager.register_auth_provider(GcpAuthProvider()) root_agent = Agent( name="gcp_auth_agent", model=MODEL, instruction=( "You are a Spotify and Google Maps assistant. Use your tools to " "search for track details, fetch the user's private playlists, " "and look up locations. Keep responses concise, friendly, and " "emoji-filled!" ), tools=[ spotify_search_track_tool, spotify_get_playlist_tool, maps_tools, ], ) app = App( name="gcp_auth", root_agent=root_agent, )