Skip to content

ACP Core Specification — v0.8

Status: Stable
Authors: ACP Community
Date: 2026-03-21
License: Apache 2.0
Supersedes: core-v0.5.md, core-v0.1.md See also: transports.md · error-codes.md · identity-v0.8.md


0. Design Principles

ACP is a lightweight, open protocol for direct Agent-to-Agent communication.

Principle Meaning
Zero-server No central relay, registry, or broker required
Zero-config The acp:// link is the connection — no setup beyond starting the process
Curl-compatible Every endpoint is reachable with plain curl. No SDK required.
Single required dep websockets only. All other features are truly optional.
Forward-compatible Unknown fields MUST be ignored by receivers
Warn-not-drop Signature mismatches produce warnings, never message loss

Design motto: MCP standardizes Agent↔Tool. ACP standardizes Agent↔Agent.


1. Message Envelope

Every ACP message shares a common JSON envelope regardless of transport:

{
  "type":       "acp.message",
  "message_id": "msg_7a3f9c2b",
  "server_seq": 42,
  "ts":         "2026-03-21T07:00:00Z",
  "from":       "AgentA",
  "role":       "user",
  "parts":      [ ... ],

  "task_id":    "task_abc123",   // optional — associate with a task
  "context_id": "ctx_xyz456",   // optional — multi-turn context group (v0.7)
  "sig":        "a3f9...",      // optional — HMAC-SHA256 hex (v0.7)
  "identity":   { ... }         // optional — Ed25519 identity block (v0.8)
}

1.1 Required Fields

Field Type Description
type string Always "acp.message" for user/agent messages
message_id string Client-generated unique ID (format: msg_<random>). Auto-generated by server if omitted.
ts string ISO 8601 UTC timestamp
from string Sender agent name
role string "user" or "agent"
parts array One or more Part objects (see §2)

1.2 Optional Fields

Field Type Since Description
server_seq integer v0.5 Server-assigned monotonic sequence number (ordering guarantee)
task_id string v0.5 Associates message with a task
context_id string v0.7 Groups messages into a named multi-turn context
sig string v0.7 HMAC-SHA256 signature (see §6.1)
identity object v0.8 Ed25519 identity block (see §6.2)

1.3 message_id Generation Strategy

message_id is intentionally optional on input: - If the client provides message_id: used as-is, enables client-side idempotency - If omitted: server auto-generates msg_<16 hex chars>

This differs from A2A where messageId is REQUIRED (and not enforced in v1.0 SDK). ACP's approach reduces friction for quick integrations while supporting full idempotency when needed.

Idempotency: A server MAY deduplicate messages with the same message_id within a session window.


2. Part Model

A Part is the atomic unit of message content. Every message carries one or more Parts.

2.1 Text Part

{
  "type":    "text",
  "content": "string content here"
}
  • content MUST be a string.
  • Use for natural language, instructions, and plain-text data.

2.2 File Part

{
  "type":       "file",
  "url":        "https://example.com/report.pdf",
  "media_type": "application/pdf",
  "filename":   "report.pdf"
}
  • url REQUIRED. Must be an accessible HTTP/HTTPS URL.
  • media_type RECOMMENDED. Standard MIME type.
  • filename OPTIONAL. Display name hint.
  • ACP does not inline raw bytes. Use URL references only (keeps messages relay-friendly).

2.3 Data Part

{
  "type":    "data",
  "content": { "any": "json", "value": true }
}
  • content MUST be a JSON-serializable value (object, array, string, number, boolean, or null).
  • Use for structured results, function call outputs, and machine-readable payloads.

2.4 Shorthand

For convenience, POST /message:send accepts "text": "..." as shorthand for "parts": [{"type": "text", "content": "..."}]. The server normalizes to full Part form.


3. Task Lifecycle

Tasks are units of delegated work. A task is created by the requesting agent and progresses through a 5-state machine.

3.1 State Machine

            ┌─────────────────────────────────────────────────────┐
            │                                                     │
  ──► submitted ──► working ──► completed (terminal)             │
                      │                                           │
                      ├──► failed (terminal)                     │
                      │                                           │
                      └──► input_required ──► working ──► ...    │
                                  │                               │
                                  └──► canceled (terminal) ◄─────┘

3.2 States

State Terminal? Description
submitted No Task created, not yet picked up by the peer
working No Peer is actively processing
completed ✅ Yes Peer finished successfully; artifact may be attached
failed ✅ Yes Peer encountered an unrecoverable error
input_required No Peer needs additional input to continue
canceled ✅ Yes Task was explicitly canceled

3.3 Transition Rules

  • Terminal states (completed, failed, canceled) cannot transition to any other state.
  • input_requiredworking resumes when the client sends a /tasks/{id}/continue message.
  • A task in any non-terminal state may be moved to canceled via POST /tasks/{id}:cancel.

3.4 Task Object

