Google ADK Integration
Google Agent Development Kit (ADK) is Google's framework for building multi-agent AI systems on top of Gemini models. Control Zero provides first-party Python integration for ADK, wrapping agents and tools with policy enforcement and audit logging.
This integration is Python-only. The Node.js and Go SDKs do not support ADK.
Installation
pip install controlzero google-adk
Quick Start: Governed Agent
The simplest way to add governance to an ADK agent is GovernedAgent with wrap_tools=True. This wraps every tool attached to the agent automatically.
from google.adk.agents import LlmAgent
from controlzero import Client
from controlzero.integrations.google_adk import GovernedAgent
cz = Client(api_key="cz_live_your_api_key_here")
agent = LlmAgent(
name="research_agent",
model="gemini-2.0-flash",
tools=[search_tool, summarize_tool],
instruction="You are a research assistant.",
)
governed = GovernedAgent(
agent=agent,
client=cz,
wrap_tools=True,
)
# Run the agent through the ADK runner as normal.
# All tool calls and agent invocations are now governed.
When wrap_tools=True (the default), every tool in agent.tools is replaced with a GovernedTool wrapper before the agent runs. No changes to your application logic are required.
GovernedAgent parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
agent | BaseAgent | required | The ADK agent to wrap. |
client | Client | required | ControlZero client. |
agent_name | str | agent.name | Name used for policy lookups and audit log entries. Defaults to the agent's own name. |
wrap_tools | bool | True | Automatically wrap all tools in agent.tools with governance. |
allowed_tools | list[str] | None | Optional allowlist of tool names. Tools not in this list are removed from the agent before wrapping. |
log_outputs | bool | False | Include a preview of the run output (up to 500 characters) in the audit log entry. |
Running the governed agent
GovernedAgent exposes run_async, matching the ADK BaseAgent interface:
result = await governed.run_async(invocation_context=ctx)
If a policy denies execution, PolicyDeniedError is raised before the agent runs. All executions (allowed and denied) are recorded in the audit trail.
Manual Wrapping: governed_adk_tool Decorator
Use the governed_adk_tool decorator when you define tools as plain async functions. The decorated function must accept args and tool_context keyword arguments, matching the ADK run_async signature.
from controlzero import Client
from controlzero.integrations.google_adk import governed_adk_tool
cz = Client(api_key="cz_live_your_api_key_here")
@governed_adk_tool(cz, tool_name="web_search")
async def web_search(*, args, tool_context):
"""Search the web for information."""
query = args.get("query", "")
results = await do_search(query)
return {"results": results}
@governed_adk_tool(cz, tool_name="write_file")
async def write_file(*, args, tool_context):
"""Write content to a file."""
path = args.get("path")
content = args.get("content", "")
await save_file(path, content)
return {"status": "ok"}
The decorated function retains its original signature and docstring. Control Zero adds .name, .description, and .is_governed attributes to it so the ADK runner can introspect it normally.
The tool_name argument is optional. If omitted, the function name is used.
Bulk Wrapping: wrap_adk_tools
When you have a list of existing BaseTool instances, use wrap_adk_tools to wrap them all at once:
from google.adk.tools import BaseTool
from controlzero import Client
from controlzero.integrations.google_adk import wrap_adk_tools
cz = Client(api_key="cz_live_your_api_key_here")
# Wrap a list of existing ADK tool instances
governed_tools = wrap_adk_tools([search_tool, summarize_tool, write_tool], cz)
# Pass the governed tools to your agent
agent = LlmAgent(
name="ops_agent",
model="gemini-2.0-flash",
tools=governed_tools,
instruction="You are an operations assistant.",
)
wrap_adk_tools returns a list of GovernedTool instances in the same order as the input. Tools that are already GovernedTool instances are not double-wrapped.
Wrapping Individual Tools: GovernedTool
To wrap a single BaseTool instance directly:
from google.adk.tools import BaseTool
from controlzero import Client
from controlzero.integrations.google_adk import GovernedTool
cz = Client(api_key="cz_live_your_api_key_here")
class DatabaseQueryTool(BaseTool):
def __init__(self):
super().__init__(name="db_query", description="Run a read-only SQL query.")
async def run_async(self, *, args, tool_context):
sql = args.get("sql", "")
rows = await run_query(sql)
return {"rows": rows}
governed = GovernedTool(base_tool=DatabaseQueryTool(), client=cz)
GovernedTool delegates all attribute access to the underlying tool, so it behaves identically to the original from the ADK runner's perspective.
Handling Denied Actions
If a policy denies a tool call or agent run, PolicyDeniedError is raised:
from controlzero.errors import PolicyDeniedError
try:
result = await governed.run_async(invocation_context=ctx)
except PolicyDeniedError as e:
print(f"Blocked by policy: {e.decision.reason}")
# Handle gracefully: return fallback, notify user, etc.
Example Policy
{
"name": "adk-agent-policy",
"rules": [
{ "effect": "allow", "action": "tool:call", "resource": "tool/web_search" },
{ "effect": "allow", "action": "tool:call", "resource": "tool/summarize_tool" },
{ "effect": "deny", "action": "tool:call", "resource": "tool/write_file" },
{ "effect": "deny", "action": "tool:call", "resource": "tool/*" }
]
}
With this policy, web_search and summarize_tool are allowed. write_file and any other tool are denied.
Audit Logging
All governed tool calls and agent runs are written to the audit trail with the provider identified as google_adk. You can view them in the dashboard under Audit Log and filter by provider.
Each audit entry includes:
- Tool or agent name
- Policy decision (allow or deny) and reason
- Execution latency
- Run count (for agents, within the current session)
- If
log_outputs=True: a 500-character preview of the output
Python-Only
The ADK integration is available in the Python SDK only. There is no equivalent integration in the Node.js or Go SDKs, as Google ADK does not have official bindings for those languages.
Related
- Google Gemini Integration: Governance for direct Gemini API calls.
- Python SDK Reference: Full Python SDK documentation.
- API Reference: Full API documentation.