What A2A v1.0 changed in the Agent Card
A2A v1.0 is not a cosmetic renumbering. The transport section of the Agent Card was restructured, which means a v0.x card does not degrade gracefully — it stops describing anything a v1.0 client can address, while still parsing as perfectly good JSON.
Why this one is not a renumbering
Most of what changed in the
A2A specification is a rename you could
handle with sed. One change is not:
four separate top-level fields that described how to reach an agent were
replaced by a single ordered array. That is a structural change, and it is why a
v0.x card fails silently rather than loudly. A v1.0 client looks for
supportedInterfaces, does not find it, and concludes your agent has no
reachable interface.
The change came with the protocol's move from its launch at Google to Linux Foundation governance and multi-vendor development — context that matters mostly because it explains the direction of travel: fewer implicit assumptions, more things stated per-endpoint instead of per-agent.
supportedInterfaces: four fields collapsed into one
In v0.x, reaching an agent meant assembling information from four places:
{
"url": "https://agents.example.com/a2a",
"preferredTransport": "JSONRPC",
"additionalInterfaces": [
{ "url": "https://agents.example.com/grpc", "transport": "GRPC" }
],
"protocolVersion": "0.3.0"
}
The primary endpoint lived in one field, its transport in another, alternatives in a third, and the protocol version applied to all of them at once — which was never quite true. v1.0 states everything per endpoint:
{
"supportedInterfaces": [
{ "url": "https://agents.example.com/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0" },
{ "url": "https://agents.example.com/grpc",
"protocolBinding": "GRPC",
"protocolVersion": "1.0" }
]
}
Three consequences worth internalising:
- Order carries meaning.
preferredTransportdisappeared because position replaced it — the first entry is the preferred interface. Reordering the array is a behavioural change. - Every entry needs all three keys.
url,protocolBindingandprotocolVersion. A partially specified entry is an error, not a fallback. - The vocabulary changed too.
transportbecameprotocolBinding, with core valuesJSONRPC,GRPCandHTTP+JSON. Custom bindings should be identified by a URI so they cannot collide with future core values.
The renames
| v0.x | v1.0 | Note |
|---|---|---|
supportsAuthenticatedExtendedCard | capabilities.extendedAgentCard | Moved inside capabilities, where the other feature flags live |
provider.name | provider.organization | The value is the organisation, not a contact person — the rename makes that explicit |
additionalInterfaces[].transport | supportedInterfaces[].protocolBinding | "Binding" because it names the protocol mapping, not the network transport |
What disappeared
capabilities.stateTransitionHistory is gone. It was a boolean claiming an
agent could report the history of a task's state changes, and it was
underspecified enough that clients could not rely on it. A v1.0 validator flags
it as not part of the specification.
If your agent really does expose task history, declare it as an extension
instead — that is what capabilities.extensions exists for, and an extension
carries a URI you can point at documentation:
"capabilities": {
"streaming": true,
"pushNotifications": true,
"extensions": [
{
"uri": "https://northwind.example.com/a2a/ext/task-history/v1",
"description": "Exposes the full state-transition history of a task.",
"required": false
}
]
}
Skill tags became required
In pre-1.0 drafts, tags on a skill were optional and widely skipped. In v1.0
they are required, because skill-level discovery filters on them. This is the
change most likely to turn an otherwise clean migration into a failing card,
since it affects every skill in every card written before the change.
// v0.x — accepted
{ "id": "reconcile", "name": "Reconcile", "description": "Matches payments." }
// v1.0 — tags are required
{ "id": "reconcile-payment-batch",
"name": "Reconcile a payment batch",
"description": "Given a bank statement export, match each line to an open
invoice and report exceptions with reasons.",
"tags": ["finance", "invoices", "reconciliation"] }
The discovery path moved
v1.0 registers /.well-known/agent-card.json as the standard
RFC 8615 discovery path.
The pre-1.0 path was /.well-known/agent.json, and it is still where every
older SDK looks first. The response should carry the media type
application/a2a+json.
GET /.well-known/agent-card.json -> 200 application/a2a+json GET /.well-known/agent.json -> 308 Location: /.well-known/agent-card.json
A card, before and after
{
"name": "Invoice Reconciliation Agent",
"description": "Matches payments to invoices.",
"url": "https://agents.northwind.example.com/a2a",
"preferredTransport": "JSONRPC",
"protocolVersion": "0.3.0",
"supportsAuthenticatedExtendedCard": false,
"provider": { "name": "Northwind Finance",
"url": "https://northwind.example.com" },
"capabilities": { "streaming": true,
"stateTransitionHistory": true },
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["application/json"],
"skills": [
{ "id": "reconcile", "name": "Reconcile",
"description": "Matches payments." }
]
}
{
"name": "Invoice Reconciliation Agent",
"description": "Matches incoming payments against open invoices, flags
partial and duplicate payments, and drafts follow-ups
for anything it cannot reconcile.",
"version": "3.1.0",
"supportedInterfaces": [
{ "url": "https://agents.northwind.example.com/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0" }
],
"provider": { "organization": "Northwind Finance",
"url": "https://northwind.example.com" },
"capabilities": {
"streaming": true,
"pushNotifications": false,
"extendedAgentCard": false
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["application/json"],
"skills": [
{ "id": "reconcile-payment-batch",
"name": "Reconcile a payment batch",
"description": "Given a bank statement export, match each line to an
open invoice and report exceptions with reasons.",
"tags": ["finance", "invoices", "reconciliation"] }
]
}
Note what the migration forced beyond the mechanical moves: a real version, a
description worth reading, and a skill with tags. That is not incidental — most
of the v1.0 changes push the card toward being useful to a stranger rather than
merely well-formed.
Migrating without stranding existing clients
If nobody is integrated with you yet, publish the v1.0 card at both paths and move on. If you have live pre-1.0 callers, you have three options, in increasing order of effort:
- Cut over. Serve the v1.0 card at both paths on a scheduled date and tell integrators. Honest, simple, and fine when you know who your callers are.
- Split by path. Serve the v1.0 card at
agent-card.jsonand keep the old document atagent.jsonfor a deprecation window. Two documents to keep in sync, so put an end date on it. - Serve a transitional card. Include
supportedInterfacesand retain the legacy top-levelurl, so v1.0 clients read the array and pre-1.0 clients read the field.
Migration checklist
- Replace
url,preferredTransportandadditionalInterfaceswith one orderedsupportedInterfaces[], preferred entry first. - Give every interface entry all three of
url,protocolBindingandprotocolVersion; renametransporttoprotocolBinding. - Set top-level
versionto your agent's build — not the protocol version you just moved. - Move
supportsAuthenticatedExtendedCardtocapabilities.extendedAgentCard. - Rename
provider.nametoprovider.organization. - Delete
capabilities.stateTransitionHistory, or re-model it undercapabilities.extensions. - Add non-empty
tagsto every skill, using the words a searcher would use. - Serve at
/.well-known/agent-card.jsonwith content typeapplication/a2a+json, and alias/.well-known/agent.json. - Run the card through the validator, then check the live domain with the well-known URL checker.
The schema reference documents every v1.0 field with its type, requirement level and the mistake most often made with it — useful as the target state while you work through the list above.
Frequently asked questions
Will a v0.x Agent Card still work with v1.0 clients?
Not usefully. A v1.0 client looks for supportedInterfaces to find your endpoint; a v0.x card has a top-level url instead, so the client finds no addressable interface. The card parses as JSON and conveys nothing actionable — which is worse than a hard failure, because it looks fine.
Where did protocolVersion go?
Into each entry of supportedInterfaces. The protocol version is now a property of an endpoint rather than of the whole agent, which is what lets one agent expose a v1.0 JSON-RPC interface and a differently versioned interface alongside it. The top-level version field means your agent build.
Do I have to serve both /.well-known/agent.json and agent-card.json?
You should. agent-card.json is the v1.0 path and the one new clients try; agent.json is where every pre-1.0 SDK looks. Serving the second as an alias or a 308 redirect to the first costs one route and removes an entire category of "why can nothing find my agent" reports.
What happened to stateTransitionHistory?
It was removed from capabilities in v1.0. If your agent genuinely exposes task state history, model it as an extension under capabilities.extensions rather than reintroducing the old boolean — a v1.0 validator flags the field as unrecognised.
Is A2A still a Google project?
It began at Google and is now developed under Linux Foundation governance with multiple vendors participating. Practically, that means the specification is versioned and published independently, which is exactly why pinning to a protocol version in each interface entry matters.
References
Related tool: Agent Card Validator
Next step
Find out which v0.x fields your card still carries.
The validator detects every pre-1.0 field, names its v1.0 replacement, and reports the rest of the card against the current specification.