Most "AI integration" still means a human reads your docs and writes a client. A2A flips that: another agent reads a small machine description of what your service does and calls it directly — no browser, no scraping, no human in the loop. I wired it up on a small tool I run (a free llms.txt validator) and it's simpler than the acronyms suggest. Here's the whole thing, with the real requests.
A2A (Agent2Agent) is an open protocol — now under the Linux Foundation — that lets AI agents discover and call each other over plain HTTP. Think of it as the agent-to-agent counterpart of a public API: instead of publishing OpenAPI docs for humans, you publish a machine description another agent can consume and invoke.
It has exactly two moving parts:
That's it. You can ship a useful A2A surface with one skill and a single method.
You serve a JSON file at /.well-known/agent-card.json
describing who you are and what you can do. The required fields are name
, description
, version
, url
(your endpoint) and at least one skill
:
{
"protocolVersion": "0.3.0",
"name": "llms.txt Validator",
"description": "Validate a website's llms.txt and return a score with findings.",
"url": "https://llms-txt-validator.dev/a2a",
"skills": [{
"id": "validate_llms_txt",
"name": "Validate llms.txt",
"description": "Given a domain or URL, fetch and validate its llms.txt."
}]
}
The card is a contract, not a meta tag. An agent fetches it, sees a validate_llms_txt
skill, and knows both what you offer and where to call.
The card's url
points at a JSON-RPC 2.0 endpoint. An agent invokes the message/send
method with a message; you do the work and return a Task
. Here's a real call to my agent:
POST /a2a
{ "jsonrpc": "2.0", "id": "1", "method": "message/send",
"params": { "message": { "role": "user",
"parts": [{ "kind": "text", "text": "validate llmstxt.org" }] } } }
…and the reply — a completed task carrying a human-readable summary and structured data the calling agent can use directly:
{ "result": { "kind": "task", "status": { "state": "completed" },
"artifacts": [{ "parts": [
{ "kind": "text", "text": "Validated llmstxt.org: score 100/100..." },
{ "kind": "data", "data": { "ok": true, "report": { "scores": { "overall": 100 } } } }
] }] } }
No HTML, no parsing, no guessing. The agent asked a question in natural language and got back exactly the data it needed. You can curl
this yourself.
You don't need the whole spec to start. Pick one real thing your service does and:
/.well-known/agent-card.json
with one skill.message/send
method wrapping that capability.Task
with the result as an artifact.Streaming, task history, and push notifications are all optional — set capabilities.streaming
to false
and add them only when you actually need them.
They stack rather than compete: A2A reaches networked agents, WebMCP reaches in-browser ones, MCP wires tools into one model.
It's tempting to drop an agent card to look modern and leave the endpoint returning 501
. Don't. An agent that fetches your card and calls a dead url
trusts you less afterward — you've spent its call for nothing. Every skill you advertise should resolve to real, working behavior. (That's why the validator I built reports an A2A signal as "present" only when a live endpoint actually answers message/send
.)
If you ship one, drop your agent card in the comments — I'd like to call it.