Documentation menu

Messages and events

Common message envelope

Intercept request

{
  "jsonrpc": "2.0",
  "id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
  "method": "hooks/intercept",
  "params": {
    "protocolVersion": "draft",
    "event": {
      "id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
      "source": "urn:uuid:5b7de29e-a9e0-41a8-bf26-d94b05f0656d",
      "harness": {"name": "example-harness", "version": "1.0.0"},
      "type": "tool.before",
      "time": "2026-08-24T08:51:14Z",
      "session": {
        "id": "sess_123",
        "cwd": "/repo",
        "workspaceRoots": [
          "/repo"
        ]
      },
      "tool": {
        "name": "Bash",
        "kind": "shell",
        "input": {
          "command": "git push --force"
        },
        "origin": "native"
      },
      "call": {
        "id": "call_456"
      },
      "path": "native"
    },
    "capabilities": {
      "effects": [
        "deny"
      ]
    }
  }
}

Subscriptions are harness-local configuration and are not transmitted as wire identity. JSON-RPC request IDs correlate responses with pending requests; they are not authorization claims. The JSON-RPC request id MUST equal params.event.id. A retry of the same event MUST reuse both values.

Intercept response with no effect

{
  "jsonrpc": "2.0",
  "id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
  "result": {
    "protocolVersion": "draft",
    "effects": []
  }
}

An omitted result.effects or an empty effect list means only that this interceptor requests no change. Missing effects default to an empty list for composition; their wire presence is not rewritten. Neither form grants permission, overrides another interceptor, or bypasses the harness’s permission system.

Known effect values, pending candidates, injection state, and optional host-supplied session counters follow the typed payload contracts.

Intercept response denying the operation

{
  "jsonrpc": "2.0",
  "id": "evt_01JQ8Z2Y6YR0H8N7Q2M3X4V5W6",
  "result": {
    "protocolVersion": "draft",
    "effects": [
      {
        "type": "deny",
        "reason": "Force pushes are prohibited by organization policy.",
        "code": "com.example.policy.force_push"
      }
    ]
  }
}

Observe notification

{
  "jsonrpc": "2.0",
  "method": "hooks/observe",
  "params": {
    "protocolVersion": "draft",
    "event": {
      "id": "evt_01JQ8Z7BEQW0X31TVPN5AZ1YJ6",
      "source": "urn:uuid:5b7de29e-a9e0-41a8-bf26-d94b05f0656d",
      "harness": {"name": "example-harness", "version": "1.0.0"},
      "type": "tool.after",
      "time": "2026-08-24T08:51:15Z",
      "session": {
        "id": "sess_123"
      },
      "tool": {
        "name": "Bash",
        "kind": "shell",
        "input": {
          "command": "git status"
        },
        "origin": "native"
      },
      "call": {
        "id": "call_789"
      },
      "path": "native",
      "items": [],
      "outcome": "ok",
      "execution": {
        "status": "executed"
      }
    }
  }
}

A notification has no JSON-RPC id and MUST NOT receive a JSON-RPC response.

Event model

Common event fields

Field Type Requirement Semantics
`id` string REQUIRED Globally unique event identifier. Stable across retries.
`source` string REQUIRED Stable, non-secret URI identifying the harness installation or runtime instance.
`type` string REQUIRED Event type defined by this specification.
`time` RFC 3339 string REQUIRED Time at which the harness created the event.
`harness` object REQUIRED Originating agent harness identity: non-empty string `name` and `version`.
`session` object Event-specific Session identity and optional execution context; required by session events.
`tool` object Tool events only Tool-call identity and event-specific data.
`native` object OPTIONAL Provider-specific fidelity payload. Disabled by default.
`extensions` object OPTIONAL Namespaced extension data.
`id` MUST be unique across events produced by a harness. UUIDv4, UUIDv7, and suitably generated ULIDs are acceptable examples; the protocol does not require a specific algorithm. `source` SHOULD remain stable for the life of the harness installation. It MUST NOT contain access tokens, user secrets, raw prompts, or other sensitive data. **AHP-CORE-005 — MUST.** Every event contains `harness` with non-empty string `name` and `version` identifying the originating agent harness.

An adapter MUST report the originating harness, not its own identity. The version identifies the harness product version; it is not the AHP protocol version and need not use semantic versioning. This metadata is not authentication or capability negotiation. Backends MUST NOT infer capabilities from it. The field is required for both interception and observation, including events received without a preceding session.start.

Session object

Field Type Requirement Semantics
`id` string REQUIRED Stable session identifier.
`cwd` string OPTIONAL Current working directory at event creation.
`workspaceRoots` string array OPTIONAL Workspace roots visible to the harness.
`model` string OPTIONAL Harness-reported model identifier.
`agent` object OPTIONAL Current subagent identity, if applicable.
Paths and model identifiers may be sensitive. A harness MUST permit implementations or administrators to omit optional session fields. If present, `agent` has a required `id` and an optional provider-defined `type`. ### Tool invocation and completion

Tool events use call: {"id": "…"} for invocation identity, separate from the tool descriptor. The call identity stays stable across the invocation’s boundaries; adapters synthesize stable identity when native identity is absent. Identical tool names and inputs alone do not distinguish repeated invocations. path identifies the covered tool path.

The tool object requires name (preserved verbatim), parsed object input, and origin (native or mcp). kind is an optional behavioral classification. MCP origin requires a self-contained mcp descriptor; native origin forbids it. See Execution payloads for MCP connection metadata, explicit gaps, and credential restrictions.

tool.after requires outcome, execution, and items, in addition to call, tool, and path. Result content uses content items rather than tool.output. Failures use event-level error with required class and message when outcome is error; there is no standard tool.error event. Other outcomes forbid error. Denial uses outcome: "denied" and skipped/policy execution. See Execution payloads for the complete event-specific requirements and canonical content selection for inline text parts and immutable non-text, non-JSON media attachments. Model-visible content uses ordered canonical messages; the owning message supplies the role, not a repeated role on each child part.

Native payload

The optional native object has this shape:

{
  "provider": "claude-code",
  "eventName": "PreToolUse",
  "payload": {}
}

A harness MUST omit native unless native-payload delivery is enabled by local policy. A portable backend MUST function when native is absent. Native fields MUST NOT change the semantics of a core effect.

Extensions

AHP-EXT-001 — MUST. Extension keys use reverse-DNS names and core behavior does not depend on an extension unless that dependency is explicitly declared.

Extension keys MUST use reverse-DNS namespacing, such as com.example.policy or org.w3c.trace_context. Unnamespaced extension keys are invalid. Extension values MAY contain any JSON value. Core implementations MUST preserve extensions when proxying an otherwise unchanged event, but they MUST NOT interpret unknown extensions. A Trace Context extension might look like:

{
  "extensions": {
    "org.w3c.trace_context": {
      "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01",
      "tracestate": "congo=t61rcWkgMzE"
    }
  }
}

This extension provides correlation only. Its presence does not make OpenTelemetry part of AHP conformance.