A2a protocol patterns
Skill yanacuti1121/Yana-AI/.claude/skills/a2a-protocol-patterns
Agent-to-Agent (A2A) protocol — Google 2025, 150+ org backing. Agent Cards discovery, task lifecycle (submitted→working→completed), artifacts (text/structured/video), opaque task model. MCP vs A2A split. Auth: bearer/mTLS/signed. Sources: rohitg00/ai-engineering-from-scratch (Apache-2.0).From its SKILL.md
npx -y skills add yanacuti1121/Yana-AI --skill a2a-protocol-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 12 stars12 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.5 KB, as published. Nobody here has run it
/a2a-protocol-patterns
When to Use
- Cross-organization agent calls: your agent calls an agent at another company/system
- Building a discoverable agent endpoint that any A2A-compatible client can invoke
- Replacing bespoke agent-to-agent HTTP contracts with a standard protocol
- Multi-agent systems where subagents are independent services (LangGraph, CrewAI, AutoGen)
Do NOT use for
- Tool use within a single agent (use MCP — [[mcp-server-patterns]])
- Internal function calls between co-located agents in the same process
- Synchronous tool calls where task lifecycle overhead isn't worth it
MCP vs A2A — the split
MCP (Model Context Protocol) — agent ↔ tool
vertical: agent reads/writes to a tool server
stateless by default, JSON-RPC
A2A (Agent-to-Agent Protocol) — agent ↔ agent
horizontal peer: both sides are agents with reasoning
stateful (task lifecycle), async by default
Production pattern: use both.
Agent A —[A2A]→ Agent B —[MCP]→ tools on Agent B's side
Agent Card (discovery at /.well-known/agent.json)
// Agent Card spec — publish at /.well-known/agent.json
interface AgentCard {
name: string;
version: string;
skills: string[]; // e.g. ["code-review", "security-scan"]
endpoints: {
tasks: string; // POST /tasks
};
auth: {
type: "bearer" | "mtls" | "signed";
scopes?: string[]; // OAuth scopes if bearer
};
modalities: ("text" | "structured" | "image" | "video" | "audio")[];
description?: string;
}
// Example
const yamtamAgentCard: AgentCard = {
name: "yamtam-security-agent",
version: "1.0.0",
skills: ["red-team-check", "blue-team-fix", "leak-check"],
endpoints: { tasks: "https://agents.example.com/tasks" },
auth: { type: "bearer", scopes: ["agent:invoke"] },
modalities: ["text", "structured"],
};
Task lifecycle
submitted → working → completed
→ failed
→ canceled
// Task model
interface A2ATask {
id: string;
skill: string;
input: Record<string, unknown>;
state: "submitted" | "working" | "completed" | "failed" | "canceled";
progress?: number; // 0–100
artifacts?: Artifact[];
error?: string;
}
interface Artifact {
type: "text" | "structured" | "image" | "video" | "audio";
content: string; // text/JSON inline or file path
mimeType?: string;
}
Minimal A2A server (Node.js / Express)
import express from "express";
import { randomUUID } from "crypto";
const app = express();
const tasks = new Map<string, A2ATask>();
// Discovery
app.get("/.well-known/agent.json", (req, res) => res.json(yamtamAgentCard));
// Submit task
app.post("/tasks", express.json(), async (req, res) => {
const task: A2ATask = {
id: randomUUID(),
skill: req.body.skill,
input: req.body.input,
state: "submitted",
};
tasks.set(task.id, task);
res.status(201).json({ task_id: task.id, state: task.state });
// Process async
processTask(task);
});
// Poll for status
app.get("/tasks/:id", (req, res) => {
const task = tasks.get(req.params.id);
if (!task) return res.status(404).json({ error: "not found" });
res.json(task);
});
// SSE for streaming updates
app.get("/tasks/:id/events", (req, res) => {
const task = tasks.get(req.params.id);
if (!task) return res.status(404).end();
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
const send = (data: object) => res.write(`data: ${JSON.stringify(data)}\n\n`);
const interval = setInterval(() => {
const t = tasks.get(req.params.id)!;
send({ state: t.state, progress: t.progress });
if (t.state === "completed" || t.state === "failed") {
clearInterval(interval);
res.end();
}
}, 500);
});
async function processTask(task: A2ATask) {
task.state = "working";
task.progress = 0;
// ... run skill logic ...
task.state = "completed";
task.artifacts = [{ type: "text", content: "Scan complete. 0 critical findings." }];
}
A2A client (discovery + task submission)
async function callAgent(agentBaseUrl: string, skill: string, input: object): Promise<Artifact[]> {
// 1. Discover
const card = await fetch(`${agentBaseUrl}/.well-known/agent.json`).then(r => r.json());
if (!card.skills.includes(skill)) throw new Error(`Agent does not support skill: ${skill}`);
// 2. Submit
const { task_id } = await fetch(card.endpoints.tasks, {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${getToken()}` },
body: JSON.stringify({ skill, input }),
}).then(r => r.json());
// 3. Poll until done
for (let i = 0; i < 60; i++) {
await new Promise(r => setTimeout(r, 1000));
const task: A2ATask = await fetch(`${agentBaseUrl}/tasks/${task_id}`).then(r => r.json());
if (task.state === "completed") return task.artifacts ?? [];
if (task.state === "failed") throw new Error(`Task failed: ${task.error}`);
}
throw new Error("Task timed out");
}
Auth patterns
Bearer token — OAuth2 or opaque; declare required scopes in Agent Card
mTLS — mutual TLS; both sides present certificates; for cross-org zero-trust
Signed request — HMAC-SHA256 over request body; lightweight; no PKI required
Anti-Fake-Pass Checklist
❌ No Agent Card → client can't discover capabilities; bespoke integration required
❌ Task state not persisted → server restart loses in-progress tasks
❌ No auth on /tasks endpoint → anyone can invoke any skill
❌ Synchronous /tasks POST that blocks until done → timeouts on long skills; use async + poll/SSE
❌ Using A2A for tool calls within one agent → MCP is the right layer; A2A overhead unwarranted
❌ Opaque lifecycle violated → prescribing HOW remote agent solves the task defeats the abstraction