← AIP

Add Cryptographic Identity to Your Google ADK Agents

Wire AIP into a Google ADK agent hierarchy: the coordinator and every sub-agent get distinct cryptographic identities, delegation depth is enforced at the token level, and tool calls carry verifiable auth headers.

Prerequisites: Python 3.10+, google-adk installed

The problem

Your ADK agents call tools anonymously. When the coordinator routes a task to a sub-agent, no identity verification happens. The tool server has no way to confirm which agent is making the request, let alone whether that agent was authorized to do so. If a tool server is compromised, any agent can impersonate any other. There is no audit trail of who authorized what.

AIP gives each agent a self-certifying Ed25519 keypair and an AIP identifier. Every tool call carries a signed token that records the issuing agent, the permitted scope, the budget ceiling, and the delegation depth. The tool server verifies the signature before serving the request. If the token is missing, expired, or out of scope, the request is rejected.

1

Install

Install the Google ADK adapter for AIP:

pip install aip-agents[adk]

This pulls in agent-identity-protocol (core SDK) and the ADK integration layer. For other frameworks: aip-agents[crewai], aip-agents[langchain], or aip-agents[all].

2

Add AIP to your app

Create a plugin, build your agent tree as normal, wrap it in a Runner, then register. One call wires identity into the root agent and every sub-agent:

from google.adk import Agent, Runner
from aip_agents import AIPConfig
from aip_agents.adapters.adk import AIPAdkPlugin

# Create the plugin
plugin = AIPAdkPlugin(AIPConfig(app_name="my-adk-app"))

# Define the root agent
root_agent = Agent(
    name="coordinator",
    model="gemini-2.5-flash",
    instruction="Route tasks to the appropriate sub-agent.",
    description="Task coordinator",
    tools=[],
)

# Register AIP -- this is the only line you add
runner = Runner(agent=root_agent)
plugin.register(runner)

# Get auth headers for outgoing tool calls
headers = plugin.get_tool_call_headers("specialist")
print(headers)
# {"X-AIP-Token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."}
3

What just happened

When you called plugin.register(runner), AIP walked the agent tree starting from the root and:

The token flow looks like this:

Agent (coordinator)
  --> AIPAdkPlugin.get_tool_call_headers("coordinator")
      --> returns {"X-AIP-Token": "<signed-jwt>"}
          --> outgoing request to MCP server
              --> server verifies signature + scope
              --> serves request or returns 401

You can inspect the delegation depth for any agent by name:

depth = plugin.get_chain_depth("coordinator")
print(f"coordinator depth: {depth}")
# coordinator depth: 0  (root has no parent)
4

Multi-agent delegation

ADK's sub_agents tree maps naturally onto AIP delegation chains. The coordinator gets depth 0; each sub-agent gets depth 1; their sub-agents get depth 2; and so on. Scope attenuates at each hop -- a sub-agent can only exercise a subset of the permissions the coordinator was granted:

from google.adk import Agent, Runner
from aip_agents import AIPConfig
from aip_agents.adapters.adk import AIPAdkPlugin

plugin = AIPAdkPlugin(AIPConfig(
    app_name="my-adk-app",
    log_tokens=True,  # prints identity and delegation events
))

# Sub-agents
summarizer = Agent(
    name="summarizer",
    model="gemini-2.5-flash",
    instruction="Summarize the research findings concisely.",
    description="Summarization specialist",
    tools=[],
)

fact_checker = Agent(
    name="fact_checker",
    model="gemini-2.5-flash",
    instruction="Verify claims against sources.",
    description="Fact verification agent",
    tools=[],
)

# Root agent with sub-agent tree
coordinator = Agent(
    name="coordinator",
    model="gemini-2.5-flash",
    instruction="Route tasks to the appropriate sub-agent.",
    description="Task coordinator",
    tools=[],
    sub_agents=[summarizer, fact_checker],
)

runner = Runner(agent=coordinator)
plugin.register(runner)

# Inspect the delegation chain depths
for name in ["coordinator", "summarizer", "fact_checker"]:
    depth = plugin.get_chain_depth(name)
    print(f"{name}: depth {depth}")
# coordinator:  depth 0
# summarizer:   depth 1
# fact_checker: depth 1

Set max_depth in AIPConfig to enforce a ceiling on delegation depth. Any token with a depth exceeding the limit is rejected by the verifier, preventing runaway delegation chains.

Try to break it

Understanding what AIP rejects is as important as what it accepts.

Tamper with the token:

from aip_token.compact import CompactToken

token = plugin.get_tool_call_headers("summarizer")["X-AIP-Token"]
tampered = token[:-5] + "XXXXX"

# On the server side:
# CompactToken.verify(tampered, public_key_bytes) raises:
#   SignatureError: signature verification failed

Use a token outside its scope:

# summarizer token is scoped to ["summarize", "fetch"]
# Trying to call a tool outside that scope:
#
# Server checks: "tool:email" not in token.scope
# Server returns: 401 Unauthorized -- scope mismatch

Exceed the delegation depth ceiling:

from aip_agents import AIPConfig
from aip_agents.adapters.adk import AIPAdkPlugin

plugin = AIPAdkPlugin(AIPConfig(
    app_name="my-adk-app",
    max_depth=1,  # allow coordinator -> sub-agent, nothing deeper
))

# Any attempt to create a depth-2 delegation is rejected:
#   DelegationError: max_depth exceeded (got 2, limit 1)

Next steps