ADR-003: JSON-RPC 2.0 for Inter-Agent Communication Protocol
Status: Accepted Date: 2025-09-20 Author: Spencer Fuller, Fiducian
Context
Section titled “Context”The OpenClaw platform supports multiple AI agents that serve different principals (humans) within a household. These agents need to exchange structured messages — requests for information, responses with data, one-way notifications, and task handoffs — while maintaining formal identity verification and audit trails.
The inter-agent protocol must support:
- Request/response semantics — Agent A asks Agent B a question, Agent B responds
- One-way notifications — Status updates, alerts, and proactive information sharing that don’t require a response
- Identity verification — Every message must carry 6 mandatory headers (agent-id, principal-id, timestamp, message-type, trust-layer-version, skill-layers-loaded) so agents can verify who they’re talking to and what behavioral commitments the sender has made
- Transport agnosticism — The protocol must work over NATS (machine-to-machine), Discord (human-visible transparency), and HTTP (MCP Gateway API) without modification
- Human readability — Messages should be inspectable by principals reviewing audit logs without specialized tooling
- Ecosystem alignment — The protocol should integrate naturally with the Model Context Protocol (MCP) ecosystem, which uses JSON-RPC 2.0
Decision
Section titled “Decision”Adopt JSON-RPC 2.0 as the wire format for all substantive inter-agent messages, with 6 mandatory identity headers embedded in the params.headers object. Four message types are defined: request, response, notification, and handoff.
Message structure:
{ "jsonrpc": "2.0", "method": "agent.request", "params": { "headers": { "agent-id": "fiducian-spencer-001", "principal-id": "spencer-fuller", "timestamp": "2026-02-08T17:00:00Z", "message-type": "request", "trust-layer-version": "1.0.0", "skill-layers-loaded": [0, 1, 2, 3] }, "body": { } }, "id": "msg-uuid-here"}Messages are signed with Ed25519 keys and verified by both the MCP Gateway (server-side) and the receiving agent. Tier 3/4 data is additionally encrypted with sealed boxes before signing.
Rationale
Section titled “Rationale”-
JSON-RPC 2.0 is a well-specified, minimal standard. The spec fits on one page. It defines exactly four concepts: requests (with
id), responses (referencingid), notifications (noid), and errors. There’s no ambiguity about how to structure a message or correlate a response to its request. This matters when agents from different codebases (or different LLM providers) need to interoperate. -
Native support for both request/response AND notifications. Agent communication isn’t purely request/response — agents need to proactively share information (“I noticed something relevant to your principal”) without expecting a reply. JSON-RPC 2.0’s notification concept (a request without an
id) maps perfectly to this. REST has no native equivalent; you’d have to invent webhook patterns. -
Transport-agnostic by design. JSON-RPC 2.0 is deliberately transport-agnostic — the spec says nothing about HTTP, TCP, or any transport. This means the same message format works over NATS subjects (
agent.<id>.inbox), Discord channels (posted as code blocks), or HTTP POST to the MCP Gateway. The protocol doesn’t change when the transport does. -
Human-readable JSON. Audit logs contain raw JSON-RPC messages that any developer (or principal) can read. No binary decoding, no protobuf schema files, no specialized tooling.
cat audit/inter-agent-2026-02.jsonl | jq .gives you the full conversation. -
MCP ecosystem alignment. The Model Context Protocol uses JSON-RPC 2.0. By choosing the same wire format, agents using this protocol can interface naturally with MCP servers and tools without protocol translation. The
methodnamespace (agent.*) avoids collisions with MCP’s method namespace.
Alternatives Considered
Section titled “Alternatives Considered”| Alternative | Why Not |
|---|---|
| REST (HTTP APIs) | REST models resources, not conversations. Agent communication is inherently bidirectional — Agent A asks a question, Agent B responds, possibly with follow-ups. REST would require inventing correlation IDs, callback URLs, and polling patterns to achieve what JSON-RPC 2.0 gives natively with id-correlated request/response pairs. REST also assumes HTTP transport, which doesn’t fit NATS or Discord channels. |
| gRPC | Binary protocol (Protocol Buffers) that’s excellent for microservice communication but overkill for agent-to-agent chat. Requires .proto schema compilation, doesn’t work over Discord or NATS without an adapter, and binary payloads aren’t human-readable in audit logs. The performance benefits of binary serialization are irrelevant — agent messages are small and infrequent. |
| Custom protocol | Tempting to design exactly what we need, but carries NIH (Not Invented Here) risk. A custom protocol means custom parsers, custom documentation, custom debugging tools, and zero ecosystem support. JSON-RPC 2.0 gives us 95% of what we need with 0% of the maintenance burden. |
| ActivityPub | Designed for federated social networking (Mastodon, etc.). The abstractions — actors, inboxes, outboxes, followers, likes — are wrong for agent-to-agent communication. We’d be fighting the protocol’s assumptions at every turn. ActivityPub also mandates HTTP as transport and brings significant complexity (JSON-LD, WebFinger, HTTP Signatures) for features we don’t need. |
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Clean interoperability with MCP ecosystem — agents can speak to MCP servers using the same wire format they use to speak to each other
- Identity headers (
skill-layers-loadedin particular) enable trust calibration — an agent declaring[0, 1, 2, 3]has committed to fiduciary duty; an agent declaring[0]has committed only to basic trust principles - Audit logging is trivial — append JSON lines to a file, one per message. The
summaryfield provides human-readable context without dumping full payloads - 4-step discovery handshake (announce → verify → capability exchange → trust establishment) prevents agents from exchanging substantive messages before establishing mutual identity
Negative
Section titled “Negative”- JSON parsing errors must be handled gracefully. LLM-generated JSON can be malformed. The protocol requires strict validation — reject and log malformed messages rather than attempting recovery. This adds error-handling complexity.
- No built-in streaming. JSON-RPC 2.0 is request/response, not streaming. For agent communication this is acceptable — messages are discrete requests and responses, not continuous streams. If streaming becomes necessary (e.g., real-time collaboration), it would require a protocol extension or a separate channel.
- Overhead of 6 mandatory headers on every message. Every message carries identity headers even when agents have an established relationship. This is deliberate (stateless verification, audit completeness) but adds ~200 bytes per message. Negligible in practice.