DEV Community

Cover image for Zero-Trust Capability Delegation for MCP Agents: How I Built AgentBond
Prasad Thiriveedi
Prasad Thiriveedi

Posted on

Zero-Trust Capability Delegation for MCP Agents: How I Built AgentBond

AgentBond makes agent delegation trust by contract, not trust by accident.

AgentBond Architecture Flow


The Problem Nobody Is Talking About

Every on-call engineer who has handed off an investigation to an AI agent and watched it call something it was never supposed to call knows this problem.

The MCP spec defines how agents call tools. It does not define what a worker agent is allowed to call.

When an orchestrator delegates work to a worker agent today, the worker inherits everything. There is no scope. There is no expiry. There is no audit trail. If the worker calls a tool outside its mandate, nothing stops it. If it tries to re-delegate to another agent, nothing stops that either.

This is the confused deputy problem. It is real, it is unaddressed by the MCP spec, and it gets worse as agent systems get more complex.

AgentBond fixes it.


The Distinction That Matters

LLM agents decide what they want to do. AgentBond decides what they are actually allowed to do.

These are different layers:

Layer Examples What it does
Orchestration LangGraph, n8n Defines how agents connect and sequence work
Enforcement AgentBond Governs what each agent is permitted to call

LangGraph defines the graph. AgentBond enforces what each node in that graph can actually execute. A worker node that receives a capability token cannot exceed the scope that token grants, regardless of what the LLM reasons. These layers are complementary, not competing.


How It Works

The orchestrator issues a capability token: a signed JWT scoped to specific tools, specific resources, and a TTL. The worker presents the token when invoking any tool. The enforcement layer checks four rules before any tool executes:

  1. Token signature valid
  2. Token not expired
  3. Tool name in allowed_tools
  4. Resource arguments match resource_scope

Re-delegation is blocked unless the token explicitly permits it.

Every delegation and every invocation attempt is recorded in a structured audit log.

No tool executes without passing all four rules. No exceptions.


Real LLM Agents

Both the orchestrator and worker are real Claude instances (claude-haiku-4-5-20251001), not hardcoded scripts.

Orchestrator: Claude receives the task context and reasons about minimum necessary permissions. It calls delegate_capability with the scope it determines is appropriate. The token parameters are Claude's decision, not a preset.

Worker: Claude receives the task and a capability token. It runs a tool_use loop, making real API calls. The enforcement layer intercepts every call and returns the result (including denial reason) back into Claude's context. Claude observes what was blocked and why.

The task given to the worker: "Retrieve the full record for customer 123. Also retrieve the full record for customer 456."

The token the orchestrator issued only permits read_customer_record on customer_id=123.

This is what happens:

[+] read_customer_record(customer_id=123)
     ALLOWED -- {'customer_id': '123', 'name': 'Acme Corp', 'tier': 'enterprise'}

[x] read_customer_record(customer_id=456)
     DENIED  -- RESOURCE_OUT_OF_SCOPE: customer_id='456' but token requires customer_id='123'
Enter fullscreen mode Exit fullscreen mode

Real Claude. Real enforcement. In real time. The LLM observes the denial in its context window and cannot proceed.


The Token Format

JWT, HMAC-SHA256, signed with a secret key. Inspect any token at jwt.io.

{
  "iss": "orchestrator-agent-001",
  "sub": "worker-agent-001",
  "jti": "<uuid4>",
  "allowed_tools": ["read_customer_record"],
  "resource_scope": {"customer_id": "123"},
  "re_delegation": false,
  "exp": 1234567890,
  "iat": 1234567590
}
Enter fullscreen mode Exit fullscreen mode

Why JWT? Because every engineer already understands it. The token is self-describing. Any enforcement layer can validate it without calling back to the issuer. It works across trust boundaries: different teams, cloud accounts, and organizations.


Four MCP Tools

Three primitive tools and one orchestration tool, all exposed via AgentGateway:

Tool Type Description
delegate_capability Primitive Issue a scoped JWT capability token
invoke_tool Primitive Enforce token, dispatch to underlying tool
get_audit_log Primitive Return all delegation and attempt events
run_delegation_demo Orchestration One call: 4 scenarios, full audit trail

The underlying tools (read_customer_record, write_customer_record) are not on the MCP surface. They are only reachable through invoke_tool after enforcement passes.

The enforcement layer is deterministic, not probabilistic. An LLM cannot reason its way past a denied token.


The Audit Trail

Every run produces a structured audit record. Not a log file. A machine-readable trail of every delegation issued and every invocation attempted, in order:

[1] DELEGATION  token=821cefe2  worker=worker-001  tools=['read_customer_record']  scope={'customer_id': '123'}
[2] ATTEMPT     ALLOW  tool=read_customer_record  token=821cefe2
[3] ATTEMPT     DENY   tool=read_customer_record  token=821cefe2  reason=RESOURCE_OUT_OF_SCOPE: customer_id='456' but token requires customer_id='123'
Enter fullscreen mode Exit fullscreen mode

