Skip to content

A2A conformance

SuperOptiX implements the A2A 1.0 wire protocol directly over FastAPI. This page covers how that implementation is verified, how version negotiation works, and how to test an agent you have adapted.

Current results

Measured with the official A2A TCK against the conformance harness:

Level Passed / exercised Not exercised TCK headline
MUST 73 / 73 21 77.7%
SHOULD 7 / 7 4 63.6%
MAY 4 / 4 0 100%

Zero failures. Every requirement the TCK is able to exercise against this endpoint passes.

Why the headline percentage is lower

The TCK counts a requirement it cannot exercise as non-compliant. Until 2026-08-31 it counted those as compliant, which is why earlier versions of this page reported 100% across the board. The change is upstream commit cf4985b, "don't count NOT TESTED requirements as compatible".

25 requirements fall into that bucket here:

Area Count Why it cannot be exercised
Authentication and TLS 13 The harness serves plain HTTP with no auth scheme
Agent Card JWS signatures 4 Card signing is not implemented
Cross-binding equivalence 4 Requires the gRPC binding for comparison
Version negotiation probes 3 Require a second declared server version
gRPC binding 1 Not implemented

None are failures. Reaching a 100% headline means implementing authentication, card signing and a gRPC binding, which are not scheduled.

What the CI gate checks

.github/workflows/a2a-conformance.yml runs on demand and fails on any conformance failure. It does not gate on the headline percentage, because that number moves when the TCK changes what it can exercise, independent of this implementation.

The published SuperOptiX endpoint and agents produced by super a2a adapt score lower on the headline figure. The difference is not a defect: the remaining requirements are TCK scenario hooks that a production agent should not implement. See The conformance harness.

The live endpoint

A SuperOptiX agent runs at a2a.superoptix.ai, hosted on Cloud Run.

Agent Card, published superoptix.ai/.well-known/agent-card.json
Agent Card, served by the agent a2a.superoptix.ai/.well-known/agent-card.json
Endpoint https://a2a.superoptix.ai

Two copies of the card is the intended arrangement rather than duplication. The published copy is a static file on the website, so discovery answers instantly whether or not the service is warm. The served copy confirms the running agent agrees with what was published. The two are the same JSON document (pretty-printed on the website, compact on Cloud Run).

The agent exposes two skills, both deterministic. Neither calls a model, reads user code, nor holds a credential, which is what makes the endpoint safe to expose and inexpensive to run.

curl -X POST https://a2a.superoptix.ai/message:send \
  -H 'content-type: application/json' \
  -d '{"message":{"role":"ROLE_USER","parts":[{"text":"Does CrewAI support A2A?"}]}}'

curl -X POST https://a2a.superoptix.ai/a2a/jsonrpc \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":"1","method":"message/send","params":{"message":{"role":"user","parts":[{"kind":"text","text":"Does CrewAI support A2A?"}]}}}'

Opening https://a2a.superoptix.ai in a browser returns a page describing the endpoint rather than an error. The address appears in the Agent Card and in registry listings, so people follow it, and a bare 404 reads as a broken service.

Hosting

The endpoint moved from Render to Cloud Run in August 2026. The reason was cold start rather than cost:

Cold start Warm
Render free tier 42.5 s 0.41 s
Cloud Run, scale to zero 1.7 s 0.06 s

A2A clients apply timeouts. A registry fetching a card with a 30 second timeout records a 42.5 second endpoint as unreachable rather than slow, so the free tier on Render was not viable for an agent meant to be discovered. Cloud Run keeps the image staged and starts a container on demand, which brings the same idle-to-first-request path inside those timeouts.

deploy/a2a/README.md covers the deployment and the migration.

Running the TCK

The TCK is a pytest suite that exercises a running agent across the JSON-RPC, HTTP+JSON and gRPC bindings, filtered by RFC 2119 level.

git clone https://github.com/a2aproject/a2a-tck.git
cd a2a-tck
uv venv && uv pip install --python .venv/bin/python -e .

Start the conformance harness and point the suite at it:

uvicorn superoptix.protocols.a2a.tck_sut:app --port 8000

.venv/bin/python run_tck.py --sut-host http://127.0.0.1:8000 --level must

Reports land in reports/: compatibility.json for machine reading, compatibility.html for review.

To test an agent you have adapted, point the same command at its server instead.

The conformance harness

The TCK drives an agent into specific protocol states using reserved messageId prefixes. tck-input-required must leave a task non-terminal, tck-complete-task must complete it, tck-artifact-text must return an artifact. Reference implementations in the A2A project do the same.

Those hooks live in a separate application, superoptix/protocols/a2a/tck_sut.py, rather than in the published endpoint. A production agent that changes behaviour based on a client-supplied identifier is honouring untrusted input. Both applications share the same server implementation, so conformance measured against the harness holds for the protocol layer that adapted agents use.

Continuous integration

