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.
What A2A actually is
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:
- The agent card — a discovery document.
- The skill — a callable JSON-RPC endpoint.
That’s it. You can ship a useful A2A surface with one skill and a single method.
Part 1 — the agent card (discovery)
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."
}]
}
Enter fullscreen mode Exit fullscreen mode
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.
Part 2 — the endpoint (a callable skill)
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" }] } } }
Enter fullscreen mode Exit fullscreen mode
…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 } } } }
] }] } }
Enter fullscreen mode Exit fullscreen mode
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.
The minimum viable A2A
You don’t need the whole spec to start. Pick one real thing your service does and:
- Serve a valid agent card at
/.well-known/agent-card.jsonwith one skill. - Implement the synchronous
message/sendmethod wrapping that capability. - Return a completed
Taskwith 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.
A2A vs WebMCP vs MCP (they’re not competitors)
- A2A — networked agents calling your service over HTTP (this article).
- WebMCP — an agent running inside the browser calling tools on your open page.
- MCP — connecting tools to a single model or app, usually locally.
They stack rather than compete: A2A reaches networked agents, WebMCP reaches in-browser ones, MCP wires tools into one model.
The one rule: don’t publish a card you can’t back
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.
답글 남기기