Agent Card validator

Check an A2A Agent Card against the v1.0 specification, signatures included, or a site's AI Catalog against its draft specification. Every result cites the section of the spec it comes from.

By Emissar. Updated .

A domain or https URL is checked at its well-known card paths; enter a /.well-known/ai-catalog.json URL to check the site's AI Catalog instead. Pasted JSON is checked as it is: as an AI Catalog when it has an entries member, otherwise as an Agent Card. Results are kept for 24 hours so you can share them; see privacy.

What it checks

Checks follow the released A2A v1.0 specification and its normative data model, a2a.proto. A failed check breaks a MUST or a required field; a warning breaks a SHOULD or is likely to trip up clients.

AreaCheckSource
DiscoveryLooks for the card at /.well-known/agent-card.json, then the legacy /.well-known/agent.json.A2A v1.0 §8.2
HTTPSThe card and every redirect on the way to it are served over HTTPS, and so is every interface URL.§7.1
HTTP cachingThe response has Cache-Control with a max-age, and an ETag.§8.6.1
Content typeThe response says application/json. The spec doesn't name a media type, so this is only ever a warning.RFC 8259 §11
Required fieldsname, description, version, supportedInterfaces, capabilities, defaultInputModes, defaultOutputModes and skills are present, have the right type, and required arrays aren't empty.§4.4.1, §5.7
InterfacesEach supportedInterfaces entry has a url, a protocolBinding (JSONRPC, GRPC, HTTP+JSON, or a URI for a custom binding) and a Major.Minor protocolVersion.§4.4.6, §5.8, §3.6
Capabilitiesstreaming, pushNotifications and extendedAgentCard are true or false, and extensions are well formed.§4.4.3, §4.4.4
SkillsEach skill has a unique id, a name, a description and tags; examples, inputModes and outputModes have the right shape when present.§4.4.5
Media typesDefault and per-skill input and output modes are type/subtype media types.§4.4.1
Provider and linksprovider has organization and url; documentationUrl and iconUrl are https URLs.§4.4.2
SecuritysecuritySchemes use the v1.0 shape (one wrapped scheme per entry), and securityRequirements only name declared schemes.§4.5
Field namesField names are camelCase. Fields the spec doesn't define are flagged, since they are often typos.§5.5, §5.7
SignaturesEach JWS is decoded (alg, kid, typ, jku), the key is fetched from jku, and the signature is checked over the RFC 8785 canonical card with signatures and default values removed.§8.4, RFC 7515, RFC 8785
v0.3 cardsCards with url and preferredTransport instead of supportedInterfaces are labeled v0.3, checked against the v0.3 schema, and flagged for upgrade.A2A v0.3 §5.5, §5.6
AI CatalogFor a /.well-known/ai-catalog.json URL or pasted catalog JSON: specVersion and entries, each entry's identifier, type and exactly one of url or data, unique identifiers (with their versions), host and publisher shapes, and the media type it was served as. The result names the conformance level it meets; catalog signatures and trust manifests are reported, not verified.AI Catalog §4, §9, §10

How it works

  • Requests come from Emissar's servers on Cloudflare, not from your browser, with the user agent EmissarTools/0.1 (+https://emissar.ai/tools). Requests aren't signed with Emissar's key: anyone can point this tool at any site, so only our crawler signs (how EmissarBot's signatures work).
  • Only public domain names over https on port 443 are fetched. IP addresses, localhost, .local, .internal and single-label names are refused.
  • A URL is reduced to its host: only /.well-known/agent-card.json and /.well-known/agent.json are requested. If you enter the legacy path, only that path is tried. If you enter /.well-known/ai-catalog.json, only the catalog is requested, and the entries it lists aren't.
  • Redirects are followed by hand, at most 3, and every hop goes through the same checks. Each request times out after 5 seconds, and a response over 256 KB is dropped.
  • Each host is fetched at most once every 30 seconds for its card and once for its AI Catalog. Asking again sooner returns the previous result. Key-set hosts have the same limit: if another check contacted one in the last 30 seconds, that signature is reported as not checked, not as a failure, and the result isn't stored, so check again a little later.
  • Signatures: ES256, ES384, RS256, PS256 and EdDSA (Ed25519) are supported. The public key comes only from the jku in the signature's protected header and is fetched under the same rules. The canonical form is checked against the RFC 8785 test vectors and against cards signed with the official A2A Python SDK.
  • For emissar.ai itself, the validator reads the card from this site's own route rather than fetching it.
  • Limits: the first 100 entries of any list in the card (skills, interfaces, extensions, security schemes and requirements) are checked, and a result lists at most 400 checks. When a card goes past either, a warning says what wasn't checked.

API

The page uses a public endpoint you can call directly. It is rate limited per IP address (per calling zone for requests from other Cloudflare Workers, which share one address), and the response is the same JSON as the downloadable report.

curl -s https://emissar.ai/api/tools/validate-card \
  -H 'Content-Type: application/json' \
  -d '{"input":"example.com"}'

# Open a stored result (kept for 24 hours)
curl -s https://emissar.ai/api/tools/validate-card/RESULT_ID

# AI Catalog: a domain (its /.well-known/ai-catalog.json is read) or catalog JSON
curl -s https://emissar.ai/api/tools/validate-catalog \
  -H 'Content-Type: application/json' \
  -d '{"input":"example.com"}'
curl -s https://emissar.ai/api/tools/validate-catalog/RESULT_ID

Limitations

  • It checks the card, not the agent. It doesn't call the endpoints in supportedInterfaces.
  • It can't tell whether a signing key has expired or been revoked; a JWK Set doesn't say. A valid signature shows who holds the key, not whether you should trust them.
  • Signatures without a jku rely on a key store the client already trusts, so they are reported but not verified.
  • Fields the spec doesn't define are kept when the card is canonicalized. If the signer dropped them before signing, the signature won't verify.
  • Cards are read with standard JSON parsing, so duplicate keys collapse to the last value and integers beyond 253 lose precision.
  • Extended cards behind authentication and cards at other paths aren't fetched.
  • An AI Catalog check doesn't follow the catalog's entries: check each listed card on its own. Catalog signatures and trust manifests (the specification's Level 3, Trusted Catalog) are reported but not verified, so a catalog is rated Level 2 at most. The AI Catalog specification is a draft and may change.
  • The per-host limit is stored in Cloudflare Workers KV, which is eventually consistent, so two requests at the same moment in different regions can both go through.

Privacy

When you check a domain or URL, we keep its host name for about a minute to enforce the per-host limit. Every complete result, including results for pasted JSON, is stored under a random id for 24 hours so the link works, then deleted automatically; a result with a signature that couldn't be checked yet isn't stored. A result contains the checks and a copy of the card or catalog they ran on.

Anyone with a result link can open it until it expires, so don't paste a card that contains secrets. Key sets fetched to check signatures aren't stored, and responses from the sites we check aren't written to logs. As with any request to this site, Cloudflare processes your IP address to deliver and protect it. Details are in the trust center and the privacy policy.

Sources