Message Format
Specification Version
A2E Protocol Version 1.0 — a strict superset of SCP (Skill Call Protocol) 1.0.
Framing
Messages are transmitted as NDJSON (newline-delimited JSON) over a reliable byte stream. Each message is a single JSON object on one line, terminated by \n.
<JSON object>\n
<JSON object>\n
...Compact JSON encoding is used — no extraneous whitespace.
Base Message Schema
Every A2E message conforms to this base schema:
{
"a2e": "1.0",
"type": "<namespace>/<verb>",
"id": "<uuid-hex>",
"ts": <unix-epoch-float>
}| Field | Type | Required | Description |
|---|---|---|---|
a2e | string | Yes | Protocol version, always "1.0" |
type | string | Yes | Message type identifier (e.g. "tool/call/req") |
id | string | Yes | UUID hex, unique per message |
ts | number | Yes | Unix epoch timestamp (float, seconds) |
Capability-specific messages add additional fields.
Type String Convention
Type strings follow the pattern: <namespace>/<verb> or <namespace>/<verb>/<suffix>
| Pattern | Example | Meaning |
|---|---|---|
<ns>/<verb>/req | tool/call/req | Request from agent |
<ns>/<verb>/resp | tool/call/resp | Response from host |
<ns>/<verb> | chain/event | Event or notification |
<ns>/<verb>/<action> | mcp/server/register/req | Nested namespace |
Encoding Rules
- UTF-8 encoding for all text
- Compact JSON — no extra whitespace
- No binary framing — newline delimits messages
- Pydantic v2 validation on both sides — invalid messages produce
A2EErrorwithschema_violationcode
Decoding
The receiver maintains a type registry mapping type strings to Pydantic model classes:
# Base types (always registered)
A2E_BASE_TYPE_MAP = {
"handshake/req": HandshakeRequest,
"handshake/resp": HandshakeResponse,
"invoke/event": A2EEvent,
"ping": Ping,
"pong": Pong,
"shutdown": Shutdown,
"error": A2EError,
}
# Plugin types (registered at startup)
TOOL_TYPE_MAP = {
"tool/list/req": ToolListRequest,
"tool/list/resp": ToolListResponse,
"tool/call/req": ToolCallRequest,
"tool/call/resp": ToolCallResponse,
"tool/event": ToolEvent,
}If a type string is not found in the registry, the message is decoded as a generic A2EMessage (graceful degradation).
Message Ordering
- Within a session: Messages are processed in order of arrival
- Across sessions: No ordering guarantee
- Events: Each
A2EEventhas a monotonicseqnumber within itsreq_idscope - Correlation: Responses reference their request via
req_id
Size Limits
No explicit message size limit is defined in the protocol. Implementations may impose limits via global_limits configuration.
Extensibility
New capability namespaces can be added by:
- Defining new
MessageTypeenum values - Creating Pydantic request/response/event models
- Building a
TYPE_MAPdict - Implementing an
A2EPluginsubclass
Unknown message types degrade gracefully — they are decoded as A2EMessage and can be routed to a catch-all handler.