This page explains how to create and use tools from plain Python functions in ADK. Python function tools are the simplest way to extend agent capabilities with custom logic—just write a regular Python function and ADK automatically converts it into a tool that agents can call.
For the broader tool framework and BaseTool interface, see Tool Framework. For other tool types like MCP, OpenAPI, and built-in Google service integrations, see MCP Tools, OpenAPI and REST Tools, and Built-in Tools and Integrations.
Python function tools allow you to provide agents with custom functionality by writing regular Python functions (synchronous or asynchronous). ADK automatically:
FunctionTool instances implementing the BaseTool interfaceToolContextThe simplest way to create a tool is to define a Python function and pass it to an agent's tools parameter:
ADK inspects the function signature and creates a schema with:
Sources: tests/unittests/flows/llm_flows/test_functions_simple.py34-76
Async functions work identically to sync functions:
ADK automatically detects whether a function is sync or async and executes it appropriately.
Sources: tests/unittests/flows/llm_flows/test_functions_simple.py78-147
Functions can access execution context (state, session info, credentials) by declaring a tool_context: ToolContext parameter:
The ToolContext parameter is automatically injected by ADK and provides:
state: Session state dictionary for reading/writing statesession: Current session objectinvocation_context: Full invocation contextactions: EventActions for requesting auth, confirmation, etc.Sources: tests/unittests/flows/llm_flows/test_functions_simple.py226-241
Sources: src/google/adk/flows/llm_flows/functions.py101-178 tests/unittests/flows/llm_flows/test_functions_simple.py149-224
When an LLM returns function calls, ADK executes them through a multi-stage pipeline with callback hooks and parallel execution support.
Sources: src/google/adk/flows/llm_flows/functions.py349-428 src/google/adk/flows/llm_flows/functions.py430-604
When an LLM returns multiple function calls in a single response, ADK executes them in parallel:
This parallel execution significantly improves performance when agents need to call multiple tools simultaneously.
Sources: src/google/adk/flows/llm_flows/functions.py367-404 tests/unittests/streaming/test_streaming.py288-396
In live/bidirectional streaming mode, blocking tool execution can prevent the event loop from processing user interruptions or model responses. ADK provides thread pool execution to avoid blocking:
Sources: src/google/adk/flows/llm_flows/functions.py64-178 src/google/adk/agents/run_config.py36-300
Sources: src/google/adk/agents/run_config.py255-300
| Tool Type | Tool Content | Thread Pool Behavior | GIL Impact |
|---|---|---|---|
| Sync function | Blocking I/O (network, file, time.sleep) | Runs in background thread, releases GIL | ✅ Event loop stays responsive |
| Sync function | Pure Python CPU work | Runs in background thread, holds GIL | ❌ No parallelism, but separates execution |
| Async function | Blocking I/O (mistakenly used) | New event loop in thread, releases GIL | ✅ Catches blocking mistakes |
| Async function | Proper async/await code | New event loop in thread | ⚠️ Works but unnecessary overhead |
| Sync function | C extension (releases GIL) | Runs in background thread | ✅ True parallelism possible |
Sources: src/google/adk/agents/run_config.py270-285
ADK provides three callback hooks for each tool execution stage, available at both agent and plugin levels:
Sources: src/google/adk/flows/llm_flows/functions.py500-586
Before Tool Callback - Override or log tool execution:
After Tool Callback - Modify or validate results:
On Tool Error Callback - Handle errors gracefully:
Sources: src/google/adk/flows/llm_flows/functions.py439-471 src/google/adk/flows/llm_flows/functions.py513-586
Tools can read and write session state through the ToolContext.state dictionary. State changes are automatically persisted with the event:
State updates in tool_context.state are collected in tool_context.actions.state_delta and applied when the session service processes the event. This ensures state changes are persisted atomically with the function response.
Sources: src/google/adk/flows/llm_flows/functions.py578-581 tests/unittests/flows/llm_flows/test_functions_simple.py226-241
ADK automatically manages function call IDs for tracking:
adk- prefixSources: src/google/adk/flows/llm_flows/functions.py180-215 tests/unittests/flows/llm_flows/test_functions_simple.py243-265
While ADK automatically wraps plain functions, you can explicitly use FunctionTool for more control:
FunctionTool provides:
inspect.signature()_preprocess_args() for type conversionToolContext parameter for automatic injectionSources: tests/unittests/flows/llm_flows/test_functions_simple.py149-224 src/google/adk/flows/llm_flows/functions.py141-178
Python function tools provide the simplest path to extending agent capabilities:
| Feature | Description |
|---|---|
| Definition | Plain Python functions (sync or async) |
| Schema | Auto-generated from type annotations |
| State Access | Via ToolContext parameter |
| Execution | Parallel for multiple calls |
| Callbacks | Before, after, and error hooks |
| Thread Pools | Optional for blocking I/O in live mode |
| Wrapping | Automatic via FunctionTool |
For more advanced tool scenarios, see Tool Confirmation and HITL for human-in-the-loop patterns, Tool Authentication for credential management, and Tool Framework for implementing custom BaseTool subclasses.
Sources: src/google/adk/flows/llm_flows/functions.py1-1941 tests/unittests/flows/llm_flows/test_functions_simple.py1-645
Refresh this wiki