A conversational Google ADK agent powered by Gemini 2.5 Flash with Scalekit integration that demonstrates tool calling capabilities. This project serves as a foundation for building Google ADK agents with real API integrations.
Required: You need both Google API and Scalekit credentials.
- Google API Key: Get from Google AI Studio
- Scalekit Credentials: Get from your Scalekit dashboard at app.scalekit.com
- Navigate to Developers → Settings → API Credentials
- Ensure you have access to Gemini API
-
Clone or download this project
-
Install dependencies using uv:
uv sync
Or using pip:
pip install -r requirements.txt
-
Set up your environment variables:
cp scalekit_tool_agent/.env.example scalekit_tool_agent/.env
Edit scalekit_tool_agent/.env and add your credentials
Create a scalekit_tool_agent/.env file with the following variables:
Get your Scalekit credentials from your dashboard at app.scalekit.com:
- Navigate to Developers → Settings → API Credentials
# Google Configuration
GOOGLE_GENAI_USE_VERTEXAI=FALSE
GOOGLE_API_KEY=your_google_api_key_here
# Scalekit Configuration
SCALEKIT_CLIENT_ID=your_scalekit_client_id
SCALEKIT_CLIENT_SECRET=your_scalekit_client_secret
SCALEKIT_ENV_URL=your_scalekit_environment_urlThis sample defaults to gemini-2.5-flash, but the Scalekit integration is not tied to Gemini. If you already have Anthropic or OpenAI credentials, or if Gemini is rate-limited in your environment, you can swap the model without changing the Scalekit tool setup.
For ADK Python, the most straightforward non-Gemini path is LiteLlm.
-
Install ADK's optional model extensions:
pip install "google-adk[extensions]" -
Add the provider API key you want to use to
scalekit_tool_agent/.env:# For Anthropic via LiteLLM ANTHROPIC_API_KEY=your_anthropic_api_key_here # For OpenAI via LiteLLM OPENAI_API_KEY=your_openai_api_key_here
-
Update
scalekit_tool_agent/agent.pyto useLiteLlminstead of a Gemini model string:from google.adk.agents import Agent from google.adk.models.lite_llm import LiteLlm root_agent = Agent( name="scalekit_tool_agent", model=LiteLlm(model="anthropic/claude-3-haiku-20240307"), description=( "You are a tool agent that can work with Gmail and answer user questions." ), instruction=( "Use Gmail tools to answer user questions based on their emails " "and general questions." ), tools=gmail_tools, )
Or switch to OpenAI:
model=LiteLlm(model="openai/gpt-4o")
Notes:
LiteLlmexpects provider credentials such asANTHROPIC_API_KEYorOPENAI_API_KEYto be available in the environment.- The rest of this sample stays the same. You are only swapping the
model=configuration. - If you install only core
google-adk, importinggoogle.adk.models.lite_llmwill fail. Install theextensionsextra first.
This sample currently uses a terminal-first authorization flow. That is fine for local development, where you can open the OAuth URL and then press Enter in the same shell after authorization completes.
-
Run the agent. If you are using
uv, activate your virtual environment first:source .venv/bin/activate adk run scalekit_tool_agent -
Click the Gmail authorization link printed in the terminal, complete authorization, and then press Enter to continue.
- Start chatting with the agent and ask questions about your emails or general queries.
Do not deploy the current input()-based flow unchanged to Railway or other server environments.
The sample prints the OAuth URL and then waits here:
print(f"Visit this URL to authorize: {auth.link}")
input("Press Enter after authorization...")That works in a local terminal, but a deployed process usually has no interactive keyboard input. The app starts, prints the URL, and then waits forever. The deployment can look healthy even though the agent is effectively frozen.
For cloud deployments, use a server-compatible pattern:
- On each request, call
get_or_create_connected_account(...). - If
connected_account.statusis notACTIVE, generate an authorization link. - Return that link to the user as a normal conversational response and stop the agent for that turn.
- After the user authorizes access, have them send their message again.
- Only continue with Gmail-dependent execution after the account is active.
In ADK, the recommended place for this check is before_agent_callback, so the first message can return the OAuth URL gracefully instead of blocking the process.
from typing import Optional
from google.adk.agents.callback_context import CallbackContext
from google.genai import types
def ensure_gmail_connection(
callback_context: CallbackContext,
) -> Optional[types.Content]:
response = actions.get_or_create_connected_account(
connection_name=connection_name,
identifier=identifier,
)
connected_account = response.connected_account
if connected_account.status == "ACTIVE":
return None
auth = actions.get_authorization_link(
identifier=identifier,
connection_name=connection_name,
)
return types.Content(
role="model",
parts=[
types.Part(
text=(
"Authorize Gmail access before I can use Gmail tools: "
f"{auth.link}\n\n"
"After you finish authorization, send your message again."
)
)
],
)Attach that callback to the root agent for deployed environments:
root_agent = Agent(
name="scalekit_tool_agent",
model="gemini-2.5-flash",
description="You are a tool agent that can work with Gmail.",
instruction="Use Gmail tools to answer questions about the user's email.",
before_agent_callback=ensure_gmail_connection,
tools=gmail_tools,
)If your deployment constructs Gmail tools during startup, move that initialization behind the same account-status check or into a request path that runs only after authorization succeeds.
If you keep a separate local-dev flow, reserve input() for terminal testing only. For deployed agents, return the authorization link conversationally on the first message and let the user retry once the connected account is active.
- "Show me my 3 latest emails"
- "What are my unread emails about?"
- "Search for emails from [sender]"