Skip to main content

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

ParameterTypeDefaultDescription
agentBaseAgentrequiredThe ADK agent to wrap.
clientClientrequiredControlZero client.
agent_namestragent.nameName used for policy lookups and audit log entries. Defaults to the agent's own name.
wrap_toolsboolTrueAutomatically wrap all tools in agent.tools with governance.
allowed_toolslist[str]NoneOptional allowlist of tool names. Tools not in this list are removed from the agent before wrapping.
log_outputsboolFalseInclude 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.