Check A2A agent cards
Last updated 7 November 2026
For teams that publish an A2A agent or call the agents of other teams. At the end you will know what is checked on an agent card, where Spellwarden finds cards in a repository, how a remote card is probed on request and where to read the results.
Plans: All plans
What an agent card is and why it is checked
A2A is the agent-to-agent protocol. An A2A agent publishes an agent card, a JSON document that says where the agent is reached, which sign-in flows it accepts, what it can do and, optionally, a signature over all of it. A client reads the card before it calls the agent, so the card decides what the client trusts: the address it sends data to, the flow it signs in with, and the text its model reads about the agent's skills.
Spellwarden checks each card against A2A 1.0, following the protocol's own v1.0.1 definition. Most checks need nothing but the card, and they run in the free scanner on every plan. Seeing the results in the dashboard needs a repository that reports from CI, which belongs to Team and Enterprise: the CI guide explains how a repository connects.
The A2A rules
The 19 A2A rules fall into groups: conformance to A2A 1.0, authentication flows, signed cards, injected text and credentials. Each rule has a page with what it detects, an example and how to fix it; Scanner rules explains ids and severities. The severity shown is the rule's worst case.
Conformance to A2A 1.0
These rules read the card as strict JSON and compare it with the protocol's definition: required members, types, absolute URIs, protocol bindings and unique skill ids. Cards are read as strict JSON, which is what A2A clients accept, so a comment, a trailing comma or a byte-order mark is refused. A card still in the 0.2 or 0.3 shape, or served at the path used before 0.3, also gets a migration status and a checklist in the scanner's report; the last step is always to scan again.
- SW-A2A-001: Agent card does not follow A2A 1.0 (medium).
- SW-A2A-002: Agent card uses a format older than A2A 1.0 (medium).
- SW-A2A-003: Agent card at the path used before A2A 0.3 (medium).
- SW-A2A-018: Agent card cannot be read (high).
- SW-A2A-019: Agent card member spelled two ways (high).
A member spelled both ways, with its JSON name and with its protocol buffer field name, is reported because readers disagree on which spelling is the card: a reader that knows only the JSON names keeps one and a protocol buffer reader keeps the other.
Authentication flows and transport
A client that follows a card signs in the way the card says, so these rules look at the endpoints and the flows. The OAuth implicit and password flows are deprecated in A2A 1.0 and documented as removed, but the protocol's own definition still carries them as deprecated fields, so they are a finding and not a parse error.
- SW-A2A-004: Agent endpoint or authentication URL not served over HTTPS (high).
- SW-A2A-005: OAuth implicit or password flow declared (deprecated in A2A 1.0) (high).
- SW-A2A-006: OAuth authorization code flow without PKCE required (medium).
- SW-A2A-007: Agent declares no authentication (medium).
- SW-A2A-008: API key sent in the URL query (medium).
- SW-A2A-009: Skill inherits every scope of the agent (low).
Signed cards
Signing is optional. A signed card carries a JSON Web Signature over the card without its signatures, canonicalised as RFC 8785 describes. The scanner verifies the signature and says which kind of key verified it, because only one of them says who signed. With a2a.keys set, that file is the only trusted source of keys. Without it, and without probing, a signed card is reported as not verifiable and the report says how to verify it.
- SW-A2A-010: Agent card signature does not verify (high).
- SW-A2A-011: Agent card signature is malformed or uses a refused algorithm (high).
- SW-A2A-012: Agent card is not signed (low).
- SW-A2A-013: Signing key not bound to the card's host (medium).
- SW-A2A-017: Agent card signed with a shared secret that only the agent can check (medium).
Injected text
The names and descriptions on a card are read by a model that decides whether to call the agent. The rule looks for instruction-like phrases and hidden characters in that text.
- SW-A2A-014: Agent card text contains injected instructions or hidden characters (high).
Credentials
A card is published for anyone to read. The rule looks for text shaped like a credential in it, and a finding shows the matched text masked.
- SW-A2A-015: Credential in the agent card (critical).
Remote cards
One more rule applies only to a card fetched from a remote agent, described in the section on probing.
- SW-A2A-016: Agent card fetched through a redirect to another host (medium).
Where cards are found in a repository scan
The scanner treats .well-known/agent-card.json at any depth, and every path that the a2a.cards setting lists, as an agent card whatever the file holds. A card that cannot be read is a finding that you can accept in the baseline, and a file that parses but does not have the shape of a card is reported by the conformance rule.
A file at .well-known/agent.json and any other file named agent-card.json is a card only when it parses strictly and has the shape of one. Otherwise the scanner records a notice and nothing else, because other software uses those names. A repository of deliberately broken sample cards leaves them out with the exclude setting.
The settings for cards go in the repository's settings file. This is the example from the scanner's README:
# .spellwarden/config.yml
version: 1
a2a:
cards: # files to check as cards, besides the names below (at most 50)
- deploy/agent/card.json
keys: .well-known/jwks.json # the only keys trusted to sign the repository's cards (a JWKS, at most 64 KiB and 20 keys)
agents: # remote agents to fetch under --probe (at most 20)
- https://agent.example.com/
- https://example.com/agents/billing/card.jsonOn a pull or merge request, a2a.keys and a2a.agents are read from the base revision, never from the request, so a request cannot choose the keys its own cards are verified with or the agents that are fetched. A request that adds a keys file and a signed card together is therefore verified against a base where neither exists: its cards are not verifiable on that request and are verified from the next one on. Merge the keys first.
Probing a remote card on request
The agents you call belong to someone else, so the scanner does not fetch them unless you ask. List them under a2a.agents and run the scan with --probe. Probing is off by default, and it is switched off on a pull or merge request from a fork or from Dependabot.
- Each address is fetched with one GET and no header but Accept and the scanner's user agent: no Authorization header, no cookie and nothing from your repository. Addresses must be public, and an
http://address is never fetched. - An address that ends in
/is a base address: the card is at that address followed by.well-known/agent-card.json, and a 404 or 410 there is followed by one request for.well-known/agent.json. Any other address is the card's own and is fetched as it is. - A redirect to another host is reported, because the card then comes from somewhere you did not name.
- The requests share the probe budget of the whole run with the MCP server probes, which come first. With no
a2a.keys, the probe also fetches the key set that a signed card names, which says only that whoever serves the key set signed the card.
A remote agent that cannot be fetched is reported as failed with a notice, never as an error: the agent's outage must not fail your build. Findings on a remote agent's card do not fail a pull or merge request, because the card belongs to someone else and changes on its own schedule. They are reported, uploaded and counted on every other run, which is where your team decides whether to keep calling the agent.
Reading the results
Findings on cards are ordinary findings: they appear in the repository's scan, in SARIF code scanning alerts, in the request comment, in the baseline and in --fail-on. In the dashboard, the Rules and Findings pages have an area filter. Choose A2A agent cards in it to see only the agent card rules, or only the findings they raised; the Findings page keeps the choice in its address, so you can share the filtered list.
Open the Findings page in the live demo
The Agent cards panel
Each repository's page has an Agent cards panel when the latest CI scan checked at least one card. It lists every card the scan checked with:
- the card, as its path in the repository or as its address for a remote agent;
- the source, Repository or Remote agent;
- the format, A2A 1.0, 0.3, 0.2 or Unknown, and the protocol versions the card declares;
- the signature, Unsigned, Verified, Invalid, Refused or Not verifiable;
- the migration status, Ready, To migrate or Invalid;
- the open findings by severity, and how many were accepted.
A card that was not checked says why: probing is off, the address is not https, the probe ran out of time, or the pipeline ran from a fork. A card that could not be read says that too, for example because it was a private address, the request timed out, the card was too large or it was not valid JSON. The link See A2A findings opens the Findings page with the repository and the area already chosen. It appears for a scan that the code host attested; for a scan reported with a CI key, the panel says that it is not attested by the code host and has no link.
The Agent cards panel needs a connected repository, which is part of Team and Enterprise. The scanner's own report and its checks on every pull or merge request work on every plan.
Locally with the scanner
The open-source scanner checks agent cards on your own machine with no account. It is Apache-2.0 and its source is at github.com/spellwarden-ai/spellwarden. Run it in a repository with a card:
pipx run spellwarden scan .
pipx run spellwarden scan . --probeThe first command checks the cards in the repository without a network. The second also fetches the remote agents that a2a.agents lists. The scan fails on findings of high severity or worse by default, and the exit code says whether it passed. Its settings live in .spellwarden/config.yml, where fail-on, probe and the a2a mapping can be set. A finding you decide to accept goes into the baseline file with a reason, as described in the guide to the scanner.
In CI the scanner sends one usage request at the end of a run. For agent cards it says only whether the run checked at least one card, never a card, an address or a count, and it can be switched off. The CI guide covers the GitHub Action and the GitLab component that run the same scanner.