Agent cards¶
Agents already call each other. What one may call on another lives in a README, in a Slack thread, or in the other team's source, so a caller finds out that a skill was removed by getting a runtime error — the latest possible moment to find out.
An agent card is that surface as data: who the agent is, what it can be asked to do, with
which schemas, under which scopes, and what it holds to. It is served at
/.well-known/agent-card.json, and every field on it is generated from the
AgentDefinition and the tools themselves. Nothing on a card is hand-maintained, because a
document written beside an implementation is a document that drifts away from it.
flowchart LR
Definition[AgentDefinition] --> Generate[card_for]
Exports[skills handed in deliberately] --> Generate
Generate -->|AgentCardError at startup| Refused[nothing is served]
Generate --> Card[AgentCard] --> Endpoint[CardEndpoint] --> Peer[a peer, unauthenticated]
Publishing¶
from tesserix_adk.a2a import CardEndpoint, card_for
card = card_for(definition, audience="https://booker.example.gov", exports=(price_leg, refund))
endpoint = CardEndpoint(card) # mount on any ASGI application
exports is the whole of the export control: a skill that is not handed in is not on the
card and is not named on it. Default-deny, so an internal tool stays internal by
construction rather than by remembering to exclude it.
| Argument | Effect |
|---|---|
definition |
The reviewed agent. Names, versions, scopes, approval and idempotence all come from here. |
audience |
What a credential for this agent is minted for. |
exports |
The skills deliberately published, as the tools themselves. |
accepted_issuers |
Which callers it takes delegations from. Empty accepts any, as a statement rather than a default. |
calls_per_minute |
A rate the deployment enforces. Absent where nothing enforces one — the kit does not publish a number nobody holds to. |
available |
Whether it is serving normally. |
What a card refuses to say¶
A card is served to strangers, so it carries what a peer needs in order to call correctly and nothing about how the agent is built.
| Not on the card | Why |
|---|---|
| Instructions, model, task budget | How the work is done is not how it is called. |
| The owner's paging address | A card is unauthenticated; an address published there is an address that gets scraped. AgentProvider names the team and the service instead. |
| Tenants, cost internals, internal endpoints | Facts about other people's use of the agent. |
| Any unexported tool | Absent by construction, and unnamed even in a refusal. |
Failing at startup, not at the first peer¶
card_for raises AgentCardError, naming the skill, when a card could not be published
honestly: an export the agent may not call, a skill with no description, arguments that are
not a JSON object, more than MAX_SKILLS skills, or a card over MAX_CARD_BYTES. Raising
at generation means a deployment fails to start rather than serving a card that peers then
call incorrectly.
tesserix_adk.cli.card.main renders or lints the card when the application supplies its
Build callback. A product may expose that callable as adk card; this distribution
does not install a global adk executable. The lint report shows skills, scopes,
approval gates and size before publication.
Serving it¶
CardEndpoint is an ASGI application with no framework dependency. It answers GET and
HEAD at its path, 405 to anything else, 404 elsewhere, and 304 to a conditional
request whose If-None-Match matches, so a peer that polls costs a conditional request
rather than a card. serve(card) replaces what is published: a degraded agent republishes
with available=False and tightened limits rather than advertising capability that will
fail, and the ETag change is what a polling peer notices.
Compatibility¶
A card is a contract, and it versions with the agent it describes — card.version is
agent.version, so two versions behind one host publish two cards and a caller can pin.
| Change | Kind |
|---|---|
| Adding a skill, adding an optional field, widening what a schema accepts | Minor |
| Removing a skill, narrowing a schema, adding a required argument, adding a required scope | Breaking: bump the major version and keep the old version served for the deprecation window in versioning.md |
Reading someone else's card¶
A description on another team's card is text that team wrote, and a card fetched from a peer is data from outside your trust boundary. Treat every string on it as data: never concatenate a peer's skill description into a system instruction, and never let it decide what your agent does. What it is for is building a request and checking a schema.
Known limitations¶
- This page describes the Tesserix peer card. Official A2A Agent Cards use the separate official A2A adapter; the two types are not wire-compatible.
- Registry-backed discovery and peer invocation exist in peer discovery and peer invocation. A deployment still owns registry publication and authenticated transport wiring.
MAX_CARD_BYTESis a ceiling, not pagination. An agent with more thanMAX_SKILLSskills is refused rather than trimmed, because a silently trimmed card is a card that lies about what an agent can do.calls_per_minuteis whatever the deployment says it is. The kit does not measure it and cannot enforce it from here.- Availability is republished by the deployment. Nothing here polls the agent's own health.