Agent Card validator

Check an A2A Agent Card against the v1.0 specification, signatures included. 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. Pasted JSON is checked as it is. 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

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.
  • 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. 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

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.
  • 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 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