Architecture
The roles, JSON-RPC protocol model, versioning rules, and message envelope that every AHP implementation shares.
AHP is a small control-plane protocol that sits in an agent harness’s runtime path. The harness is the AHP client; the external policy, security, approval, or middleware component is the AHP server, called a backend.
Roles and terminology
| Term | Definition |
|---|---|
| Harness | The agent runtime that is about to perform an operation. The AHP client. |
| Backend | The external policy, security, approval, or middleware component receiving AHP messages. The AHP server. |
| Interceptor | A backend subscription using intercept mode. The harness waits for its response. |
| Observer | A backend subscription using observe mode. The harness does not wait for a decision. |
| Operation | The harness action represented by an event, such as a tool call. |
| Effect | A semantic instruction returned by an interceptor, such as deny. |
| Operational failure | A timeout, transport error, process failure, JSON-RPC error, malformed message, or invalid effect. |
| Explicit denial | A valid deny effect returned by a backend. Not an operational failure. |
JSON-RPC model
AHP uses UTF-8 encoded JSON-RPC 2.0 and defines two methods in v0.1:
| Method | JSON-RPC type | Profile | Purpose |
|---|---|---|---|
hooks/intercept |
Request | Tool Interception | Ask a backend for effects before continuing an operation. |
hooks/observe |
Notification | Lifecycle Observation | Deliver a one-way lifecycle event. |
Batch JSON-RPC messages are not used in v0.1. A notification has no JSON-RPC id and
never receives a response.
The intercept exchange
A hooks/intercept request carries the protocol version, the event, and the capabilities
the harness can enforce for this event. The JSON-RPC request id equals the event id,
and a retry of the same event reuses both.
{
"jsonrpc": "2.0",
"id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
"method": "hooks/intercept",
"params": {
"protocolVersion": "0.1",
"event": {
"id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
"source": "urn:uuid:5b7de29e-a9e0-41a8-bf26-d94b05f0656d",
"type": "tool.before",
"time": "2026-08-24T08:51:14Z",
"session": { "id": "sess_123", "cwd": "/repo" },
"tool": {
"callId": "call_456",
"name": "Bash",
"kind": "shell",
"input": { "command": "git push --force" }
}
},
"capabilities": { "effects": ["deny"] }
}
}
A successful result contains protocolVersion and an effects array — either empty (this
interceptor requests no change) or containing exactly one valid deny effect:
{
"jsonrpc": "2.0",
"id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
"result": {
"protocolVersion": "0.1",
"effects": [
{
"type": "deny",
"reason": "Force pushes are prohibited by organization policy.",
"code": "com.example.policy.force_push"
}
]
}
}
Versioning
Every AHP params object carries protocolVersion with the exact value 0.1. A backend
that does not support the supplied version returns the JSON-RPC error
unsupported_protocol_version. Because process-per-event backends cannot rely on a prior
handshake, version and capabilities travel on each intercept request.
Unknown fields
Receivers ignore unknown fields in otherwise valid objects, and senders must not use unknown fields to alter core semantics. Unknown event types, effect types, and enum values are different: they are unsupported protocol semantics, not ordinary unknown fields.
Fields described as JSON objects contain objects, not encoded JSON strings.
Design lineage
The Working Draft adopts principles from adjacent systems: Kubernetes admission webhooks (distinguish rejection from webhook failure; explicit failure behavior), CloudEvents (stable event identity and typed events), OpenFeature hooks (normative hook order and timing), MCP transports (newline-delimited JSON-RPC over stdio with clean stdout), and ACP (requests for decisions, notifications for one-way events).