Build a valid A2A (Agent2Agent) Agent Card JSON manifest - name, capabilities, input and output modes, skills - ready to serve at /.well-known. Free.
An Agent Card is the discovery document for the Agent2Agent (A2A) protocol. It is a single JSON object that says what your agent is called, where to reach it, which transport features it supports, and — the part that actually matters to a calling agent — which discrete skills it offers. Another agent fetches that document, reads the skill list, and decides whether to delegate work to you. If the card is malformed or missing, your agent is invisible to the rest of the network no matter how good it is.
This generator is a form-to-JSON builder. You fill in the agent metadata, tick the capability flags, choose input and output MIME types, add one entry per skill, and the card is rebuilt on every keystroke in the panel on the right. It runs entirely in your browser: the JSON is assembled in React state and never leaves the page, so it is safe to paste in a real service URL or an internal hostname while drafting. There is no upload, no server round trip, and no account.
The card is assembled in a fixed order, and optional fields are omitted entirely rather than emitted as empty strings. That matters, because a consumer that does a strict schema check will treat "documentationUrl": "" differently from an absent key. Here is every field the builder can produce.
| Field | Type | Emitted when |
|---|---|---|
protocolVersion | string | The protocol version box is non-empty. Pre-filled with 0.3.0. |
name | string | Always. Trimmed; an empty box gives "". |
description | string | Always. Trimmed. |
url | string | Service URL is non-empty. |
version | string | Version is non-empty. This is your agent's version, not the protocol's. |
documentationUrl | string | The optional documentation URL is filled in. |
provider | object | Either provider box is filled. Contains organization and url, each only if given. |
capabilities | object | Always. Three booleans, described below. |
defaultInputModes | string[] | Always. Falls back to ["text/plain"] if you deselect everything. |
defaultOutputModes | string[] | Always, same fallback. |
skills | object[] | Always. An empty array if you remove every skill. |
Every skill object carries id, name, description and tags unconditionally, plus examples only when you have typed at least one. tags is produced by splitting your comma-separated input, trimming each piece and dropping blanks, so "cooking, search, " becomes ["cooking","search"]. examples is split the same way but on newlines — one example utterance per line.
The default state of the tool describes a recipe agent with a single skill. Filled in as shipped, it produces this:
| Input | Resulting JSON |
|---|---|
| Name: Recipe Agent | "name": "Recipe Agent" |
| Service URL: https://recipe-agent.example.com | "url": "https://recipe-agent.example.com" |
| Version 1.0.0, protocol 0.3.0 | "version": "1.0.0", "protocolVersion": "0.3.0" |
| Streaming on, the other two off | "capabilities": { "streaming": true, "pushNotifications": false, "stateTransitionHistory": false } |
Skill id find-recipe, tags cooking, search | { "id": "find-recipe", "name": "Find Recipe", "description": "…", "tags": ["cooking","search"], "examples": ["I need a recipe for sourdough bread"] } |
Below the JSON pane the tool shows the exact path to publish at, derived from whatever you typed in the Service URL box with any trailing slashes stripped: https://recipe-agent.example.com/.well-known/agent-card.json. That well-known path is how a client finds you without being told anything except your origin.
streaming: true may open a stream and hang when nothing arrives.All three are emitted as literal booleans every time. There is no way to omit the capabilities object from this generator, which is deliberate — consumers should not have to guess.
The mode pickers offer eight presets — text/plain, text/markdown, application/json, image/png, image/jpeg, audio/mpeg, video/mp4 and application/pdf — and a free-text box for anything else. Type a MIME type such as application/xml and press Enter or click Add, and it joins the list as a selected chip. Duplicates are rejected silently.
These are defaults, applied to the agent as a whole. Be conservative here. Declaring image/png as an input mode is a promise that any skill can accept an image; if only one of your skills can, a calling agent will send images to the others and get failures. The honest pattern for a text-first agent is ["text/plain"] in and ["text/plain", "text/markdown"] out.
This is a card builder, not a full spec implementation, and the difference is worth stating plainly before you paste the output into production. The generator produces the core discovery fields listed above and nothing else. It does not emit authentication or security-scheme declarations, alternative transport or interface listings, a preferred-transport hint, per-skill input and output modes, or any signature block. If your deployment needs those, treat the generated document as a starting skeleton and add them by hand.
It also performs no validation. Nothing checks that your service URL resolves, that the skill ids are unique, that version is semantic, or that the protocol version you typed is one a client will accept. The protocol version field is free text precisely so you can target whichever revision your counterparty implements — A2A moves quickly, and a card written against one revision is not guaranteed to satisfy a client written against another. Confirm the version with the agents you actually intend to interoperate with rather than assuming the pre-filled default is right for you.
The skill list is the working part of the card. Everything above it is metadata; the skills are what a remote planner reads when deciding whether you are the right agent for a subtask. A few habits make the difference:
id a stable, machine-safe value. Lowercase and hyphenated, like find-recipe. It is an identifier, not a label, and changing it later breaks callers that hard-coded it.description for a model, not a marketing page. State the input it expects and the output it returns. "Search and retrieve recipes for a given ingredient or dish" tells a planner more than "world-class culinary intelligence".tags as a coarse filter. Two to four broad terms beat a dozen narrow ones. They exist so a directory can shortlist candidate agents cheaply.examples. One per line, phrased the way a user or an upstream agent would actually phrase it. These are the strongest signal for routing, and they cost nothing.| Symptom | Likely cause |
|---|---|
| Clients get a 404 for your card | Served at the wrong path. It must be /.well-known/agent-card.json on the origin in url, not under a sub-path. |
| Card fetched but ignored | Served with the wrong content type. Return it as application/json. |
| Skill never gets routed to | description and examples are too vague, or tags are so specific nothing matches them. |
| Callers open a stream that never yields | streaming set to true on an endpoint that only returns a single response. |
| Requests arrive with unusable payloads | defaultInputModes lists MIME types some skills cannot handle. |
Empty "name" in the output | The name box was cleared. name and description are always emitted, blank or not. |
A2A and MCP solve different halves of the same architecture. A2A is how independent agents discover and delegate to each other; MCP is how one agent reaches its own tools and data. A typical deployment uses both — an MCP client inside the agent, an Agent Card facing outward. If you are still deciding which one your problem needs, the agent protocol selector walks the decision in a couple of questions.
agent-card.json./.well-known/agent-card.json on the same origin as the url field, over HTTPS, with Content-Type: application/json.version whenever the skill list changes, and keep the old skill ids working for at least one release.It is easier to write a good card once you can picture what happens to it. A client that knows only your origin requests /.well-known/agent-card.json. It reads name and description to decide whether you are plausibly relevant at all. It reads skills to decide which specific capability to invoke, matching on the descriptions, tags and example utterances. It reads capabilities to decide how to call you — whether to open a stream, whether to register a webhook, whether it can ask for state history. It reads defaultInputModes and defaultOutputModes to decide what it is allowed to send and what it should be ready to receive. Then it uses url as the endpoint.
Every one of those decisions is made from text you wrote in this form, before a single request reaches your agent. That is why the description fields deserve more care than they usually get: they are not documentation, they are the routing logic. A skill whose description does not distinguish it from a competing agent's skill will not be chosen, and you will have no log entry telling you why.
Two version numbers live in the card and they move independently. protocolVersion tracks the A2A revision you implement and changes only when you migrate. version is your own agent's release, and it is the one you will actually be editing.
The rule that keeps a network of agents working is that skill ids are a public contract. Adding a skill is safe. Improving a description is safe. Renaming or removing a skill id is a breaking change for anyone who cached it, and because A2A discovery is pull-based you have no reliable way to notify them. If a skill must go, keep the id serving a useful error for a release before you remove it, and bump version so an observant caller can see something changed. Regenerate the whole card here whenever the skill list changes rather than hand-editing the published file — hand edits are how a card drifts out of sync with what the agent actually implements.