What Is an Agent Card and How Do You Implement It in 2026?
An agent card is the JSON file an agent fetches first to learn what your service can do and how to call it. This page gives you the exact v1.0 field set, three working ways to publish it, and the mistakes that make a card unreadable.
Agent Card (/.well-known/agent-card.json) is the JSON manifest an A2A server publishes so that other agents can discover its identity, skills, protocol endpoints and authentication requirements before sending a single request. The A2A specification requires every A2A server to make one available, and agent-card.json is registered with IANA as a permanent well-known URI suffix under the Linux Foundation. If you are exposing an agent or an API for other agents to call, this file is the thing that makes you callable without a bilateral integration.
Why Agent Card matters for AI visibility
Agents do not crawl your site to work out whether you are worth calling. They request one fixed path, parse the JSON, and either add you to their routing table or move on. Because the path is a registered well-known URI rather than a convention, a client written against the specification knows exactly where to look on any domain, and a missing or malformed file is a silent disqualification rather than a soft ranking penalty. This is a harder gate than most GEO surfaces: there is no partial credit for a card that fails to parse.
The card is also your contract surface. The skills array, with its tags and examples, is what a routing agent matches an incoming user request against, so a card listing one vague skill loses to a card listing four specific ones with realistic example prompts. Everything else on the card, the transports in supportedInterfaces, the schemes in securitySchemes, and the optional JWS signatures, decides whether the calling agent can connect at all and whether it should trust what it just read.
Where the spec lives
The card is defined by protobuf in the A2A repository and rendered into prose on the specification site, so the proto file is the authority when the two disagree.
- A2A Protocol Specification, section 8 - the normative text on discovery, protocol declaration, card signing and caching, plus a full sample card in section 8.5
- a2a.proto - the canonical field definitions; the REQUIRED annotations here are what implementations validate against
- a2a.json JSON Schema bundle - a generated 2020-12 schema you can point Ajv at in CI to validate your card on every deploy
- Agent Discovery guide - when to use the well-known URI versus a registry or direct configuration, and how to lock down the endpoint
- IANA Well-Known URIs registry - proof the path is permanent, not a convention; look for the agent-card.json row
- RFC 8785 JSON Canonicalization Scheme - required reading before you sign a card, because signing the pretty-printed file will not verify
Three ways to implement Agent Card
Start with a static file if you have one agent and one endpoint: it is a text file and it is done in ten minutes. Move to serving the card from the application when the endpoint URL differs per environment or the skill list is generated from code, because that is where hand-maintained cards drift out of date. Add a JWS signature when other organisations call your agent and need to prove the card they fetched is the one you published.
Static file: the minimum valid card
Every field below is REQUIRED in A2A v1.0 except provider. Arrays marked required must carry at least one element, so an empty skills array is not a valid card. Drop this at the root of the origin your agent is reachable on.
{
"name": "Northwind Support Agent",
"description": "Answers order and returns questions for Northwind customers, and files support tickets on their behalf.",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https://api.example.com/a2a/v1",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"provider": {
"organization": "Northwind Traders",
"url": "https://www.example.com"
},
"capabilities": {
"streaming": false,
"pushNotifications": false
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json"],
"skills": [
{
"id": "order-status",
"name": "Order status lookup",
"description": "Returns the current shipping status for an order given its reference number.",
"tags": ["orders", "shipping", "support"],
"examples": ["Where is order NW-10422?", "Has NW-10422 shipped yet?"]
}
]
}
What this does: A client fetches this file, reads the first entry of supportedInterfaces as your preferred transport, and can immediately open a JSON-RPC session against that URL. The examples strings are what a routing agent compares an incoming user request against, so write them as real prompts rather than as documentation.
Serve it from the app so it cannot drift
Use this when the endpoint URL changes per environment, or when you want the card to carry cache validators. The specification asks servers to send a Cache-Control max-age and an ETag derived from the card version or a hash of its content, so clients can revalidate with If-None-Match instead of re-downloading.
// server.js - npm i express
const express = require('express');
const { createHash } = require('node:crypto');
const { readFileSync } = require('node:fs');
const app = express();
const ORIGIN = process.env.PUBLIC_ORIGIN || 'https://api.example.com';
// Build the body once at boot so the ETag is stable across requests.
const card = JSON.parse(readFileSync('./agent-card.json', 'utf8'));
card.supportedInterfaces = [
{ url: `${ORIGIN}/a2a/v1`, protocolBinding: 'JSONRPC', protocolVersion: '1.0' },
];
const body = JSON.stringify(card);
const etag = `"${createHash('sha256').update(body).digest('base64url').slice(0, 27)}"`;
function sendCard(req, res) {
res.set({
'Content-Type': 'application/json; charset=utf-8',
'Cache-Control': 'public, max-age=3600',
'Access-Control-Allow-Origin': '*',
ETag: etag,
});
if (req.headers['if-none-match'] === etag) return res.status(304).end();
res.send(body);
}
app.get('/.well-known/agent-card.json', sendCard);
// Pre-1.0 clients still probe the old path. Keep it while they are in your logs.
app.get('/.well-known/agent.json', sendCard);
app.listen(8080);
What this does: The body and its ETag are computed once at boot, so repeat fetches from the same client cost a 304 rather than a full transfer. The second route keeps the pre-0.3 path /.well-known/agent.json answering for older clients, which is a local compatibility decision: the v1.0 specification does not mention that path at all.
Sign the card with a detached JWS
Signing is optional in the specification, but clients SHOULD verify at least one signature before trusting a card, so signing is what lets a cautious caller accept yours. The payload has to be canonicalised with RFC 8785 and the signatures field itself excluded, otherwise the verifier rebuilds different bytes and the check fails.
// sign-agent-card.mjs - npm i jose canonicalize
import { readFileSync, writeFileSync } from 'node:fs';
import canonicalize from 'canonicalize';
import { FlattenedSign, importPKCS8 } from 'jose';
const ALG = 'ES256';
const KID = 'agent-card-2026-01';
const JKU = 'https://www.example.com/.well-known/jwks.json';
const card = JSON.parse(readFileSync('agent-card.json', 'utf8'));
delete card.signatures; // never sign the signatures field
const payload = new TextEncoder().encode(canonicalize(card)); // RFC 8785
const key = await importPKCS8(readFileSync('signing-key.pem', 'utf8'), ALG);
const jws = await new FlattenedSign(payload)
.setProtectedHeader({ alg: ALG, typ: 'JOSE', kid: KID, jku: JKU })
.sign(key);
card.signatures = [{ protected: jws.protected, signature: jws.signature }];
writeFileSync('agent-card.json', JSON.stringify(card, null, 2));
console.log('signed with', KID);
What this does: It writes a signatures array back into the card holding the base64url protected header and the signature, with the payload left out because the verifier reconstructs it from the card. Publish the matching public key at the jku URL as a JWKS so clients can resolve the kid without contacting you.
Implementation guidelines
These are the failures that show up once a card is live and other people's clients are parsing it.
- Get the path exactly right. It is
/.well-known/agent-card.jsonat the root of the origin, served over HTTPS with a 200 and a JSON content type. A redirect chain, an HTML error page returned with status 200, or the file parked one directory deeper all read as no card at all. - Treat the required list as required.
name,description,version,supportedInterfaces,capabilities,defaultInputModes,defaultOutputModesandskillsare all marked REQUIRED in the proto.provider,documentationUrl,iconUrl,securitySchemes,securityRequirementsandsignaturesare optional. - Order supportedInterfaces by preference. The first entry is the preferred interface and clients select the first one they support. Each entry needs its own
url,protocolBinding(JSONRPC,GRPCorHTTP+JSON, or a URI for a custom binding) andprotocolVersion. If you settenanton an entry, clients must echo that exact value on every request to that interface. - Version the card and the validator together. Bump
versionwhenever the skills or interfaces change, and derive the ETag from that value or from a hash of the body. Clients cache cards aggressively, so a skill you added without a version bump can take a day to be noticed. - Keep the public card public. Internal hostnames, rate limits, unreleased skills and anything customer-specific belong in the extended card, which is fetched only after authentication. Declare it with
capabilities.extendedAgentCard: trueand serve it atGET /extendedAgentCardon the REST binding. - Migrate off the 0.3 shape deliberately. Version 1.0 removed the top-level
url,protocolVersionandsupportsAuthenticatedExtendedCardfields and dropped thekinddiscriminator on polymorphic objects. The published JSON Schema setsadditionalProperties: false, so leftovers from an older card are hard validation errors, not harmless extras. - Validate on every deploy. Point Ajv at the generated schema bundle and assert your card against
#/$defs/AgentCardin CI. It is a five-line test and it catches the whole class of typos that make you silently invisible to callers.
Do this, not that
Do
- Give every skill a stable
id, at least onetagsentry, andexampleswritten as prompts a user would actually type. - Send
Cache-Control: public, max-age=3600and anETag, and answerIf-None-Matchwith a 304. - Describe how to authenticate in
securitySchemes, using the wrapper key that matches the scheme, such ashttpAuthSecuritySchemeoropenIdConnectSecurityScheme. - Keep
/.well-known/agent.jsonanswering as an alias for as long as pre-1.0 clients still appear in your access logs.
Do not
- Do not leave a top-level
urlorprotocolVersionon the card. Both moved insidesupportedInterfacesin v1.0 and the schema rejects unknown properties. - Do not declare
supportsAuthenticatedExtendedCard. It is nowcapabilities.extendedAgentCard. - Do not sign the pretty-printed file. Canonicalise with RFC 8785 and remove
signaturesfirst, or no client will be able to verify it. - Do not advertise skills you cannot route to. A skill that returns an error on its own
examplesprompt costs you more trust than omitting it would.
How Glippy checks this
Glippy requests /.well-known/agent-card.json at the root of the domain once per crawl, with a five second timeout, and scores the result under Agent Interactivity, category 10. A response that parses as JSON and carries name, description and a version field scores the full four points, and Glippy reports how many entries your skills array holds. A card that responds but is missing those fields scores two and is raised as a warning. No card at all is reported as information rather than a penalty, because most pages are not agent hosts, but if a UCP profile on the same domain advertises a2a while the card returns 404, that contradiction is flagged as a warning. The same discovery sweep also looks for an MCP server card, an agent skills index and a schemamap, and any one of them is enough to mark the domain as having an agent discovery surface. You can see the whole category on the AI agent accessibility checker.
Check your Agent Card setup
Glippy runs 240+ checks across 16 categories on any page, including Agent Interactivity (category 10). No sign-up required.
Frequently asked questions
/.well-known/agent-card.json is the current and only path named in the A2A specification, and it is the suffix registered with IANA. The older /.well-known/agent.json was the pre-0.3 location; the v1.0 specification does not mention it, and the A2A Python SDK carried a legacy constant for it at version 0.3.0 but removed it by 1.0.0. Publish the new path as the real card and keep the old path as an alias only if you can still see clients requesting it.
Eight fields are marked REQUIRED in the A2A proto: name, description, version, supportedInterfaces, capabilities, defaultInputModes, defaultOutputModes and skills. Arrays marked required must contain at least one element, so an empty skills or supportedInterfaces array is invalid. Each AgentSkill in turn requires id, name, description and tags, with examples, inputModes and outputModes optional.
No. The A2A specification says cards MAY be signed using JWS as defined in RFC 7515, and that clients SHOULD verify at least one signature when signatures are present. If nobody outside your organisation calls your agent, an unsigned card over HTTPS is fine. If they do, signing is what lets a careful client distinguish your card from one served by a compromised CDN or an impostor domain, and multiple signatures are allowed so you can rotate keys without a gap.
They describe different things and they are at different levels of maturity. An agent card describes an agent as a peer you delegate a task to, and its path is a permanent registered well-known URI, so it is safe to build against. MCP describes tools and resources a model calls directly, and its equivalent discovery document is still a proposal: the server card work under SEP-1649 and SEP-2127 is being developed as an experimental extension, with the endpoint convention still moving. Publish an agent card today; treat an MCP server card as something to revisit when the proposal settles.
Reviewed against the primary sources on . These standards move quickly, so check the linked specs before you ship.