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[hosted]' 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 (controlzero[hosted] or controlzero[adk]). 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.