{
  "id":          "task_abc123",
  "status":      "working",
  "created_at":  "2026-03-21T07:00:00Z",
  "updated_at":  "2026-03-21T07:00:05Z",
  "input":       { "parts": [...] },
  "artifact":    { "parts": [...] },   // present when status=completed
  "error":       "string",             // present when status=failed
  "message_id":  "msg_xyz"             // message_id that created this task
}

4. AgentCard

Every ACP agent exposes its capabilities via a well-known endpoint:

GET /.well-known/acp.json

4.1 AgentCard Schema

{
  "name":        "MyAgent",
  "acp_version": "0.8",
  "timestamp":   "2026-03-21T07:00:00Z",
  "skills":      [{"id": "summarize", "name": "summarize"}],

  "capabilities": {
    "streaming":          true,
    "push_notifications": true,
    "input_required":     true,
    "part_types":         ["text", "file", "data"],
    "max_msg_bytes":      1048576,
    "query_skill":        true,
    "server_seq":         true,
    "multi_session":      true,
    "error_codes":        true,
    "hmac_signing":       false,
    "lan_discovery":      false,
    "context_id":         true,
    "identity":           "none"
  },

  "identity": null,

  "trust": {
    "scheme":  "none",
    "enabled": false
  },

  "auth": {
    "schemes": ["none"]
  },

  "endpoints": {
    "send":          "/message:send",
    "stream":        "/stream",
    "tasks":         "/tasks",
    "agent_card":    "/.well-known/acp.json",
    "skills_query":  "/skills/query",
    "peers":         "/peers",
    "peer_send":     "/peer/{id}/send",
    "peers_connect": "/peers/connect"
  }
}

4.2 Capability Flags

Flag Type Description
streaming bool SSE /stream endpoint available
push_notifications bool Agent can push unsolicited events
input_required bool Agent supports input_required task state
part_types string[] Supported Part types
max_msg_bytes int Maximum message size in bytes (default: 1,048,576)
query_skill bool /skills/query endpoint available
server_seq bool Outbound messages include server_seq
multi_session bool Multiple simultaneous peer connections supported
error_codes bool Error responses include error_code field (v0.6)
hmac_signing bool HMAC-SHA256 signing active (v0.7)
lan_discovery bool mDNS LAN discovery active (v0.7)
context_id bool context_id field supported (v0.7)
identity string "ed25519" or "none" (v0.8)

4.3 Forward Compatibility

Receivers MUST ignore unknown capability fields. A flag value of false or absent is equivalent.


5. Error Codes

All error responses follow a consistent envelope:

{
  "ok":                false,
  "error_code":        "ERR_NOT_CONNECTED",
  "error":             "No P2P connection",
  "failed_message_id": "msg_abc123"
}
Code HTTP Trigger
ERR_NOT_CONNECTED 503 No peer WebSocket connection
ERR_MSG_TOO_LARGE 413 Message exceeds max_msg_bytes
ERR_NOT_FOUND 404 Task, peer, or resource does not exist
ERR_INVALID_REQUEST 400 Missing fields, malformed body, invalid state transition
ERR_TIMEOUT 408 Sync wait timed out
ERR_INTERNAL 500 Unexpected server-side exception

failed_message_id is present for ERR_TIMEOUT and ERR_MSG_TOO_LARGE only.

See error-codes.md for retry guidance and full examples.


6. Optional Extensions

Extensions are opt-in fields that can be combined freely. Absence of any extension has no effect on core protocol operation.

6.1 HMAC-SHA256 Signing (v0.7)

For closed deployments where both peers share a secret out-of-band:

Outbound: server appends sig to every message:

sig = HMAC-SHA256(secret, "{message_id}:{ts}").hexdigest()

Inbound: if sig present and secret configured, verify with hmac.compare_digest.
Mismatch → log warning + set _sig_invalid=true on message object. Message is not dropped.

AgentCard: capabilities.hmac_signing = true, trust.scheme = "hmac-sha256".

CLI: --secret <shared_key> (both peers must use the same key).

6.2 Ed25519 Identity (v0.8)

For open scenarios where peer identity must be publicly verifiable:

Wire format:

"identity": {
  "scheme":     "ed25519",
  "public_key": "<base64url 32-byte public key>",
  "sig":        "<base64url 64-byte Ed25519 signature>"
}

Signing input: canonical JSON of full message envelope, excluding identity.sig:

canonical = {k: v for k, v in msg.items() if k != "identity"}
payload = json.dumps(canonical, sort_keys=True, separators=(",",":")).encode()

Keypair storage: ~/.acp/identity.json (auto-generated on first --identity run, chmod 0600).

Verification: warn-only on mismatch; accept if cryptography not installed.

AgentCard: capabilities.identity = "ed25519", identity.{scheme, public_key} block.

CLI: --identity [path]. Requires: pip install cryptography.

HMAC and Ed25519 may be active simultaneously — they serve different use cases.

See identity-v0.8.md for full spec including coexistence table and security properties.

6.3 context_id (v0.7)

Groups messages across multiple turns into a named conversation context:

{ "context_id": "ctx_xyz456", ... }

