Aira
Integrations

Google ADK

Gate Google Agent Development Kit tool calls through Aira's policy engine via before_tool_call / after_tool_call plugin hooks.

Kind: gate · Pre-execution gate: yes · Peer dep: Google ADK

What this integration actually does

Google ADK's plugin system provides before_tool_call and after_tool_call hooks. before_tool_call runs BEFORE the tool executes and can raise to abort the call, so this is a real pre-execution gate.

AiraPlugin implements the plugin contract:

  • before_tool_callaira.authorize(). Raises AiraToolDenied if the policy engine denies or holds the action. The tool never runs.
  • after_tool_callaira.notarize(outcome="completed").
  • on_tool_erroraira.notarize(outcome="failed").

Install

pip install aira-sdk[google-adk]

Full example — gated tool call

gated-adk-agent.py
from aira import Aira
from aira.extras.google_adk import AiraPlugin, AiraToolDenied

aira = Aira(api_key="aira_live_...")
plugin = AiraPlugin(
    client=aira,
    agent_id="adk-agent",
    model_id="gemini-2.0-flash",
)

def search_documents(query: str) -> list[dict]:
    """Real tool body — side effects happen here."""
    # your search code
    return [{"title": "...", "snippet": "..."}]

# Manual invocation with before/after hooks — works with any ADK runner
try:
    plugin.before_tool_call("search_documents", args={"query": "confidential Q4 forecast"})
    try:
        result = search_documents(query="confidential Q4 forecast")
    except Exception as e:
        plugin.on_tool_error("search_documents", e)
        raise
    plugin.after_tool_call("search_documents", result=result)
except AiraToolDenied as e:
    # Policy engine blocked — handle or surface to the agent
    print(f"Blocked: {e.code}{e.message}")

Integrating with ADK's plugin registry

If you use Google ADK's plugin loader, register AiraPlugin as a before/after-tool-call listener:

from google.adk import Agent
from aira.extras.google_adk import AiraPlugin

aira_plugin = AiraPlugin(client=aira, agent_id="adk-agent")

agent = Agent(
    name="my-agent",
    plugins=[
        aira_plugin,  # ADK will call before_tool_call / after_tool_call on it
    ],
)

What happens when a policy denies

  1. The agent decides to call search_documents.
  2. ADK invokes before_tool_call("search_documents", args={"query": "..."}).
  3. The plugin calls aira.authorize(action_type="tool_invoked", ...).
  4. Policy engine runs. If denied, the plugin raises AiraToolDenied.
  5. ADK propagates the exception as a tool error. The real tool body never runs.

Known limits

  • args dict keys only. The plugin uses sorted(args.keys()) in the details string rather than the actual argument values. This keeps authorize() latency low and avoids sending potentially sensitive tool arguments to Aira's details column. If you want to gate on argument values (e.g. "deny if query contains 'confidential'"), use a content_scan policy or build a custom details string.
  • Thread safety. The plugin uses an in-memory tool_name → action_uuid map keyed by tool name. If the same tool is called concurrently in the same plugin instance, the second call will overwrite the first before after_tool_call runs. For multi-threaded agents, create one plugin instance per thread or switch to keying on a run id if ADK exposes one.
  • Notarize is non-blocking. Notarize failures log a warning but don't crash the agent.

When to use this vs inline authorize()

Use the plugin when...Call authorize() inline when...
You're building a standard ADK agent with the plugin systemYou have custom business logic per tool branch
You want blanket gating on every toolYou need argument values in the details string
Thread safety isn't a concern (single-threaded runners)You're running multiple concurrent tool calls in the same plugin instance

Proof it works

On this page