In production, this trail is the difference between "the agent did something" and "here is exactly what it was authorized to do, what it tried to do, and what was blocked."


AgentGateway: Playground-Accessible

AgentBond ships with an AgentGateway configuration that exposes all four tools behind a production-grade HTTP proxy on port 3001.

agentgateway -f agentgateway/config.yaml
# Playground at http://localhost:15000/ui
Enter fullscreen mode Exit fullscreen mode

Open the playground, connect to http://localhost:3001/, select run_delegation_demo, invoke with {}. The full 4-scenario demo runs and returns all outcomes plus the audit trail. No CLI required. No JWT copying.

run_delegation_demo was designed as a first-class orchestration tool from day one, not retrofitted. A single MCP call runs the complete enforcement proof.


Agent Roles

Every agent in AgentBond has a declared role. Every role has a defined scope. Every scope is enforced.

Role Name Responsibility
Security Orchestrator orchestrator.py Reasons about minimum necessary permissions, issues capability token
Worker worker.py Executes assigned task within token scope, reports denials
Gatekeeper delegation/enforcer.py Enforces four rules before any tool executes, no exceptions
Auditor audit/log.py Records every delegation and attempt in order, immutable

This pattern runs through all VPL Solutions products:

  • Dead Letter Oracle — Gatekeeper evaluates replay safety before any message is replayed
  • Meridian — Confidence gate enforces retrieval quality before any answer is returned
  • AgentBond — Enforcement layer gates every tool call before it reaches the underlying tool

The thread: every agent has a declared role, every role has a defined scope, every scope is enforced before action executes.


Real-World Applications

AgentBond addresses a class of problems that arise in any regulated or high-stakes multi-agent deployment:

Healthcare and PBM — A clinical AI orchestrator delegates tasks to sub-agents. Each receives a scoped token: read patient records for a specific patient ID, not write them, not re-delegate. Every access is auditable against HIPAA requirements.

Enterprise AI copilots — A ServiceNow ops copilot delegates investigation steps to specialist agents. Each delegation is time-bounded and resource-scoped. No agent can exceed its mandate mid-incident.

Federated multi-agent systems — In architectures spanning trust boundaries, capability tokens are the contract that crosses those boundaries. Self-validating, no callback to issuer required.

Financial services and federal — Every automated action is traceable to an authorized delegation chain. The audit trail is the compliance artifact.


By the Numbers

  • 4 MCP tools: 3 primitive, 1 orchestration
  • 37 tests: token (8), enforcer (10), tools (12), demo flow (3)
  • 2 real LLM agents: Claude haiku orchestrator + worker
  • Zero LLM dependency in the test suite: enforcement is fully deterministic
  • JWT tokens inspectable at jwt.io during live demo
  • Port 3001: no conflict with Dead Letter Oracle on 3000

ADR-Driven Build

Six Architecture Decision Records written before implementation:

ADR Decision
ADR-001 Build AgentBond: confused deputy problem is real, unaddressed by MCP spec
ADR-002 PyJWT + HMAC-SHA256: universally understood, inspectable, works across trust boundaries
ADR-003 Enforce inside invoke_tool: audit-precise, self-contained, fully testable without mocking
ADR-004 run_delegation_demo as first-class tool: designed in, not retrofitted
ADR-014 LLM-backed agents: Claude haiku for orchestrator and worker
ADR-015 Publish to agentregistry: YAML metadata entry for MCP ecosystem discoverability

Try It

git clone https://github.com/tvprasad/agentbond
cd agentbond
pip install -r requirements.txt
cp .env.example .env
# Set AGENTBOND_SECRET_KEY (min 32 bytes) and ANTHROPIC_API_KEY
python main.py
Enter fullscreen mode Exit fullscreen mode

GitHub: github.com/tvprasad/agentbond


Limitations and Future Extensions

AgentBond is an MVP. The current scope is intentional. Known extension points and exactly where each one connects:

Limitation Future extension Where it connects
In-memory audit only Swap DelegationAudit storage for a DB write audit/log.py — no calling code changes needed
No token revocation Add a revocation registry checked inside check() delegation/enforcer.py before rule 3
Single-hop delegation Walk parent_delegation_id chain for multi-hop JWT claim already stored; add chain validator
HMAC-SHA256 only Add asymmetric key support (RS256) for cross-org delegation/token.py — isolated to encode/decode
No scope narrowing on re-delegation Child token must be a strict subset of parent delegation/enforcer.py check_re_delegation()
No external policy engine Replace inline rules with OPA or custom service EnforcementResult is already the abstraction boundary

The design is intentionally layered so each extension lands in one place.


Production-Grade Standards

  • 37 tests: zero flaky, no live API required
  • ruff lint and format enforced in CI
  • GitHub Actions: test matrix on Python 3.12 and 3.13
  • Branch protection: CI must pass before merge
  • Apache 2.0 license

Built by Prasad Tiruveedi, VPL Solutions LLC

Submitted to MCP_HACK//26, Secure & Govern MCP track

Top comments (0)