Serving over A2A
Expose an AOS assembly as a first-class Agent2Agent (A2A) agent so other agents and clients can call its skills over JSON-RPC — skill-routed, warm, and fail-closed by default.
Overview
A2A is an open JSON-RPC protocol for agent-to-agent calls. aos serve turns your project into an A2A endpoint: each inbound request is routed to one of your skills and run on a warm workflow reused across requests — focused work, no cold start.
- An Agent Card at
/.well-known/agent-card.jsonadvertises identity and skills. - A JSON-RPC endpoint (default
/a2a) handlesmessage/send,tasks/get, andtasks/cancel. - A skill bound to an MCP tool resolves to a native tool call — no LLM round-trip.
The serve Command
Serve the current project's skills as an A2A agent:
aos serve --a2a The command builds a warm runtime — a vendor adapter plus one spawned worker agent and the MCP toolset manager — then serves every skill under core/skills/. Flags map to AOS_A2A_* environment variables (see Configuration).
The Agent Card
Clients discover the agent by fetching its card. It advertises the JSON-RPC endpoint, protocol version, and exposed skills:
GET /.well-known/agent-card.json
{
"name": "aos",
"url": "https://me.example.com/a2a",
"protocolVersion": "1.0",
"preferredTransport": "JSONRPC",
"capabilities": { "streaming": false, "pushNotifications": false },
"skills": [ { "id": "summarize", "name": "summarize", "tags": [] } ]
} When signing is enabled, the card carries a detached JWS signature so a caller can verify your identity before connecting. See Agent Mesh Security.
JSON-RPC Methods
message/send— create or continue a Task, run the routed skill, return the Task with its artifacts. This is the expensive, guarded path.tasks/get— fetch a Task by id (owner-scoped).tasks/cancel— cancel an in-flight Task (owner-scoped).
POST /a2a
{ "jsonrpc": "2.0", "id": 1, "method": "message/send",
"params": { "message": {
"role": "user",
"parts": [{ "kind": "text", "text": "Summarize this thread." }],
"metadata": { "skillId": "summarize" }
} } } Note
Tasks are owner-scoped: a caller can only read, cancel, or continue tasks it created. A mismatched id reads as “not found.” Streaming and push notifications are not advertised.
Skill Routing
Each inbound message is routed to a single exposed skill. The caller selects one via message.metadata.skillId; an unknown id is rejected (it cannot introduce a new skill). The selected skill runs on the warm worker:
- Skills are discovered from
core/skills/<id>/skill.yaml. - A skill with an
mcp_bindingresolves to a native MCP tool call — see MCP Toolsets. - The inbound text becomes the skill input; the skill output becomes a text Artifact on the Task.
Configuration
The serve entrypoint reads AOS_A2A_* environment variables:
# Endpoint
AOS_A2A_PORT=8080 # listen port
AOS_A2A_BIND=127.0.0.1 # interface (loopback by default)
AOS_A2A_PUBLIC_URL=https://me.example.com
AOS_A2A_AUTH_TOKEN=… # bearer token required on /a2a
AOS_A2A_WORKER_AGENT=arbiter # warm worker agent
AOS_A2A_ADAPTER=claude-code # runtime adapter
# Ingress limits (sane defaults if unset)
AOS_A2A_MAX_CONCURRENT=4
AOS_A2A_RATE_PER_MIN=60
AOS_A2A_BUDGET_PER_MIN=60
AOS_A2A_EXEC_TIMEOUT_MS=120000
# Signing / key discovery
AOS_A2A_SIGNING_KEY=/run/secrets/a2a.pem
AOS_A2A_SIGNING_ALG=ES256
AOS_A2A_SIGNING_JKU=https://me.example.com/.well-known/jwks.json Secure Defaults
- Loopback by default. The endpoint binds
127.0.0.1unless you setAOS_A2A_BIND. - Fail closed. Binding a public interface without
AOS_A2A_AUTH_TOKENis refused (setAOS_A2A_ALLOW_ANON=1to serve anonymously on purpose). - Limits on by default. Concurrency, rate, and budget guards apply to
message/sendeven when unset. - Signing fails closed. If
AOS_A2A_SIGNING_KEYis set but unreadable, the server refuses to start rather than serve an unsigned card.
The full model — signing, key discovery, SSRF egress, and ingress guards — is documented in Agent Mesh Security.
Calling Other Agents
The same runtime ships an A2A client for outbound calls. It resolves a peer's Agent Card, posts message/send, and drives the resulting Task to a terminal state — all through the SSRF egress gate, with optional card-signature verification:
- Fetch
/.well-known/agent-card.json, verify identity, then POST to the card's endpoint. - Every request and redirect hop is re-validated against the egress policy; credentials are stripped cross-origin.
- Polling
tasks/getis wall-clock bounded so a hostile peer cannot keep the client polling forever.
An execution-mode workflow step can delegate a sub-task to a remote A2A agent and graft its result back into the run.