ArmorIQ LogoArmorIQ SDK
Integrations

Google ADK

Add ArmorIQ to a Google ADK agent

Google ADK Integration

ArmorIQADK wraps Google's Agent Development Kit so every tool call produced by an ADK agent goes through ArmorIQ's plan / token / policy / audit pipeline. You keep your existing LlmAgent, McpToolset, and runner wiring — you just install the ArmorIQ scope on the agent for the duration of each request.

Python only today. The TypeScript ADK adapter is coming soon. Every other framework adapter (CrewAI, LangChain, OpenAI, Anthropic) is also coming soon on both languages — see Integrations for the full status matrix.

Install

pip install "armoriq-sdk[google-adk]"

Minimal example

from armoriq_sdk.integrations.google_adk import ArmorIQADK

# Once per process
armoriq = ArmorIQADK(api_key=os.environ["ARMORIQ_API_KEY"])

# Per-request
scope = armoriq.for_user("alice@example.com", goal=user_message)
scope.install(root_agent)
try:
    async for event in runner.run_async(...):
        handle(event)
finally:
    scope.uninstall(root_agent)

That's it. The SDK installs three ADK lifecycle callbacks on your agent:

CallbackWhat ArmorIQ does
after_model_callbackBuilds a plan from the LLM's tool calls and mints an intent token
before_tool_callbackEnforces the user's policy — allow, hold (wait for approval), or block
after_tool_callbackAudits the call with the result

Full example: an ADK agent with GitHub + Stripe MCPs

This mirrors the sdk-adk-test-agent-py reference app.

import os
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
from google.adk.agents import LlmAgent
from google.adk.runners import InMemoryRunner
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset, StdioConnectionParams
from google.genai import types as genai_types
from mcp import StdioServerParameters

from armoriq_sdk.integrations.google_adk import ArmorIQADK

load_dotenv()

# ── Once per process ────────────────────────────────────────────────
armoriq = ArmorIQADK(api_key=os.environ["ARMORIQ_API_KEY"])

root_agent = LlmAgent(
    model=os.environ.get("ADK_MODEL", "gemini-2.5-flash"),
    name="demo_agent",
    description="Agent with GitHub + Stripe tools under ArmorIQ policy.",
    instruction="Use the tool that matches the user's request.",
    tools=[
        McpToolset(connection_params=StdioConnectionParams(
            server_params=StdioServerParameters(
                command="npx",
                args=["-y", "@modelcontextprotocol/server-github"],
                env={"GITHUB_PERSONAL_ACCESS_TOKEN": os.environ["GITHUB_TOKEN"]},
            ),
        )),
        McpToolset(connection_params=StdioConnectionParams(
            server_params=StdioServerParameters(
                command="npx",
                args=["-y", "@stripe/mcp", f"--api-key={os.environ['STRIPE_API_KEY']}"],
            ),
        )),
    ],
)

# ── Per request ─────────────────────────────────────────────────────
app = FastAPI()

@app.post("/chat/stream")
async def chat_stream(req: Request):
    body = await req.json()
    message = body["message"]
    user_email = body["userEmail"]

    async def events():
        scope = armoriq.for_user(user_email, goal=message)
        scope.install(root_agent)
        runner = InMemoryRunner(agent=root_agent, app_name="demo")
        try:
            session = await runner.session_service.create_session(
                app_name=runner.app_name, user_id=user_email
            )
            async for event in runner.run_async(
                user_id=user_email,
                session_id=session.id,
                new_message=genai_types.Content(
                    role="user", parts=[genai_types.Part(text=message)],
                ),
            ):
                yield f"data: {event}\n\n"
        finally:
            scope.uninstall(root_agent)

    return StreamingResponse(events(), media_type="text/event-stream")

What you need to add to an existing agent

If you already have an ADK agent, the diff is four lines:

+ from armoriq_sdk.integrations.google_adk import ArmorIQADK
+ armoriq = ArmorIQADK(api_key=os.environ["ARMORIQ_API_KEY"])

  async def handle_chat(user_email: str, message: str):
+     scope = armoriq.for_user(user_email, goal=message)
+     scope.install(root_agent)
      try:
          async for event in runner.run_async(...):
              ...
+     finally:
+         scope.uninstall(root_agent)

No changes to your tools, no changes to your LlmAgent definition, no plan-building by hand.

ArmorIQADK parameters

ParameterTypeDefaultDescription
api_keystrYour ArmorIQ API key
backend_endpointstrproductionOverride the control-plane URL
iap_endpointstrproductionOverride the IAP URL
proxy_endpointstrproductionOverride the proxy URL
use_productionboolTrueSet False for local dev endpoints
default_mcp_namestrNoneMCP name to attribute tools that can't be auto-mapped
tool_name_parsercallablebuilt-inCustom tool_name -> (mcp, action) mapper
validity_secondsint300Intent-token validity window
modestr"sdk"Session mode (SDK / server / local)
llmstr"agent"Label recorded in the audit trail

for_user(email, *, goal=None)

Returns a per-request bundle with three methods:

MethodDescription
scope.install(agent)Attach the three ADK callbacks to the agent
scope.uninstall(agent)Detach them — always run this in finally
scope.sessionThe underlying ArmorIQSession (for advanced use)

The bundle caches plan state for the request. Tool calls that aren't in the plan get blocked before they hit the MCP.

Testing

  • Use use_production=False + BACKEND_ENDPOINT=http://localhost:3000 to point at a local control plane.
  • Set DEFAULT_USER_EMAIL in your test harness so test runs don't hit your real users' policies.
  • Bootstrap once (armoriq.bootstrap()) and reuse — it's cached.

Reference implementation

Full source: sdk-adk-test-agent-py — a FastAPI + ADK + GitHub/Stripe MCP reference agent wired for ArmorIQ.

On this page