LearnTool CallingAuthorized tool calling using Google ADK

Authorized tool calling using Google ADK

Google ADK is a framework for building AI agents. Arcade provides an integration with Google ADK to allow you to use Arcade tools in your Google ADK agents with ease.

Overview

In this guide, you’ll learn how to use Arcade to call tools using Google ADK. In particular, you’ll learn how to call tools that require user authorization.

We’ll start with a barebones example of a Google ADK agent that can only chat. Then, we’ll add tools one by one, we’ll add entire toolkits at once, and we’ll handle authorization for tools that require it.

Prerequisites

Setup your environment

Google ADK uses Python. We’ll use the uv package manager to install python, as well as the necessary dependencies.

Create a new directory for this project. This guides assumes you will create it in your home directory, but feel free to create it anywhere you like.

mkdir -p ~/auth-tool-calling-adk
cd ~/auth-tool-calling-adk

Create a new uv project, and activate the virtual environment.

uv init
source .venv/bin/activate

This will have created a .venv directory with the virtual environment, as well as a pyproject.toml file with the project metadata, and a main.py file with the entry point for the application. We’ll use the main.py file to write our code.

Install the necessary dependencies:

uv add google-adk-arcade

Configure your API keys

Set the following environment variables:

export ARCADE_API_KEY='{arcade_api_key}'
export GOOGLE_API_KEY='YOUR_GOOGLE_API_KEY'
export GOOGLE_GENAI_USE_VERTEXAI=FALSE

or create a .env file with the following contents:

# .env
ARCADE_API_KEY={arcade_api_key}
GOOGLE_API_KEY=YOUR_GOOGLE_API_KEY
GOOGLE_GENAI_USE_VERTEXAI=FALSE

We need to set GOOGLE_GENAI_USE_VERTEXAI=FALSE because we’re using the Gemini API, not Vertex AI.

Create a simple agent

We’ll start with a simple agent that can only chat.

import asyncio
from google.adk import Agent, Runner
from google.adk.artifacts import InMemoryArtifactService
from google.adk.sessions import InMemorySessionService, Session
from google.genai import types
from dotenv import load_dotenv
 
 
load_dotenv(override=True)
 
 
async def main():
    app_name = 'Arcade Google ADK'
    user_id = '{arcade_user_id}'
    session_service = InMemorySessionService()
    artifact_service = InMemoryArtifactService()
 
    # This creates a simple agent, and for now it does not have any tools.
    agent = Agent(
        model="gemini-2.0-flash",
        name="simple_agent",
        instruction="You are a helpful assistant that can help users with"
                    " everyday tasks.",
    )
    session = await session_service.create_session(
        app_name=app_name, user_id=user_id, state={
            "user_id": user_id,
        }
    )
    runner = Runner(
        app_name=app_name,
        agent=agent,
        artifact_service=artifact_service,
        session_service=session_service,
    )
 
    # This is a helper function to run the agent with a prompt.
    async def run_prompt(session: Session, new_message: str):
        content = types.Content(
            role='user', parts=[types.Part.from_text(text=new_message)]
        )
        async for event in runner.run_async(
            user_id=user_id,
            session_id=session.id,
            new_message=content,
        ):
            if event.content.parts and event.content.parts[0].text:
                print(f'** {event.author}: {event.content.parts[0].text}')
 
    # This is the main agent loop to prompt the user until they exit.
    while True:
        user_input = input("User: ")
        if user_input.lower() == "exit":
            print("Goodbye!")
            break
        await run_prompt(session, user_input)
 
 
if __name__ == "__main__":
    asyncio.run(main())

So far, we’ve only used Google ADK’s primitives. The agent is already functional and will invoke the model and keep the messages in memory for the duration of the conversation.

Now, let’s add a tool to the agent.

Add a tool to the agent

Now, let’s update the code to add:

  • An Arcade client
  • Get a specific tool from the Arcade Platform
  • Pass the tool to the agent

In 5 lines of code, we’ve done all of this. And now the agent can invoke the tool. If the tool requires authorization, the tool will reply with a URL for the user to visit, which will be displayed to the user. If the tool does not require authorization, the tool will be invoked directly.

import asyncio
from arcadepy import AsyncArcade
from google.adk import Agent, Runner
from google.adk.artifacts import InMemoryArtifactService
from google.adk.sessions import InMemorySessionService, Session
from google.genai import types
from google_adk_arcade.tools import get_arcade_tools
from dotenv import load_dotenv
 
 
load_dotenv(override=True)
 
 
async def main():
    # initialize the Arcade client
    client = AsyncArcade()
    app_name = "Arcade Google ADK"
    user_id = "mateo@arcade.dev"
    session_service = InMemorySessionService()
    artifact_service = InMemoryArtifactService()
 
    # This function returns a list of tools in the format expected by ADK
    gmail_list_emails = await get_arcade_tools(client, tools=["Gmail_ListEmails"])
 
    # We've updated the agent to access a single tool
    agent = Agent(
        model="gemini-2.0-flash",
        name="simple_agent",
        instruction="You are a helpful assistant that can help users with"
                    " everyday tasks. It is very important that you use"
                    " the provided tools to ensure the task is successfully"
                    " achieved",
        tools=gmail_list_emails,  # pass the tool to the agent
    )
    session = await session_service.create_session(
        app_name=app_name, user_id=user_id, state={
            "user_id": user_id,
        }
    )
    runner = Runner(
        app_name=app_name,
        agent=agent,
        artifact_service=artifact_service,
        session_service=session_service,
    )
 
    # This is a helper function to run the agent with a prompt.
    async def run_prompt(session: Session, new_message: str):
        content = types.Content(
            role='user', parts=[types.Part.from_text(text=new_message)]
        )
        async for event in runner.run_async(
            user_id=user_id,
            session_id=session.id,
            new_message=content,
        ):
            if event.content.parts and event.content.parts[0].text:
                print(f'** {event.author}: {event.content.parts[0].text}')
 
    # This is the main agent loop to prompt the user until they exit.
    while True:
        user_input = input("User: ")
        if user_input.lower() == "exit":
            print("Goodbye!")
            break
        await run_prompt(session, user_input)
 
 
if __name__ == "__main__":
    asyncio.run(main())