AAP as an A2A profile
This page describes the editable next-major contract, not the frozen v1.3 release. Example extension and schema URLs use the non-routable draft.autoagentprotocol.invalid namespace; release preparation replaces them with approved version-pinned URLs. Do not send draft identifiers to a production agent. See migration guidance.
The Auto Agent Protocol is a strict profile of A2A v1.0. It does not redefine discovery, message envelopes, the task model, or transport. It only constrains the shape of one specific A2A construct: typed DataParts carried inside Message.parts[].
The AAP profile targets the A2A v1.0.x line, including A2A v1.0.1 — a non-breaking patch that changed no AgentCard, AgentSkill, or message field. Per A2A §3.6 the agent card advertises the Major.Minor version only, so AAP cards keep protocolVersion: "1.0" (patch numbers SHOULD NOT be sent and MUST NOT affect version negotiation). A2A v1.0.1's one transport nudge — preferring application/a2a+json on the HTTP+JSON binding — does not apply to AAP, which uses JSON-RPC 2.0 exclusively. AAP publishes each skill's request/response JSON Schema URLs in capabilities.extensions[].params (a free-form A2A Struct) rather than on the AgentSkill object, because A2A's AgentSkill has no schema field in v1.0 or v1.0.1, and capabilities.extensions[].params is the place A2A designates for extension-specific configuration.
The error-envelope and required-extension corrections described here require a major release after v1.3.0. They do not amend the frozen v1.3 contract; see migration guidance.
The three layers of A2A
A2A is structured in three layers. AAP sits as a profile that constrains layer 1 (data model).
A2A defines several bindings; AAP rides on exactly one — JSON-RPC 2.0 — with the same data model on every call.
Where AAP fits
AAP is a layer 1 profile. It defines:
-
Standard skill vocabulary. Five canonical
skills[].idvalues an AAP-compliant agent card draws from:dealer.information,inventory.facets,inventory.search,inventory.vehicle,lead.submit. An agent declares the subset it actually implements (one or more); none is individually mandatory. AAP RECOMMENDS at leastinventory.search+lead.submitfor an end-to-end shopping flow. -
Typed
DataPartpayloads. For each skill, an exact request and response JSON Schema. Each payload includes atypefield whose value is<scope>.<thing>.requestor<scope>.<thing>.response(e.g.inventory.search.request). The AAP version is announced once via the agent-card extension URI; it is not repeated on the wire. -
An extension URI.
https://draft.autoagentprotocol.invalid/extensions/aap/latest, declared incapabilities.extensions[]of the agent card and markedrequired: true.A2A splits extensions into data-only ones, which add metadata and leave the request/response flow alone, and profile extensions, which overlay structure on the core messages — A2A's own illustration of the class is "requiring all messages to use
DataPartsadhering to a specific schema". That is AAP exactly. A2A says only that agents shouldn't mark data-only extensions as required; it does not say profile extensions must be required, and it cautions thatrequired: true"creates a hard dependency for all clients". AAP accepts that dependency deliberately: an AAP dealer agent's core function is serving AAP-schemaDataParts, and a caller that has not accepted the profile cannot be served as an AAP client. The flag is therefore load-bearing rather than decorative — a buyer agent activates the profile with theA2A-Extensionsheader on every call, which A2A defines as the client having to "understand and comply with the extension's requirements" (a2a.proto,AgentExtension.required, normative per A2A §1.4), and a dealer agent MUST reject a request that does not withExtensionSupportRequiredError(A2A §3.3.4). See request headers.
AAP does NOT redefine layer 2 (abstract operations) or layer 3 (protocol bindings) — it deliberately uses a minimal slice of each. AAP uses exactly one A2A operation: SendMessage (the message-only pattern — request Message in, response Message out).
The message-only pattern is sanctioned by A2A, not a departure from it. A2A §3.1.1 lists both outputs for SendMessage — a Task, "OR ... a direct response message (for simple interactions that don't require task tracking)" — and its Behavior clause says the agent "MAY return a direct Message response for simple interactions". §3.7's "Messages SHOULD NOT be used to deliver task outputs" governs the output of a Task; an AAP dealer creates no tasks, so no Artifact is owed. This is why a buyer agent never has to implement the tasks surface to read an AAP result. The optional A2A surface (SendStreamingMessage, the tasks Get/List/Cancel/Subscribe operations, push notification configs, GetExtendedAgentCard) is out of scope for AAP — dealer agents do not need to implement it, and buyer agents MUST NOT require it. On bindings: JSON-RPC 2.0 is the sole binding AAP defines — a JSON-RPC interface is REQUIRED on every AAP agent card. The HTTP+JSON (REST) binding was removed in v1.1.0, and gRPC is out of scope.
The typed DataPart pattern
A2A messages are composed of one or more parts. Each part identifies its kind by the member it carries — the v1.0 Part is a oneof over text, raw (inline bytes), url (a file reference), and data; a part carrying data is a DataPart. AAP only uses DataParts — it never relies on free-text natural-language parsing for protocol semantics.
A DataPart looks like this:
{
"data": {
"type": "inventory.search.request",
"filters": { "make": ["Honda"] },
"pagination": { "skip": 0, "limit": 20 }
},
"mediaType": "application/vnd.autoagent.inventory-search-request+json"
}
The type field is the AAP-typed identifier (e.g. inventory.search.request). Every AAP request and response carries a type matching the regex ^[a-z_]+(\.[a-z_]+){1,2}$. This lets a buyer agent or middleware validate the payload against the right schema without inspecting the surrounding A2A envelope. The mediaType field on the part advertises the AAP media type so generic A2A middleware can route or filter parts without parsing the inner data.
AAP rides on A2A v1.0, whose single canonical wire format is the ProtoJSON form: the method is SendMessage, Role is the enum name "ROLE_USER" (buyer agent) / "ROLE_AGENT" (dealer response), and a Part has no kind discriminator (it is typed by the member it carries — AAP uses the data member). The Message has no kind discriminator either. A compliant AAP agent MUST emit and accept this form so any A2A v1.0 client and the published A2A SDKs (a2a-js, a2a-python) can parse its replies. Every Message carries a unique messageId. Every server response Message MUST also include a nonempty contextId, including direct responses that create no Task; clients may omit it on an initial request.
Concrete example: inventory.search
A full A2A Message carrying an AAP request:
{
"messageId": "01HZ9Q2V5L8F1U3ABV6K1ETBDEX",
"role": "ROLE_USER",
"parts": [
{
"data": {
"type": "inventory.search.request",
"filters": {
"make": ["Honda"],
"condition": ["used", "cpo"],
"year_min": 2020,
"price_max": 30000
},
"pagination": { "skip": 0, "limit": 20 },
"sort": { "field": "price", "order": "asc" },
"privacy": { "anonymous": true }
},
"mediaType": "application/vnd.autoagent.inventory-search-request+json"
}
]
}
The dealer agent replies with an A2A Message containing the AAP response:
{
"messageId": "01HZ9Q2W9SH5ZB6DUA0J1K2L3M",
"contextId": "ctx_example_001",
"role": "ROLE_AGENT",
"parts": [
{
"data": {
"type": "inventory.search.response",
"data": {
"total": 1,
"skip": 0,
"limit": 20,
"vehicles": [
{
"dealer_id": "dealer_demo_toyota",
"vin": "1HGCY2F57RA000001",
"stock": "T12345",
"year": 2022,
"make": "Honda",
"model": "Civic",
"trim": "EX",
"condition": "cpo",
"status": "available",
"list_price": 24990,
"price": 26780,
"fees": [
{ "name": "Documentation fee", "amount": 500 },
{ "name": "Pre-installed theft protection", "amount": 1290 }
],
"inventory_date": "2026-04-12",
"updated_at": "2026-04-30T10:15:00Z"
}
]
}
},
"mediaType": "application/vnd.autoagent.inventory-search-response+json"
}
]
}
The outer envelope around this Message is the JSON-RPC 2.0 binding — AAP's sole transport. See JSON-RPC binding for full envelope examples.
Why typed DataParts instead of natural-language parts
An AI agent could in principle stuff a search query into a TextPart and let the dealer's LLM parse it. AAP does not allow that for protocol calls because:
- Buyer agents need deterministic schemas to plan and validate.
- Dealer CRMs need structured data to write to ADF/XML and downstream lead pipes.
- Pricing and consent are regulated; ambiguous natural language increases compliance risk.
- Validation tooling (Ajv, ajv-formats, OpenAPI clients) only works on structured payloads.
Protocol calls use typed JSON DataParts ONLY; dealer agents MUST ignore any TextParts. AAP defines no protocol semantics for free-text parts — only the typed DataPart is normative, and a dealer agent MUST NOT derive any protocol meaning from a TextPart.
Discovery and bindings: the rest of the profile
AAP layers one more piece on top of the base A2A surface:
| Piece | Where it lives | Purpose |
|---|---|---|
| Agent card with AAP extension | /.well-known/agent-card.json | A2A discovery; declares the AAP extension URI and lists the subset of AAP skills the agent implements. |
| Binding section | A2A Section 9 | How AAP DataParts ride inside JSON-RPC 2.0 envelopes — the sole AAP binding, REQUIRED on every AAP agent card. |
See:
- Discovery for the agent card.
- JSON-RPC binding for the wire format.