agentsclimarketplace

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

Install
npx -y skills add yanacuti1121/Yana-AI --skill a2a-protocol-patterns

Assembled 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

Keep looking

Skills are one crate of 626,474. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.