Before two agents can cooperate, one has to find the other. The A2A protocol borrows a trick that browsers and TLS clients have used for years: a well-known URI at a predictable path. Drop an agent.json file under /.well-known/ on your domain, and any peer can fetch it, read your capabilities, and start a conversation. No registry required, no proprietary handshake, no client library.
It sounds simple. It is almost simple. But the details of the schema, the capability negotiation that follows, and the security model are where real deployments get hurt. This article walks through all of it, using the Inbox Check agent card as a worked example.
An agent card is a JSON document served at https://your-domain/.well-known/agent.json. It describes who the agent is, what it can do, how to authenticate, and what transport to use. A2A clients fetch this card as the first step of any conversation.
RFC 8615 — the .well-known convention
The /.well-known/ prefix was standardised in 2014 by RFC 8615. The idea is small and powerful: if every protocol that needs a discovery endpoint uses the same prefix, operators do not have to negotiate URI real estate with every new spec. TLS uses it for ACME challenges. OAuth uses it for openid-configuration. Mastodon uses nodeinfo. Apple uses apple-app-site-association.
A2A reserves agent.json at that prefix. The file must be served over HTTPS, must respond with Content-Type: application/json, and must be fetchable without authentication. That last point matters: discovery is public by definition. If you need private agents, do not publish a card — hand a signed URL to your partner instead.
The agent.json schema
A minimal valid card has four top-level fields: name, version, capabilities, and transport. A production card adds description, auth, contact,homepage and a handful of optional metadata fields. The schema is intentionally small — the point of discovery is to be trivially parseable.
Top-level fields
name— human-readable agent name. Shown to users when they approve a connection.version— semver for the agent card itself, not the underlying service. Bump this when the card changes, not when your backend changes.description— one or two sentences. LLMs read this when deciding whether to call you.homepage— optional URL for humans. Not used by agents.contact— optional object withemailandurlfor the maintainer.
Capabilities array
The capabilities array is the heart of the card. Each entry names one thing the agent can do, with a short description and an optional input schema. The client uses this to decide which method to invoke and what arguments to send.
"capabilities": [
{
"name": "start_test",
"description": "Starts an inbox placement test against our seed mailboxes.",
"input_schema": { "$ref": "#/schemas/StartTestInput" }
},
{
"name": "get_test",
"description": "Fetches the verdict for a given test ID.",
"input_schema": { "$ref": "#/schemas/GetTestInput" }
}
]Auth section
Three flavours are common. none for public read-only agents. api_key for simple bearer-token services. oauth2 for multi-tenant integrations. The card describes the flow; the client follows it. If you support multiple schemes, list them in order of preference.
Transport section
A2A permits HTTP/JSON-RPC, HTTP/REST-over-JSON, and WebSocket streams. The card tells the client which endpoint to hit and which verbs to use. If you have an MCP bridge, advertise that in a separate entry — some clients prefer MCP when it is available.
Versioning the card
The version field uses semver. Any breaking change to a capability schema — renamed field, tightened type, removed capability — is a major bump. Added fields with safe defaults are minor. Fixes to the description text are patch. Clients are expected to cache the card for up to 24 hours; a major bump should coincide with a cache bust header or a rotation of the agent name.
Worked example: Inbox Check card
Here is an abbreviated version of the card we serve at https://check.live-direct-marketing.online/.well-known/agent.json:
{
"name": "Inbox Check",
"version": "1.2.0",
"description": "Free inbox placement tests. SPF/DKIM/DMARC, per-provider folder placement, DNSBL checks.",
"homepage": "https://check.live-direct-marketing.online",
"contact": { "email": "agents@live-direct-marketing.online" },
"auth": [
{ "type": "none", "scopes": ["read:public"] },
{ "type": "api_key", "header": "X-API-Key", "scopes": ["run:test", "read:history"] }
],
"transport": [
{ "type": "jsonrpc", "url": "https://check.live-direct-marketing.online/api/a2a" },
{ "type": "mcp", "package": "ldm-inbox-check-mcp" }
],
"capabilities": [
{ "name": "start_test", "description": "Start a new placement test." },
{ "name": "get_test", "description": "Fetch a verdict by test ID." },
{ "name": "list_providers", "description": "List current seed mailboxes." },
{ "name": "check_auth", "description": "Run SPF/DKIM/DMARC for a domain." },
{ "name": "check_blacklist","description": "Query 50+ DNSBL lists for an IP." }
]
}How a peer fetches and parses the card
A correctly-behaved A2A client follows five steps before it sends its first real request:
- Resolve domain. The user or upstream agent provided a hostname. The client fetches
https://<host>/.well-known/agent.json. - Validate certificate. Cards served over plain HTTP or with broken certificates must be rejected. This is the first line of defence against spoofed agents.
- Parse and version-check. If the client supports the major version in the card, continue; otherwise fail with a clear error.
- Pick a transport and auth. Prefer MCP if supported; otherwise pick the first advertised transport the client can speak.
- Cache. Respect standard HTTP caching headers.
Cache-Control: max-age=86400is a common value.
Discovery at scale
Static well-known files cover one case: the agent's domain is already known. For everything else — agent marketplaces, autonomous scouting, cross-org directories — you need a registry on top. Two approaches are emerging in 2026:
- Curated directories. Projects like a2a-directory.org collect cards from public agents, verify ownership, and expose a search API. The equivalent of npm for agents.
- Peer-to-peer discovery. An agent you already trust publishes a list of agents it trusts. You walk the graph. This is how federated networks like ActivityPub scale discovery, and it will probably be how the A2A graph grows.
Finding an agent is not the same as trusting it. A valid card at a valid domain proves only that someone controls that domain. For high-stakes work you still need out-of-band verification — a public registry entry, a signed card, a shared secret provisioned by a human.
Security considerations
Agent discovery opens attack surfaces that REST APIs have never had to worry about. The big three:
Spoofed agent cards
An attacker who compromises a subdomain (agent.acme.com) can publish their own agent.json and impersonate Acme's capabilities. Mitigation: serve cards only from the apex or a small set of approved subdomains, and publish a CNAME-anchored CAA record so only your CA can issue certificates for it.
MITM on card fetch
If a client follows a plaintext HTTP redirect or ignores a bad cert, a network attacker can substitute a malicious card. Mitigation: the A2A spec requires HTTPS and a valid cert; no sane client should accept anything else. HSTS on the card domain is strongly recommended.
Scope creep in capabilities
An agent card can advertise capabilities the operator never intended to expose — for example, a debug method that reaches into internal systems. Mitigation: generate the card from a whitelist, not from your method registry. Review it before each deploy. Never auto-expose every function your agent can do.