.github/workflows/a2a-conformance.yml is a manual (workflow_dispatch) job. It starts superoptix.protocols.a2a.tck_sut:app, runs the official TCK against that harness, publishes the compatibility report as an artifact, and fails on any FAIL status. It does not run on every push, and it does not gate on the TCK headline percentage.

The published endpoint at a2a.superoptix.ai is a separate Cloud Run service, redeployed from a git tag. Protocol regressions can therefore land on main before they are on the live host.

Version negotiation

One endpoint serves both spec lines. Clients select with the A2A-Version request header, and 1.0 is assumed when the header is absent.

curl -H 'A2A-Version: 1.0' localhost:8000/.well-known/agent-card.json
curl -H 'A2A-Version: 0.3' localhost:8000/.well-known/agent-card.json

What changes between the two:

1.0 0.3
Task state TASK_STATE_COMPLETED completed
Message role ROLE_AGENT agent
Part shape Unified, fields set directly Wrapped, tagged with kind
File part raw / url / filename / mediaType file.bytes / file.uri / file.name / file.mimeType
Card supportedInterfaces Top-level url and preferredTransport
JSON-RPC method names SendMessage message/send

Both sets of method names reach the same handlers, so a 0.3 client does not have to know it is talking to a 1.0 implementation:

0.3 1.0
message/send SendMessage
message/stream SendStreamingMessage
tasks/get GetTask
tasks/list ListTasks
tasks/cancel CancelTask
tasks/resubscribe SubscribeToTask
agent/authenticatedExtendedCard GetExtendedAgentCard
tasks/pushNotificationConfig/* *TaskPushNotificationConfig

A method outside both sets returns -32601.

An unrecognised version returns VersionNotSupportedError: -32009 over JSON-RPC, HTTP 400 over REST.

Both lines matter because the installed base is on 0.3. Of the eight frameworks SuperOptiX adapts, five declare no A2A dependency, and the three that do (CrewAI, Google ADK and Pydantic AI) are pinned below 1.0. An endpoint that speaks only 1.0 is unreachable by most agents currently deployed.

Translation is available directly:

from superoptix.protocols.a2a import bridge

legacy = bridge.task_to_v03(task)
current = bridge.task_to_v1(legacy)
card = bridge.card_to_v03(agent_card)

Agent Card caching

The Agent Card is fixed for the life of the process, so it is served with validators that let a caller skip the transfer on a repeat read.

Cache-Control: public, max-age=3600
ETag: "5b8e694cb6e718eb2633ad7de9a2909b"
Last-Modified: Mon, 31 Aug 2026 19:40:41 GMT
Vary: A2A-Version

A conditional request that matches returns 304 with no body:

curl -sI localhost:8000/.well-known/agent-card.json | grep -i etag
curl -si -H 'If-None-Match: "<etag>"' localhost:8000/.well-known/agent-card.json | head -1

The 1.0 and 0.3 renderings of the card are different documents and carry different entity tags, which is what the Vary header exists to signal. A cache holding one will not hand it to a client that asked for the other.

If-None-Match follows RFC 9110: a comma separated list is accepted, * matches anything, and a weak validator compares equal to its strong form.

Error handling

A2A binds each error to a JSON-RPC code, an HTTP status and an ErrorInfo reason. superoptix/protocols/a2a/errors.py holds the table.

JSON-RPC errors are returned with HTTP 200. The transport succeeded; the failure is inside the envelope. Returning 4xx alongside a JSON-RPC error causes conformant clients to treat the response as a transport failure and never read the code.

A2A-specific errors carry a google.rpc.ErrorInfo entry in error.data, per specification section 9.5:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32001,
    "message": "Task not found",
    "data": [{
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "domain": "a2a-protocol.org",
      "reason": "TASK_NOT_FOUND"
    }]
  }
}

HTTP+JSON errors use AIP-193 bodies with the same ErrorInfo in error.details.

Implemented surface

Method Status
SendMessage Implemented
SendStreamingMessage Implemented, SSE
GetTask Implemented, honours historyLength
ListTasks Implemented
CancelTask Implemented; terminal tasks return TaskNotCancelableError
SubscribeToTask Implemented
GetExtendedAgentCard Returns ExtendedAgentCardNotConfiguredError
Push notification config methods Return PushNotificationNotSupportedError

Bindings: JSON-RPC 2.0 and HTTP+JSON. gRPC is not implemented.

The JSON-RPC route is served at both /a2a/jsonrpc and /a2a/jsonrpc/. A client that treats the interface URL as an HTTP base and posts to / resolves to the trailing-slash form, and a redirect there returns an empty body that JSON-RPC clients cannot parse.

Known gaps

Agent Cards are unsigned. Signed cards are the 1.0 mechanism for proving a card was issued by the domain owner. The agent-card-review skill on the published endpoint reports this against SuperOptiX's own card.

gRPC is not implemented. The TCK covers it, and the requirements are skipped rather than failed.

The Agent Payments Protocol (AP2), published alongside 1.0, is out of scope.