Everything you need to equip your ADK agents with the right capabilities
Tools are how ADK agents do things
An ADK LlmAgent without tools is just a chatbot. Tools are what let agents search the web, execute code, call APIs, query databases, and interact with external systems. This guide covers the three layers of ADK tooling: built-ins, custom Python functions, and MCP servers.
Built-in tools
ADK ships with three production-ready built-in tools that require no extra setup beyond enabling them.
| Tool | Import | What it does |
|---|---|---|
| google_search | google.adk.tools.google_search | Web search via Google Search API |
| code_execution | google.adk.tools.code_execution | Runs Python code in a sandbox |
| vertex_ai_search | google.adk.tools.vertex_ai_search | Searches your Vertex AI Search corpus |
from google.adk.agents import LlmAgent
from google.adk.tools import google_search, code_execution
research_agent = LlmAgent(
name='researcher',
model='gemini-2.0-flash',
instruction='You are a research assistant. Use search to find current information.',
tools=[google_search], # enable web search
)
data_agent = LlmAgent(
name='data_analyst',
model='gemini-2.0-flash',
instruction='Analyse data and produce charts. You can execute Python code.',
tools=[code_execution], # enable Python sandbox
)
google_search requires a Google Search API key and Custom Search Engine ID. code_execution runs in a Vertex AI sandbox — it does not execute locally and incurs additional costs.Custom function tools
Wrap any Python function as a tool using FunctionTool. ADK uses the function's docstring and type annotations to build the tool schema automatically — the quality of your docstring directly affects how reliably the LLM calls the tool correctly.
from google.adk.tools import FunctionTool
import requests
def get_stock_price(ticker: str) -> dict:
"""
Returns the current stock price for a given ticker symbol.
Args:
ticker: The stock ticker symbol, e.g. 'AAPL' or 'GOOGL'.
Returns:
A dict with 'ticker', 'price', and 'currency' keys.
"""
# Replace with a real API call
return {'ticker': ticker, 'price': 185.23, 'currency': 'USD'}
stock_tool = FunctionTool(get_stock_price)
finance_agent = LlmAgent(
name='finance_agent',
model='gemini-2.0-flash',
instruction='Help users with stock price queries.',
tools=[stock_tool],
)
Write docstrings in the Args: / Returns: Google style. ADK parses them to generate descriptions for each parameter — these descriptions appear in the tool schema the LLM sees. Better descriptions = fewer tool call mistakes.Async function tools
For I/O-bound operations like API calls, use async functions. ADK handles async tools transparently.
import httpx
from google.adk.tools import FunctionTool
async def fetch_weather(city: str) -> dict:
"""
Fetches current weather for a city.
Args:
city: The city name, e.g. 'London' or 'New York'.
Returns:
Dict with 'temperature_c', 'condition', and 'humidity_pct'.
"""
async with httpx.AsyncClient() as client:
r = await client.get(f'https://wttr.in/{city}?format=j1')
data = r.json()
current = data['current_condition'][0]
return {
'temperature_c': int(current['temp_C']),
'condition': current['weatherDesc'][0]['value'],
'humidity_pct': int(current['humidity']),
}
weather_tool = FunctionTool(fetch_weather)
Toolsets: grouping tools for reuse
A Toolset is a collection of related tools that can be added to an agent as a unit. Use toolsets to organise tools by domain and share them across multiple agents.
from google.adk.tools import ToolContext
# Define CRM tools as a group
def get_customer(customer_id: str) -> dict:
"""Fetches a customer record by ID."""
return {'id': customer_id, 'name': 'Alice', 'tier': 'premium'}
def update_customer_note(customer_id: str, note: str) -> str:
"""Adds a note to a customer's account."""
return f'Note added to customer {customer_id}.'
crm_tools = [FunctionTool(get_customer), FunctionTool(update_customer_note)]
# Reuse across agents
support_agent = LlmAgent(
name='support',
model='gemini-2.0-flash',
instruction='Handle customer support queries.',
tools=crm_tools, # shared toolset
)
billing_agent = LlmAgent(
name='billing',
model='gemini-2.0-flash',
instruction='Handle billing questions.',
tools=crm_tools, # same toolset
)
MCP integration
ADK can connect to any MCP server via MCPToolset. This gives your agents access to hundreds of community-built tools — file systems, databases, GitHub, Slack, and more — without writing custom code.
import asyncio
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters
async def create_agent_with_mcp():
# Connect to the filesystem MCP server
mcp_tools, exit_stack = await MCPToolset.from_server(
connection_params=StdioServerParameters(
command='npx',
args=['-y', '@modelcontextprotocol/server-filesystem', '/tmp/workspace'],
)
)
agent = LlmAgent(
name='file_agent',
model='gemini-2.0-flash',
instruction='Help users manage files in the workspace directory.',
tools=mcp_tools,
)
return agent, exit_stack
async def main():
agent, exit_stack = await create_agent_with_mcp()
async with exit_stack:
from google.adk.runners import InMemoryRunner
runner = InMemoryRunner(agent=agent, app_name='file_app')
# ... run your agent
Always use the exit_stack context manager when using MCPToolset. It ensures the MCP subprocess is cleanly terminated when your agent finishes.Debugging tool call failures
When an agent fails to call a tool correctly, these are the most common causes:
| Symptom | Cause | Fix |
|---|---|---|
| Agent describes tool output instead of calling it | Weak model or ambiguous instruction | Use gemini-2.0-flash or better; add 'use your tools' to system prompt |
| Wrong parameter values | Poor docstring descriptions | Add detailed Args: section to function docstring |
| Tool returns error dict, agent ignores it | Error not clearly labelled | Return {'error': 'message', 'success': False} consistently |
| MCP tool not found | MCP server not running or wrong args | Test MCP server independently with npx @modelcontextprotocol/inspector |
| Async tool times out | httpx default timeout too short | Set timeout=httpx.Timeout(30.0) in your async client |
# Enable verbose tool call logging
import logging
logging.getLogger('google.adk').setLevel(logging.DEBUG)
# Or inspect events from the runner
from google.adk.events import Event
async for event in runner.run_async(user_id='u1', session_id='s1', new_message=msg):
if event.get_function_calls(): # LLM requested a tool call
print('Tool calls:', event.get_function_calls())
if event.get_function_responses(): # Tool returned a result
print('Tool responses:', event.get_function_responses())