Docs / Serving over A2A

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.json advertises identity and skills.
  • A JSON-RPC endpoint (default /a2a) handles message/send, tasks/get, and tasks/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_binding resolves 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.1 unless you set AOS_A2A_BIND.
  • Fail closed. Binding a public interface without AOS_A2A_AUTH_TOKEN is refused (set AOS_A2A_ALLOW_ANON=1 to serve anonymously on purpose).
  • Limits on by default. Concurrency, rate, and budget guards apply to message/send even when unset.
  • Signing fails closed. If AOS_A2A_SIGNING_KEY is 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/get is 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.

Next Steps