Agent Identity Protocol gives every AI agent a verifiable identity, cryptographically-scoped authority, and an unforgeable audit trail across MCP and A2A.
Today's agent ecosystem has no standard way to answer the most basic security question: who is this agent, and what is it allowed to do?
A survey of the MCP ecosystem found over 2,000 publicly accessible servers, none of which implement authentication or identity verification. An attacker can impersonate any server or agent, and no existing mechanism can trace which agent invoked which tool on whose behalf. The Agent Identity Protocol closes this gap.
AIP introduces three simple but powerful primitives built on established cryptographic standards.
Every agent gets a globally unique, cryptographically verifiable Decentralized Identifier (DID). Like a passport, it proves who the agent is to any service it talks to - no central registry required.
An agent can authorize another agent to act on its behalf, but with strict limits: a budget, an expiration, a specific toolset. Attenuation ensures delegated power cannot be misused - even by accident.
Every delegation and invocation is recorded in an append-only provenance chain. You can trace exactly who authorized what, producing a complete audit trail from the original request to every sub-task.
Watch how tokens grow, compare wire formats, and follow the verification process step by step.
The token grows with each delegation. Each hop appends identity, capability, and provenance - append-only, unforgeable.
One flat token. Best for single-hop MCP calls where no delegation chain exists.
Each hop appends a block. The token carries the full provenance tree.
AIP combines identity, capability-based authorization, and provenance into a single, extensible protocol.
IBCTs are the core cryptographic primitive of AIP. They fuse three concerns into one atomic token: identity (who issued it), authorization (what the bearer can do), and provenance (the chain of delegations that produced it). Each IBCT binds a capability directly to a specific invocation context, preventing token reuse across different operations.
# Create an IBCT for a sub-agent
token = IBCT.create(
issuer=agent_a.did,
subject=agent_b.did,
capabilities={
"tool:search": {"max_results": 10},
"tool:read": {"paths": ["docs/*"]},
},
constraints={
"exp": int(time()) + 3600,
"max_depth": 2,
},
parent=parent_token,
)
# token now carries: identity,
# attenuation, and provenanceSingle-hop MCP calls
Signed JSON Web Tokens (JWTs) for direct, single-hop invocations. The entire IBCT - identity, capability, and parent reference - is encoded as standard JWT claims.
{
"iss": "did:aip:alice-agent",
"cap": {"read": ["reports/"]},
"exp": 1719000000
}
[SIGNED WITH ES256K]Multi-hop delegation
Biscuit tokens with embedded Datalog policies for multi-hop delegation chains. Each hop appends a new block with attenuated capabilities.
// Block 0
right("read", path) <-
resource(path), prefix(path, "reports/");
// Block 1
right("read", path) <-
right("read", path),
check(limit(path, 5MB));In chained mode, AIP uses Datalog - a declarative logic programming language - to express and verify access policies. Each delegation block adds rules or checks that the token must satisfy.
// Policy: budget check
allow if
budget_remaining(B),
B >= cost(C),
cost(C);
// Policy: time window
allow if
time(T),
T >= 2025-03-20T00:00:00Z,
T <= 2025-03-21T00:00:00Z;
// Attenuation rule
right(tool, path) <-
parent_right(tool, path),
allowed_scope(tool, path);See how AIP protects a database tool, enables multi-agent travel booking, and how to build tokens from scratch.
The read_database tool is one of the most sensitive endpoints in any MCP server. With AIP, you add a middleware layer that verifies every invocation against a Datalog policy before the tool handler is ever reached.
from aip import AIPMiddleware, IBCTVerifier
server = MCPServer()
@server.tool("read_database")
def read_db(query: str):
# Only reached if AIP verified:
# 1. Token signed by authorized issuer
# 2. Datalog policy: resource="db_1",
# operation="select", time < cutoff
# 3. Attenuation constraints met
return db.execute(query)
# Wrap with AIP middleware
server.use(AIPMiddleware(
verifier=IBCTVerifier(),
resolver=DIDResolver(),
policy_file="policies/db.datalog",
))"Book a hotel in Paris under $200/night, June 15-20."
# User invokes Travel Coordinator
result = travel_agent.invoke(
task="book_hotel",
params={
"city": "Paris",
"max_price": 200,
"check_in": "2026-06-15",
"check_out": "2026-06-20",
},
)Travel Coordinator delegates to Hotel Booker with an attenuated IBCT.
# Travel Coordinator delegates
delegation = agent.delegate(
target=hotel_booker.did,
task="search_and_book",
constraints={
"max_price": 200,
"city": "Paris",
"dates": ["2026-06-15", "2026-06-20"],
"max_depth": 1,
},
)
# Hotel Booker calls hotel API
result = hotel_booker.call_tool(
server=hotel_api_mcp,
tool="search",
params={"city": "Paris"},
ibct=delegation.token,
)Hotel API verifies the token, returns results. Provenance recorded.
# Token verified, provenance
# chain recorded:
# User -> Travel Coordinator
# -> Hotel Booker -> Hotel API
print(result)
# Hotel: Le Marais Inn
# Price: $185/night
# Dates: June 15-20
# Provenance:
# 3 hops, all verifiedfrom aip import BiscuitBuilder, check
# Initialize the builder
builder = BiscuitBuilder("ed25519")
# Add a Datalog check
builder.add_check(
check(
'resource == "db_1"',
'operation == "select"',
'time < 2026-07-01T00:00:00Z',
)
)
# Add attenuation rules
builder.add_right(
'right(op, res) <- '
'parent_right(op, res), '
'allowed_scope(op, res)'
)
# Sign and export the token
token = builder.sign(
secret_key=issuer_key,
expiry=3600,
)
print(f"Token: {token.to_base64()}")
# Token now carries:
# identity, policy, signatureuse aip::biscuit::BiscuitBuilder;
let mut builder = BiscuitBuilder::new();
// Add a Datalog check
builder.add_check(
check!(r#"resource == "db_1""#,
r#"operation == "select""#,
r#"time < 2026-07-01T00:00:00Z"#)
);
// Attenuation rule
builder.add_right(
r#"right(op, res) <-
parent_right(op, res),
allowed_scope(op, res)"#,
);
// Sign
let token = builder
.sign(&issuer_key)
.with_expiry(3600)
.build()
.await?;
println!("Token: {}", token.to_base64());
// Verify on receiving side
let data = token.verify(&issuer_pub).await?;
assert!(data.operations.contains("select"));AIP sits as a middleware layer. Drop it into any MCP server or A2A agent runtime without changing your business logic.
Add the AIP middleware to your existing MCP server. Every incoming tool call is verified against the caller's IBCT before reaching your handler. No code changes to your tool implementations.
from aip import AIPMiddleware
# Wrap your MCP server
server = MCPServer(tools=[search, read, write])
server.use(AIPMiddleware(
verifier=IBCTVerifier(),
resolver=DIDResolver(),
))
# Every call is now verified:
# 1. Extract IBCT from request header
# 2. Verify signature against issuer DID
# 3. Evaluate Datalog policy chain
# 4. Check attenuation constraints
# 5. Record provenance entry
# 6. Route to tool handlerAgent A receives a task, identifies it needs help, and delegates a sub-task to Agent B. Agent B gets an attenuated IBCT that limits what it can do, for how long, and on what data. The delegation chain is provable end-to-end.
# Agent A delegates to Agent B
delegation = agent_a.delegate(
target=agent_b.did,
task="summarize:reports/Q4.md",
constraints={
"max_tokens": 2000,
"expires_in": 300,
},
)
# Agent B invokes with its token
result = agent_b.call_tool(
server=reports_mcp,
tool="summarize",
params={"path": "Q4.md"},
ibct=delegation.token,
) # Verified end-to-endAIP is an open protocol. Read the paper, explore the reference implementation, and join the working group.