A2A Agent Card — Mentionable v0.1
Status: v0.1 (planned for Phase 4)
This document defines how a Mentionable host publishes a domain-level A2A agent card at /.well-known/agent-card.json so that A2A-aware tooling (Google ADK, Claude.ai connectors, third-party A2A clients) can discover and invoke the host without prior knowledge of any specific @agent@domain address.
Mentionable’s per-agent agent-card.md is the resolution target of @local@domain via WebFinger. This document defines the adjacent surface: a hub agent card at the standard A2A discovery path, with mention-based routing inside the host so multiple agents stay reachable from a single A2A endpoint.
A2A spec assumes one domain = one agent. Mentionable assumes one domain = many. The hub model bridges those without breaking either side.
1. The hub model
A2A client ─────────► GET https://<domain>/.well-known/agent-card.json
│
▼ (single A2A AgentCard, hub-shaped)
│
A2A client ─────────► POST https://<domain>/a2a (the hub endpoint)
│
▼
┌────────────┐
│ hub router │
└────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
text starts text starts no @ mention
with @lean with @gamebld OR unknown handle
│ │ │
▼ ▼ ▼
lean@domain gamebuilder@ defaultAgent
domain
The hub is one A2A agent card describing the host as a whole, with a single A2A endpoint. Inbound messages are dispatched by parsing the leading @<handle> mention from the message text:
- First
@<handle>matches a registered agent → deliver to that agent. - No mention OR unknown handle → deliver to the host’s
defaultAgent.
Follow-up messages in an A2A conversation inherit contextId and route to the same agent that handled the first turn — no need to re-mention. (Same shape as Slack-reference’s follow-up handler from #154; the implementation is shared in spirit.)
1.1 Why mention routing
Most first-message scenarios already carry the address explicitly in the user-visible text:
“@lean — what’s the difference between Lean FIRE and Coast FIRE?”
A user typing this into a Claude.ai connector or an ADK chat panel writes the mention naturally. The router does the same parsing the rest of Mentionable already does (Pattern A in slack-connector, the shared parser in @mentionable/core). Without a mention, a default agent is the least-surprising fallback — an A2A client that just discovered the domain and asked “hello?” gets a coherent reply rather than an error.
1.2 Why a hub instead of one card per agent
A2A’s discovery contract pins /.well-known/agent-card.json (singular). An A2A client that GETs that path expects exactly one agent card. We can’t return an array there without breaking the contract. So we describe the host as a single hub agent that happens to dispatch internally.
Per-agent agent cards (one per registered Mentionable agent) stay reachable at /.well-known/agent-card/<local> — the canonical Mentionable resolution path that WebFinger already advertises (see agent-card.md and webfinger.md). The hub is additional discovery, not a replacement.
2. Payload
The card MUST conform to the A2A AgentCard shape. Mentionable-specific fields are added as JSON-LD-namespaced properties so A2A-vanilla parsers ignore them as unknown fields.
2.1 Single-agent host
When the host has exactly one registered agent, the hub IS that agent. The card inherits the agent’s name, description, and skills directly from its agent-card.md representation; the only material difference is the url (now the hub endpoint, not the per-agent endpoint) and the optional Mentionable extension.
{
"@context": "https://a2a-protocol.org/2025-06-18",
"name": "Lean FIRE Manager",
"description": "Financial independence coach.",
"url": "https://firemanager.info/a2a",
"skills": [
{
"id": "chat",
"name": "chat",
"description": "Natural-language chat with an LLM-backed agent.",
"input_modes": [{ "kind": "text", "mime": "text/plain" }],
"output_modes": [{ "kind": "text", "mime": "text/plain" }]
}
],
"https://mentionable.dev/ns/v1#defaultAgent": "lean",
"https://mentionable.dev/ns/v1#agents": [
{
"handle": "lean",
"name": "Lean FIRE Manager",
"card_url": "https://firemanager.info/.well-known/agent-card/lean"
}
]
}
2.2 Multi-agent host
When the host has more than one registered agent, the hub describes itself as a router. name is the org’s hub identity; description MUST clearly state the routing convention (Mention @<handle> to address...). skills SHOULD inherit the default agent’s skills so an A2A client can drive skills[0] against the hub URL and get a sensible response without having to learn the routing convention up front.
{
"@context": "https://a2a-protocol.org/2025-06-18",
"name": "Verse8",
"description": "Mentionable router. Mention @<handle> in messages to address a specific agent (assistant, gamebuilder). Without a mention, messages route to assistant.",
"url": "https://verse8.io/a2a",
"skills": [
{
"id": "chat",
"name": "chat",
"description": "Natural-language chat. Inherited from @assistant@verse8.io.",
"input_modes": [{ "kind": "text", "mime": "text/plain" }],
"output_modes": [{ "kind": "text", "mime": "text/plain" }]
}
],
"https://mentionable.dev/ns/v1#defaultAgent": "assistant",
"https://mentionable.dev/ns/v1#agents": [
{
"handle": "assistant",
"name": "Assistant",
"card_url": "https://verse8.io/.well-known/agent-card/assistant"
},
{
"handle": "gamebuilder",
"name": "Gamebuilder",
"card_url": "https://verse8.io/.well-known/agent-card/gamebuilder"
}
]
}
2.3 Field reference
| Field | Required | Notes |
|---|---|---|
@context | RECOMMENDED | A2A’s JSON-LD context. Tooling that doesn’t follow JSON-LD ignores it. |
name | Required by A2A | Org hub name (multi-agent) or default agent name (single). |
description | Required by A2A | For multi-agent hosts MUST describe the routing convention so a confused client + the user picking up the message can self-orient. |
url | Required by A2A | The hub A2A endpoint, e.g. https://<domain>/a2a. Per-agent endpoints stay at https://<domain>/a2a/<handle> and are discovered separately via WebFinger. |
skills | Required by A2A | Inherits the default agent’s skills. The hub itself has no skills of its own — it’s a router. |
version | Recommended | SemVer of the hub agent card. Bump when the routing semantics or extension shape changes. |
protocol_version | Recommended | '0.1' for Mentionable v0.1 publishers. |
https://mentionable.dev/ns/v1#defaultAgent | Required | Handle (slug, no @) of the agent that receives messages without a routable mention. MUST appear in mentionable:agents. |
https://mentionable.dev/ns/v1#agents | Required | Array of public agents on the host. Each item: handle (slug, required), name (display name, required), card_url (per-agent agent-card URL, recommended). |
https://mentionable.dev/ns/v1#routerType | Optional | "logic" (v1 default — first-wins per §3.2) or "llm" (v0.2 reserved). Absent = "logic". See §3.6. |
| Other A2A fields | As per A2A spec | auth, capabilities, input_modes, output_modes, etc. Inherit defaults from the hub’s underlying agent. capabilities.extensions[] (per agent-card.md §1.2) advertises PolicyPart v0.1 and other spec extensions; the hub SHOULD union the underlying agents’ declared extensions. |
3. Routing semantics
3.1 Mention parsing
The hub router parses the first @<handle> token from the inbound message’s text content. The grammar:
mention = "@" handle
handle = [a-z0-9_-]{1,30} ; lowercase only on the wire; handles in the
; agents[] list MAY use mixed case but the
; router lowercases before matching
Reuses the bare-mention parser from #178 / Pattern A — minus Slack-specific entity masking, which doesn’t apply to A2A messages. Implementations SHOULD share one parser between Slack-reference and the A2A router so behaviour stays consistent.
3.2 Match precedence
For each inbound message:
- Extract the first
@<handle>from the leading text part. - Lowercase the handle and look it up in the runtime’s registered agents.
- If hit → deliver to that agent.
- If miss OR no mention → fall through to §3.3.
The router scans for the first mention only. Subsequent @handle tokens in the same message text are NOT a routing signal — they are preserved verbatim in the message body so the chosen agent sees the full user intent and can decide what to do (acknowledge other agents, suggest a follow-up to them, attempt a relay; see §3.5).
v1 is deliberately “first wins”. Multi-agent fan-out as a hub-side responsibility is out of scope for v1 — see §3.5 for why and §8 for v0.2 directions.
3.3 Conversation continuity (mention-priority)
A2A’s contextId carries the conversation across turns. The router maintains a (contextId → agentHandle) mapping that the first turn populates. Mention presence on a follow-up overrides the sticky mapping:
| Follow-up message contains… | Routing decision |
|---|---|
A routable @<handle> | Route to that handle (and update the sticky mapping). |
| No routable mention | Use the sticky mapping (contextId → agentHandle). |
| Sticky mapping absent + no mention | Route to defaultAgent. |
This makes “first-message routing, follow-up inheritance, explicit re-mention to switch” the common path. The user’s mental model: address an agent the first time, follow up freely, re-address explicitly if you want to switch.
When the conversation switches agents mid-thread via re-mention, the new agent does NOT inherit the prior agent’s history — that history belongs to a different conversation by another party. The router treats the switch as a fresh first turn for the new agent. (Cross-agent context propagation is intentionally out of scope; see normalized-message.md for NormalizedMessage.history’s ownership model.)
Implementations MAY use a TTL on the sticky mapping; v1 RECOMMENDS the same 7-day idle timeout as ThreadStateStore.
3.4 Missing default
When defaultAgent is absent from mentionable:agents, the host is misconfigured. Routers MUST reject the publish at validation time (this is enforced by the validator in @mentionable/core).
When the defaultAgent field itself is missing from the card, A2A clients still see a valid hub card and can attempt to invoke skills[0] against url. The router MAY return a structured error (no_default_agent_configured) or fall back to the first agent in mentionable:agents. Implementations choose; this spec requires defaultAgent to be set so this case shouldn’t arise.
3.5 Multi-agent composition (when one message names two agents)
When a single message addresses more than one agent — e.g. @lean what would @gamebuilder say about this? — A2A’s request/response model gives one delivery and one response. Three patterns can compose multi-agent interactions on top:
(A) Caller-side fan-out. The A2A client splits the message and issues two message/send calls, one per agent. Each gets its own contextId. The client UI presents the two responses side-by-side. A2A spec aligns with this naturally — most general-purpose clients (a future Claude.ai connector with multi-agent intent, custom orchestration agents) SHOULD work this way. The hub doesn’t need to know.
(B) Hub-side fan-out (out of scope for v1). The hub fans out internally to the named agents, awaits both responses, and returns a single composite response on the original A2A request. Adds timeout + partial-failure surface to the hub. Reserved for v0.2 (see §8); the spec leaves room for this via the https://mentionable.dev/ns/v1#routerType field below.
(C) Agent-side relay (recommended for the reference implementation). The first-mentioned agent receives the full message text, sees the additional mentions, and decides what to do — acknowledge the others, suggest the user follow up with them, or itself open A2A connections to the named siblings and weave their replies into a single response. The hub’s mentionable:agents list is exactly the affordance an agent needs to discover its own siblings. A reference implementation (examples/llm-agent-vercel) ships with a system-prompt scaffold encouraging this behaviour, but the policy is the agent’s, not the hub’s.
For v1, the spec mandates only the hub’s “first wins” routing (§3.2). Patterns A and C are RECOMMENDED for clients and agents respectively; pattern B is a clearly-marked v0.2 extension point.
3.6 Optional routerType declaration (v0.2 hint)
The hub MAY declare its routing strategy via:
https://mentionable.dev/ns/v1#routerType : "logic" | "llm"
"logic"— first-wins per §3.2 (the v1 default; if the field is absent, this is implied)."llm"— the hub uses an LLM router that may fan out, summarize, or otherwise intelligently dispatch across agents. The cost / latency / determinism tradeoffs are the operator’s. Spec’d here so consumers (clients building their own UI affordances) can adapt expectations; implementation is reserved for v0.2.
A2A-vanilla clients ignore the field. Mentionable-aware clients SHOULD treat absence as "logic".
4. Endpoint coexistence
Decision recorded for Phase 4: per-agent endpoints (/a2a/<handle>) stay live. The hub endpoint (/a2a) is additional, not a replacement.
| Path | Audience | Status |
|---|---|---|
https://<domain>/a2a/<handle> | A2A clients that already have a specific address | Existing, unchanged |
https://<domain>/a2a (the hub) | A2A clients that just discovered the domain | New (this spec) |
https://<domain>/.well-known/agent-card/<handle> | Mentionable per-agent card resolution | Existing, unchanged |
https://<domain>/.well-known/agent-card.json | A2A standard discovery (the hub) | New (this spec) |
https://<domain>/.well-known/mentionable-agents.json | Mentionable-aware discovery (Layer 2) | Existing (#188 / #205) |
Operators who want to deprecate per-agent endpoints can do so at their own pace; nothing in this spec requires it.
5. Static hosting friendliness
The card at /.well-known/agent-card.json is single representation, JSON only — same constraint as mentionable-agents.json from agent-directory.md. An operator running the agent runtime elsewhere can serve a checked-in JSON file from S3, CloudFront, nginx try_files, or a static-site generator and still answer A2A discovery correctly.
The hub endpoint (/a2a) of course requires a function runtime — it’s where the router lives — but the discovery surface stays static-hostable. Mentionable is spec-first; the publish path remains accessible to deployments that split runtime from static hosting.
6. Mentionable extension shape
JSON-LD-namespaced fields:
https://mentionable.dev/ns/v1#defaultAgent : string // handle slug
https://mentionable.dev/ns/v1#agents : MentionableAgentRef[]
Where:
type MentionableAgentRef = {
/** Local part of the @<handle>@<domain> address. Lowercase. */
handle: string
/** Display name. Per-agent, so a multi-agent host's hub can render a list. */
name: string
/** Optional per-agent Mentionable agent-card URL (the WebFinger target). */
card_url?: string
/** Optional one-paragraph description, mirrors per-agent card.description. */
description?: string
}
A2A-vanilla parsers ignore the namespaced properties as unknown JSON-LD fields. JSON-LD-aware parsers reach the same fields via the term mentionable:agents if they pre-register the context, but most A2A tooling doesn’t, hence the fully-qualified URI shape.
The choice of JSON-LD namespacing (vs. an unprefixed extensions.mentionable object) follows the agent-directory.md precedent — Schema.org ContactPoint already accepts our mentionable: namespaced fields the same way.
7. Conformance
A publisher is conformant for v0.1 if:
https://<domain>/.well-known/agent-card.jsonreturns 200 withContent-Type: application/json.- The body satisfies the A2A spec for an AgentCard (
name,description,url,skills). https://mentionable.dev/ns/v1#defaultAgentis set and matches ahandleinhttps://mentionable.dev/ns/v1#agents.- Each
mentionable:agentsentry hashandleandname. - The
urlendpoint accepts inbound A2A messages and applies the routing rules in §3.
A consumer is conformant for v0.1 if:
- It treats the card as a valid A2A AgentCard regardless of whether it understands the
mentionable:extension. - It addresses follow-up messages to the same
urland trusts the hub to route bycontextId. - (Mentionable-aware) it can list and address sibling agents via
mentionable:agentswithout separate WebFinger lookups.
8. Non-goals for v0.1
- Hub-side multi-mention fan-out (pattern B in §3.5): the hub does not split one inbound message across multiple agents and stitch responses. Caller-side fan-out (pattern A) and agent-side relay (pattern C) are the v1 paths. The
mentionable:routerTypeextension field reserves room for a v0.2"llm"router that may take this on. - Cross-agent context propagation on a mention switch (§3.3): when a follow-up re-mentions a different agent, the new agent does not inherit the prior agent’s history. Conversations belong to one agent at a time.
- Aggregated skill listing: the hub inherits the default agent’s skills. Aggregating skills from every registered agent into one
skills[]is a fast-follow. - Capability filtering by agent: a multi-agent host’s hub presents one capability set. A consumer that wants the per-agent capability matrix follows
card_urlfor each. - Authentication on the hub endpoint: per the underlying agent’s policy. The hub doesn’t add auth; it forwards what’s there.
- Push subscriptions / streaming: per A2A spec for those features. Routing is at message-arrival time.
9. References
- A2A Specification
- A2A Agent Discovery
- agent-card.md — the per-agent card the hub references
- webfinger.md — per-agent address resolution
- agent-directory.md — Mentionable-aware Layer 1 / 2 discovery
- normalized-message.md — message shape the router dispatches