Agent Governance Toolkit Integration
The Agent Governance Toolkit controls what agents can do. AGLedger records what they committed to, what they delivered, and whether the two matched. Together they provide governance (authorization) and accountability (audit trail) in one pipeline.
Without AGLedger, the governance toolkit answers: "Was this agent allowed to make this transfer?" With AGLedger, you also answer: "Was this transfer committed to under a mandate? Did the agent document what it did? Does the evidence match the commitment? Is the audit trail tamper-evident?"
The integration is lightweight. You write a policy configuration, wire it through a thin adapter, and the toolkit's denial response teaches agents how to create accountability mandates. The agent self-corrects instead of failing.
No system prompt changes required. The agent's instructions remain exactly what they were. The gate directive and AGLedger tool descriptions do the teaching.
Prerequisites
- AGLedger instance deployed (Docker Compose or Helm) — see Installation
- Agent Governance Toolkit installed:
pip install agent-governance-toolkit[full] - Agent has access to AGLedger tools (CLI, MCP server, or native HTTP)
Step 1: Write a policy configuration
Define which tools require accountability mandates. This uses the AGLedger policy format — a thin layer that the adapter (Step 2) translates into toolkit policy rules:
# policies/finance-agent.yaml
#
# AGLedger policy configuration.
# This is NOT the toolkit's native YAML format — the adapter in Step 2
# reads this file and registers the rules with the toolkit's PolicyEvaluator.
version: "1.0"
defaultAction: allow
agledger:
apiUrl: https://your-agledger-instance.com
agentKey: ${AGENT_KEY}
rules:
- name: funds-transfer-mandate
tools:
- transfer_funds
- wire_payment
- process_refund
require: mandateId
contractType: ACH-TXN-v1
validate: true
directive: >-
REJECTED: This action requires an accountability mandate.
Create a mandate of type ACH-TXN-v1 using the accountability
API at https://your-agledger-instance.com.
Include the mandate ID when retrying this action.
- name: purchase-order-mandate
tools:
- create_purchase_order
- approve_vendor
require: mandateId
contractType: ACH-PROC-v1
validate: true
directive: >-
REJECTED: This action requires an accountability mandate.
Create a mandate of type ACH-PROC-v1 using the accountability
API at https://your-agledger-instance.com.
Include the mandate ID when retrying this action.
Each rule specifies:
- tools — which tool calls require a mandate
- require — the parameter name the agent must provide (always
mandateId) - contractType — the AGLedger contract type for this category of work
- validate — whether to verify the mandate ID against the AGLedger API (always
truein production — see "Why server-side validation is required" in Step 3) - directive — the error-as-directive message returned to the agent when the mandate is missing
Step 2: Wire the adapter
The adapter reads your policy configuration, registers rules with the toolkit's PolicyEvaluator, and enriches denial responses with the AGLedger directive.
Build the policy from your YAML config:
from agent_os.policies import (
PolicyEvaluator, PolicyDocument, PolicyRule, PolicyCondition,
PolicyAction, PolicyOperator, PolicyDefaults
)
# Build policy from our YAML config
rules = []
for rule_config in policy_config["rules"]:
for tool_name in rule_config["tools"]:
rules.append(PolicyRule(
name=f"require-mandate-{tool_name}",
condition=PolicyCondition(
field="tool_name",
operator=PolicyOperator.EQ,
value=tool_name,
),
action=PolicyAction.DENY,
priority=100,
message=rule_config["directive"],
))
evaluator = PolicyEvaluator(policies=[PolicyDocument(
name="agledger-accountability",
version="1.0",
defaults=PolicyDefaults(action=PolicyAction.ALLOW),
rules=rules,
)])
Enrich denials with AGLedger directives:
The toolkit's evaluator.evaluate(...) returns a PolicyDecision with allowed, matched_rule, action, and reason fields. The adapter maps denied tool names to your directive messages:
# Build directive lookup from policy config
DIRECTIVES = {}
for rule in policy_config["rules"]:
for tool_name in rule["tools"]:
DIRECTIVES[tool_name] = rule["directive"]
def evaluate_with_directive(tool_name: str, args: dict) -> dict:
# Check if mandate ID is present and valid
mandate_id = args.get("mandateId")
if mandate_id:
# Validate against AGLedger API (see Step 3)
is_valid, error = validate_mandate(mandate_id, api_url, api_key)
if is_valid:
return {"allowed": True}
return {"allowed": False, "directive": error}
# No mandate ID — check if this tool requires one
decision = evaluator.evaluate({"tool_name": tool_name})
if not decision.allowed:
directive = DIRECTIVES.get(tool_name)
if directive:
return {"allowed": False, "directive": directive}
return {"allowed": False, "reason": decision.reason}
return {"allowed": True}
Note: we match on tool_name rather than parsing the decision reason. The toolkit's internal messages may change between versions — matching on tool name is stable.
Step 3: Enable server-side validation
This is critical. Agents will fabricate mandate IDs to satisfy parameter requirements. In our testing, an agent passed "IRS-EIN-000000000" (pulled from another tool's response) as a mandate ID. A presence check alone is not enough.
The adapter must validate the mandate ID against the AGLedger API before allowing the tool call:
import requests
def validate_mandate(mandate_id: str, api_url: str, api_key: str) -> tuple[bool, str | None]:
"""
Validate a mandate ID against the AGLedger API.
Returns (is_valid, error_message_if_invalid).
"""
try:
r = requests.get(
f"{api_url}/v1/mandates/{mandate_id}",
headers={"Authorization": f"Bearer {api_key}"},
timeout=5,
)
if r.status_code == 404:
return False, f"Mandate {mandate_id} not found. Create a real mandate first."
if r.status_code != 200:
return False, f"Could not verify mandate (HTTP {r.status_code})."
data = r.json()
# Only allow tool calls against mandates in actionable states.
# FULFILLED means the work is already done — a new action needs a new mandate.
valid_states = ("CREATED", "ACTIVE")
if data.get("status") not in valid_states:
return False, (
f"Mandate is in state {data['status']}. "
f"Only mandates in {', '.join(valid_states)} states are valid for new actions."
)
return True, None
except requests.Timeout:
# Fail closed: block the action if we can't verify.
# Change to return (True, None) for fail-open behavior.
return False, "Could not reach AGLedger API (timeout). Retry in a moment."
except Exception as e:
return False, f"Mandate validation failed: {e}"
Fail-open vs fail-closed: The example above fails closed (blocks the agent when AGLedger is unreachable). This is safer — an agent cannot bypass accountability by waiting for an outage. For high-availability deployments where blocking is unacceptable, change the timeout handler to return (True, None) and log the validation skip for later audit.
Step 4: Make AGLedger tools available to the agent
The agent needs AGLedger tools alongside its domain tools. Three options, each with different characteristics:
CLI (@agledger/cli):
The agent gets a run_agledger tool that executes CLI commands. Lowest token cost per successful run in our testing. Simpler setup (npm install + env vars). The agent discovers commands via list-commands. See the CLI guide.
MCP Server (@agledger/mcp-server):
The agent gets agledger_discover and agledger_api tools. Full API response bodies come through as tool results, including nextSteps guidance on mutation responses. Higher token cost (full JSON responses) but richer context for the agent. See the MCP Server guide.
Native API (agent's existing HTTP tool):
No additional packages. The agent uses its existing http_request tool to call the AGLedger API directly. Point it to /llms-agent.txt (1.4KB quick reference) or /llms.txt (11KB full reference) for API documentation.
Choose based on your deployment: CLI for simplicity, MCP for richer guidance, native API for environments where installing additional tools is not possible.
Step 5: Optimize tool descriptions
The tool descriptions are the agent's only AGLedger guidance — they replace what would otherwise go in the system prompt. Default descriptions work, but optimized versions measurably improve success rates.
CLI tool description (optimized):
Run an agledger CLI command. AGLedger tracks accountability for your work.
Workflow:
1. "schema list" - see available contract types
2. "schema get <type>" - see required fields and examples
3. "mandate create --type <type> --data '{"criteria":{...}}'" - create a mandate
4. "receipt submit <mandateId> --data '{"evidence":{...}}'" - submit evidence
Run "list-commands" for the full command reference.
MCP agledger_api description (optimized):
Make any AGLedger API call. All paths start with /v1/. Workflow:
1. GET /v1/schemas - list contract types
2. GET /v1/schemas/{type} - get required fields and examples
3. POST /v1/mandates - create a mandate
4. POST /v1/mandates/{id}/receipts - submit evidence when done
For full docs: GET /llms-agent.txt
In our testing, these optimized descriptions reduced token usage by 64% in one MCP scenario (12,122 to 4,359 tokens) and converted multiple failure cases to successful completions. The key changes: include the /v1/ prefix in path examples, show the 4-step workflow, and reference /llms-agent.txt for full documentation.
What the agent experiences
The agent's journey through a policy-gated tool call:
Agent: "I need to transfer $45,000 to the IRS."
-> Calls transfer_funds(amount=45000, toPayee="IRS")
-> Adapter: no mandateId -> returns directive
Agent reads: "REJECTED: Create a mandate of type ACH-TXN-v1..."
-> Looks at available tools, finds AGLedger CLI or MCP
-> Calls schema get ACH-TXN-v1 (learns required fields)
-> Calls mandate create with proper criteria
-> Gets mandate ID: 019d-xxxx-xxxx
Agent: "Now I have a mandate. Let me retry."
-> Calls transfer_funds(amount=45000, toPayee="IRS", mandateId="019d-xxxx-xxxx")
-> Adapter: mandateId present -> validates against AGLedger API -> passes
-> Transfer executes
Agent: "Transfer complete. Let me document it."
-> Calls receipt submit with transfer evidence
-> AGLedger verifies evidence against mandate criteria
-> Status: FULFILLED
The agent adapted to the compliance requirement without any prior knowledge of AGLedger. The gate directive was the teaching moment. The tool descriptions provided the method. The API responses provided the specifics.
Note: receipt submission is the agent's responsibility, guided by the AGLedger tool descriptions and API nextSteps responses. The gate enforces mandate creation (the commitment), not receipt submission (the evidence). For stricter enforcement, you could add a post-action gate that blocks subsequent tools until the receipt is submitted — but in our testing, agents with optimized tool descriptions submitted receipts voluntarily.
Provider considerations
AGLedger is vendor-agnostic — any LLM that can call tools can work with the accountability gate pattern. In our testing (70 traces total), provider behavior varied:
- Some models read error directives carefully and recover in 1-2 retries
- Others require more attempts or stronger tool descriptions to complete the full lifecycle
- All providers benefit from server-side mandate validation (Step 3) — never rely on presence checks alone
Test your specific provider and model version with your contract types. The blog post includes detailed per-provider behavioral data from our 70-trace study.
Architecture
Your Agent Framework
(LangChain / CrewAI / Vercel AI SDK / etc.)
|
v
+-------------------------------+
| Agent Governance Toolkit |
| (PolicyEvaluator) |
| |
| Policy rules -> evaluate() |
+-------------------------------+
|
+---------+---------+
| |
Allowed Denied
| |
v v
+------------------+ +-------------------+
| AGLedger Adapter | | AGLedger Adapter |
| (custom wrapper) | | (custom wrapper) |
| | | |
| Validate mandate | | Return directive |
| ID against API | | with contract type |
| (GET /v1/ | | and API URL |
| mandates/{id}) | | |
+--------+---------+ +---------+---------+
| |
Passes Agent creates
| mandate via
Tool executes AGLedger tools,
| then retries
Agent submits |
receipt via (loops back to
AGLedger tools tool call)
|
Verification
|
FULFILLED
The governance toolkit handles authorization (native). The AGLedger adapter handles accountability (custom wrapper). The agent handles compliance automatically through self-correction.
Version compatibility
This guide was tested against:
- Agent Governance Toolkit:
agent-governance-toolkit[full](tested against v3.1.0 ofagent-os-kernel) - AGLedger API: v0.19.6
- AGLedger CLI:
@agledger/cliv0.4.1 - AGLedger MCP Server:
@agledger/mcp-serverv2.0.2
The adapter depends on PolicyEvaluator, PolicyDocument, PolicyRule, PolicyCondition, PolicyAction, PolicyOperator, and PolicyDefaults from the agent_os.policies package. These are public APIs in the toolkit. Pin your version and test after upgrades — the toolkit is in public preview and breaking changes may occur before GA.