Receivers SHOULD use context_id to correlate related messages in multi-turn workflows. No server-side enforcement is required — it is a hint field.

6.4 mDNS LAN Discovery (v0.7)

When --advertise-mdns is set, the agent broadcasts its presence on the LAN via UDP multicast (224.0.0.251:5354). Nearby ACP agents appear at GET /discover.

No external library required (raw UDP socket). AgentCard: capabilities.lan_discovery = true.


7. Transport Bindings

ACP separates the message envelope (this document) from the transport layer. The same envelope is used across all bindings.

Binding Link Scheme Description
A — WebSocket P2P acp:// Direct WebSocket (preferred)
B — stdio n/a Subprocess pipe
C — HTTP Relay acp+wss:// Cloudflare Worker relay fallback
D — HTTP/SSE http(s):// Standard HTTP with SSE streaming
E — TCP tcp:// Raw TCP (LAN)

Binding A is the default and preferred transport. Use Binding C only when direct IP connectivity is blocked (firewalls, K8s NAT, etc.).

See transports.md for full binding specifications including HMAC header handling.


8. Multi-Session Peer Registry (v0.6)

An ACP agent can maintain simultaneous connections to multiple peers.

8.1 Endpoints

Endpoint Method Description
/peers GET List all connected peers with metadata
/peer/{id} GET Get single peer info (agent_card, link, stats)
/peer/{id}/send POST Send message to a specific peer (same body as /message:send)
/peers/connect POST Establish a new peer connection {"link": "acp://..."}

8.2 Peer Object

{
  "id":               "peer_001",
  "name":             "AgentB",
  "link":             "acp://1.2.3.4:7801/tok_xxx",
  "connected":        true,
  "connected_at":     "2026-03-21T07:00:00Z",
  "messages_sent":    12,
  "messages_received": 8,
  "agent_card":       { ... }
}

9. Skill Query API (v0.5)

Enables runtime capability discovery:

POST /skills/query
{ "query": "summarize", "limit": 5 }

Response:

{
  "ok": true,
  "skills": [
    { "id": "summarize", "name": "summarize", "match_score": 0.95 }
  ]
}

AgentCard declares available skills in skills[]. query_skill: true capability flag indicates this endpoint is available.


10. Versioning

ACP uses semantic versioning. The acp_version field in AgentCard declares the implemented version.

Compatibility guarantee: An ACP v0.8 implementation MUST: 1. Accept connections from v0.5+ peers (unknown extension fields are silently ignored) 2. Include acp_version: "0.8" in its AgentCard 3. Return error_code in all error responses if capabilities.error_codes = true

Breaking changes (requiring major version bump): changes to required envelope fields, removal of existing endpoints, changes to HTTP status code semantics.

Non-breaking changes (minor/patch): new optional fields, new endpoints, new capability flags.


11. Reference Implementation

The reference implementation is relay/acp_relay.py — a single Python file (~1900 lines) with one required dependency (websockets).

# Minimal start
pip install websockets
python3 acp_relay.py --name "MyAgent"

# Full feature set
pip install websockets cryptography
python3 acp_relay.py --name "MyAgent" \
  --secret "shared-key" \    # HMAC signing
  --identity \               # Ed25519 identity
  --advertise-mdns           # LAN discovery

Compliance can be verified against any implementation using the compat test suite:

ACP_BASE_URL=http://localhost:7901 python3 tests/compat/run.py


Appendix A: Version History

Version Date Key Features
v0.1 2026-03-05 P2P WebSocket, AgentCard, basic send/recv, auto-reconnect
v0.2 2026-03-10 JSONL persistence, SSE streaming, relay fallback
v0.3 2026-03-12 Task lifecycle (3 states), multi-session, Cloudflare Worker
v0.4 2026-03-14 Multimodal parts (text/file/data), relay v2
v0.5 2026-03-19 QuerySkill API, server_seq, message idempotency, 5-state task machine
v0.6 2026-03-20 Peer registry, standardized error codes (ERR_*), minimal agent spec
v0.7 2026-03-20 HMAC-SHA256 signing, mDNS LAN discovery, context_id
v0.8 2026-03-21 Ed25519 identity, Node.js SDK, compat test suite

Appendix B: Comparison with A2A v1.0

ACP and A2A target different deployment contexts:

A2A v1.0 ACP v0.8
Target Enterprise orchestration Personal / small teams
Server required Yes (agent infrastructure) No (pure P2P)
Auth OAuth 2.0 (mandatory) HMAC or Ed25519 (optional)
Required SDK deps Many (incl. implicit SQLAlchemy) websockets only
Identity Agent Passport System (full PKI) Self-sovereign Ed25519 (no PKI)
message_id REQUIRED by spec, not enforced in SDK Optional, auto-generated if absent
Test suite ✅ ITK (Issue #868) tests/compat/
Streaming SSE SSE
Task states 6 5 (no separate unknown state)

Spec version: v0.8 | Supersedes: core-v0.5.md | Reference impl: relay/acp_relay.py