Skip to content

A2A (Agent2Agent) ​

Purple8 implements the A2A protocol v1.0 (JSON-RPC binding) in both directions:

  • Server. Other agents can discover Purple8 and delegate tasks to it as a peer.
  • Client. Remote A2A agents can be imported so their skills become tools. Those tools are then callable over MCP, REST and the console.

A2A complements MCP. MCP is how an agent uses tools; A2A is how agents delegate to each other.

Discovery: the Agent Card ​

bash
curl http://localhost:8100/.well-known/agent-card.json

The card is public. Its contents:

  • Skills. One skill per MCP namespace (16 namespaces covering 125 MCP tools), generated from the tool manifest. Human-only tools are excluded.
  • Capabilities. streaming: true and pushNotifications: true. extendedAgentCard is false.
  • Caching. The card is served with ETag and Cache-Control headers, and conditional requests get 304 responses.

Calling Purple8 over JSON-RPC ​

All methods are POST /a2a with a bearer token or an API key. Send the header A2A-Version: 1.0.

bash
curl -X POST http://localhost:8100/a2a \
  -H "Authorization: Bearer $TOKEN" -H "A2A-Version: 1.0" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{
        "message":{"messageId":"m1","role":"ROLE_USER",
                   "parts":[{"text":"What changed in the Q3 contracts?"}]}}}'
MethodPurpose
SendMessageStart or continue a task. It blocks until the task finishes unless configuration.returnImmediately is set.
SendStreamingMessageSame as SendMessage, but events are streamed over SSE.
SubscribeToTaskStream the events of an existing task over SSE.
GetTaskFetch a task. historyLength trims the history.
ListTasksList tasks with cursor paging. Filters: contextId, status, statusTimestampAfter. The response includes totalSize.
CancelTaskCancel a running task.
Create/Get/List/DeleteTaskPushNotificationConfigManage webhooks for a task (see below).

Errors use the spec codes -32001 to -32009.

How a skill is chosen ​

No LLM planning happens. The message is resolved deterministically, in this order:

  1. metadata.skill names a tool (for example graph.pagerank). The arguments come from metadata.arguments or from a data part.
  2. A data part carries {"skill": ..., "arguments": {...}}.
  3. Plain text goes to rag.hybrid_query.

If required arguments are missing, the task moves to TASK_STATE_INPUT_REQUIRED. To continue it, send another message with the same taskId.

Execution uses the caller's MCP path, so RBAC, the human-only gate and an API key's allowed_tools all apply. Tasks are stored as per-tenant A2ATask graph nodes, with history capped at 50 messages.

Streaming across workers ​

The SSE streams follow the task store as well as the in-process event bus. A task that is updated by a different worker process still reaches the subscriber. P8G_A2A_STREAM_POLL_S sets the poll interval (default 1.0).

Push notifications ​

You can attach a webhook when you start a task:

json
"configuration": {
  "returnImmediately": true,
  "taskPushNotificationConfig": {
    "url": "https://hooks.example.com/a2a",
    "token": "opaque-verifier",
    "authentication": {"scheme": "Bearer", "credentials": "s3cret"}
  }
}

You can also manage webhooks on an existing task with CreateTaskPushNotificationConfig ({"taskId": ..., "url": ...}) and the other three *TaskPushNotificationConfig methods.

Delivery works like this:

  • What is sent. Every status or artifact event is POSTed to each webhook as a StreamResponse with Content-Type: application/a2a+json.
  • Headers. Authorization: <scheme> <credentials>, X-A2A-Notification-Token: <token> and A2A-Version: 1.0.
  • Ordering and retries. Deliveries are ordered per task and retried up to 3 times with exponential backoff. At most 32 run concurrently.
  • Limits. Each task can have up to 10 configs. credentials is write-only and is never returned by Get or List. Delete is idempotent.

SSRF protection. The webhook URL is checked when the config is created, and checked again before every delivery attempt (this guards against DNS rebinding). Private, loopback, link-local and plain-HTTP targets are rejected.

Env varDefaultMeaning
P8G_A2A_PUSH_TIMEOUT_S15Per-delivery HTTP timeout
P8G_A2A_PUSH_ALLOW_INSECUREoffAllow private and plain-HTTP targets. Local development only.

Using other agents as tools ​

To import a peer agent, use any of these:

  • REST: POST /registry/a2a-agents {"prefix": "billing", "card_url": "https://billing.example.com", "credential": "..."}
  • MCP: the registry.register_a2a_agent tool, with the same arguments.
  • UI: MCP Console, then the A2A tab, then Peer agents.

Purple8 fetches the card and registers one tool per skill as {prefix}.{skill_id}. A call sends SendMessage and polls GetTask with backoff, bounded by the tool's timeout_s. It returns {state, text, artifacts, task_id, context_id}. The card URL and the JSON-RPC URL in the card are both SSRF-checked, and the card fetch does not follow redirects. Credentials are stored with envelope encryption.

Console ​

The A2A tab in the MCP Console shows three things:

  • This agent's card and skills.
  • Imported peers, with a test-message form.
  • Inbound tasks, backed by GET /a2a/tasks and GET /a2a/tasks/{id}.

Purple8 Hyper Graph v0.72.0 · AI-Native Execution Infrastructure · NVIDIA Inception Member · Red Hat Technology Partner.