Layer 1 — A2A Binding (shallow)
| Field | Value |
|---|---|
| Layer | 1 (additive binding) |
| Status | draft |
| Working Group | transport |
| Substrate | A2A v1.0.1 |
Scope
This document specifies an additive Layer 1 binding onto the Agent2Agent
Protocol (A2A), alongside the MCP binding
in 01-transport.md. It does not change that document —
MCP remains the baseline, unmodified. A provider MAY advertise this binding in
addition to, or instead of, the MCP one; Layer 5 discovery and the buyer’s own
transport preference decide which connection is opened. There is no
protocol-level precedence rule between the two bindings.
This is the shallow strategy named in A2A-MAPPING.md: ACMP’s existing JSON-RPC messages travel unchanged inside an A2A envelope. The deep strategy — remodeling ACMP semantics natively onto A2A’s task state machine — remains future work; see Open Questions.
Normative Language
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.
1. Envelope
ACMP’s existing JSON-RPC 2.0 messages (Layer 1 §2–§3, byte-
identical, no schema change) travel inside the data Part of an A2A
Message:
{
"message_id": "msg_9c2f11",
"role": "user",
"task_id": "task_a7c923f1",
"parts": [
{
"data": {
"acmp_method": "acmp/invoke",
"params": {
"task_id": "task_a7c923f1",
"capability": "sentiment-analysis",
"input": { "type": "text", "data": "Revenue grew 12% YoY." },
"max_price_cu": 0.005,
"escrow_id": "esc_a3f9c2"
}
},
"media_type": "application/acmp+json"
}
]
}
media_type(the A2APartfield carrying the MIME type) MUST beapplication/acmp+jsonon everydataPart carrying an ACMP message.- The
dataPart’s root object MUST be{"acmp_method": "<method>", "params": {...}}for requests and notifications. Responses and errors carryacmp_result/acmp_erroras the root key instead, with the same field contents as the corresponding JSON-RPCresult/errorobject in Layer 1 §3. task_idis carried in both places: the A2AMessage.task_idfield (for A2A-native correlation without parsing the payload) and, unchanged, inside the ACMPparams(for ACMP-side processing). This redundancy is intentional.- No ACMP field is hoisted into
Message.metadata. See §3.1.
2. Capability Negotiation via Agent Card
Neither substrate uses a handshake for this any more: A2A never had one, and
MCP removed initialize in revision 2026-07-28. Both now declare
extensions inside their capabilities — but in different shapes, which is
worth keeping straight. MCP uses a map of extension identifier to a
settings object; A2A uses an array of AgentExtension objects (uri,
description, required, params). The acmp capability object from
Layer 1 §1 therefore travels as the
params of an Agent Card extension entry.
The two substrates also name the extension differently — org.a2agora/acmp
under MCP’s reverse-DNS rule, https://a2agora.org/acmp/v0.1 under A2A’s URI
convention. They denote the same extension. The authoritative version is
the version field inside the capability object; the v0.1 in the A2A URI is
part of that URI’s own convention, not a second version number.
{
"capabilities": {
"streaming": true,
"extensions": [
{
"uri": "https://a2agora.org/acmp/v0.1",
"description": "ACMP Layer 1 shallow binding",
"params": {
"version": "0.1.0",
"role": "provider",
"accepts": ["acmp/invoke", "acmp/cancel"],
"emits": ["acmp/streamChunk", "acmp/heartbeat"],
"features": {
"output_streaming": true,
"input_streaming": false,
"heartbeat_interval_ms": 5000
}
}
}
]
}
}
Field meanings inside params are identical to Layer 1
§1 — this is the same object, carried
at a different location. A buyer MUST NOT rely on a feature the provider did
not advertise here, exactly as it would not over the MCP binding. The provider
MAY set the extension’s required flag to true if it serves ACMP traffic
exclusively; it SHOULD leave it false when the same Agent Card also serves
plain A2A clients.
3. Resolved Questions
A2A-MAPPING.md left four sub-questions open. This binding resolves three of them; the fourth is a Layer 6 question and stays open.
3.1 Field placement (escrow_id, proof_method, max_price_cu)
These fields stay exactly where Layer 1 §3.1
puts them: inside the ACMP JSON-RPC payload in the data Part. None are
hoisted to top-level Message.metadata. Promoting fields to metadata would
already be a step toward the deep strategy — the shallow strategy’s entire
value is that the ACMP schema does not change.
3.2 Heartbeat / liveness
acmp/heartbeat notifications travel exactly like acmp/streamChunk: as
another data-Part message over the same A2A streaming channel
(SendStreamingMessage / SSE). No new A2A mechanism is introduced. As in
Layer 1 §1, heartbeat remains
optional and capability-gated (heartbeat_interval_ms).
3.3 Input streaming (acmp/inputChunk)
Not supported by this binding. A provider offering this binding MUST
advertise input_streaming: false in its capability object
(§2) — this is already an
optional, capability-gated feature in Layer 1
§1, so declining it here changes
nothing structurally. This is a deliberate, documented gap rather than a
forced-fit approximation.
3.4 Error mapping
An ACMP error (Layer 1 §3.3, codes -33xxx)
stays entirely inside the payload: the enclosing A2A task reaches completed
regardless. A2A has no knowledge of ACMP semantics and sees only a
successfully delivered data Part. Generic A2A tooling (task lists,
monitoring dashboards) sees an ACMP error only if it parses the payload — an
accepted trade-off of the shallow strategy, not a defect to fix here.
Design Decisions
| Question | Decision | Rationale |
|---|---|---|
| Shallow or deep? | Shallow | Zero schema change, lowest risk, serves Principle P5 (incremental adoptability). Deep remains future work — see Open Questions. |
| Field placement? | Inside the data Part, not Message.metadata |
Consistent with zero schema change. |
| Capability transport? | Extension params on the Agent Card |
Neither substrate has a handshake to carry it — the Agent Card is A2A’s declaration point, as server/discover is MCP’s. |
| Error mapping? | Payload-internal; the A2A task stays completed |
Consistent with shallow — the binding needs no knowledge of A2A’s state machine. |
| Input streaming? | Not supported | An honest gap rather than a forced fit; already optional and capability-gated in Layer 1. |
Open Questions
[OPEN]Deep-remodel strategy. Remodeling ACMP semantics natively onto A2A’sSendMessage/GetTask/CancelTask/SendStreamingMessageand its 8-state task machine — touching every Layer 1 message schema and several Layer 2/6 assumptions — is deliberately out of scope here. See A2A-MAPPING.md. It would let ACMP inherit A2A’sinput-required/auth-requiredresumable-interrupt states essentially for free, closing the gap noted in Layer 1’s own Open Questions — but it is a substantially bigger undertaking that deserves its own dedicated design work if pursued.[OPEN]Negotiation viainput-required(cross-layer note). A2A’s resumable-interrupt states could plausibly give Layer 6 (negotiation) a native “task paused, awaiting counter-offer” mechanism. This is a Layer 6 question, not a Layer 1 one — noted here as a pointer, not resolved. See Layer 6.
Related
- Layer 1 — Transport & Invocation — the MCP baseline, unchanged
- A2A-MAPPING.md — the analysis and rationale behind this binding
- RFC-0001 §7
This document is part of the A2Agora specification. Licensed under Apache 2.0.