.agents/skills/a2a-protocol/SKILL.md
How agents call other agents via the A2A (agent-to-agent) JSON-RPC protocol. Use when enabling inter-agent communication, exposing agent skills to other agents, or calling external agents from scripts.
npx skillsauth add BuilderIO/agent-native a2a-protocolInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Agents can call other agents using the A2A protocol. This is a JSON-RPC-based protocol for agent discovery and communication. Use it when work should go to a different agent entirely — not the local agent chat.
Agent-native apps don't exist in isolation. A mail agent might need analytics data. A calendar agent might need to search issues. A2A lets agents discover each other, send messages, and receive structured results — all over HTTP with bearer token auth.
Add mountA2A() to a server plugin:
// server/plugins/a2a.ts
import { mountA2A } from "@agent-native/core/a2a";
export default defineNitroPlugin((nitro) => {
const app = nitro.h3App;
mountA2A(app, {
name: "Analytics Agent",
description: "Queries analytics data across providers",
version: "1.0.0",
skills: [
{
id: "query-data",
name: "Query Data",
description: "Run analytics queries across connected data sources",
tags: ["analytics", "data"],
examples: ["What were last week's signups?", "Show conversion rates"],
},
],
apiKeyEnv: "A2A_API_KEY", // env var holding the bearer token
streaming: true, // enable message/stream method
});
});
This mounts two endpoints:
GET /.well-known/agent-card.json — public agent discovery (no auth required)POST /a2a — JSON-RPC endpoint (bearer token auth required)interface A2AConfig {
name: string; // agent display name
description: string; // what this agent does
version?: string; // semver version (default: "1.0.0")
skills: AgentSkill[]; // capabilities this agent exposes
handler?: A2AHandler; // custom message handler
apiKeyEnv?: string; // env var name for bearer token auth
streaming?: boolean; // enable streaming responses
}
interface AgentSkill {
id: string; // unique skill identifier
name: string; // human-readable name
description: string; // what this skill does
tags?: string[]; // categorization tags
examples?: string[]; // example prompts
}
The agent card is auto-generated at GET /.well-known/agent-card.json. Other agents fetch this to discover what skills are available:
{
"name": "Analytics Agent",
"description": "Queries analytics data across providers",
"url": "https://analytics.example.com",
"version": "1.0.0",
"protocolVersion": "0.3",
"capabilities": { "streaming": true },
"skills": [
{
"id": "query-data",
"name": "Query Data",
"description": "Run analytics queries across connected data sources"
}
]
}
callAgent() (text in, text out)import { callAgent } from "@agent-native/core/a2a";
const answer = await callAgent(
"https://analytics.example.com",
"What were last week's signups?",
{ apiKey: process.env.ANALYTICS_A2A_KEY },
);
// answer is a plain string
A2AClient (full control)import { A2AClient } from "@agent-native/core/a2a";
const client = new A2AClient(
"https://analytics.example.com",
process.env.ANALYTICS_A2A_KEY,
);
// Discover agent capabilities
const card = await client.getAgentCard();
// Send a message and get a task back
const task = await client.send({
role: "user",
parts: [{ type: "text", text: "What were last week's signups?" }],
});
// task.status.state === "completed"
// task.status.message.parts[0].text === "Last week: 1,247 signups..."
// Stream responses
for await (const update of client.stream({
role: "user",
parts: [{ type: "text", text: "Detailed breakdown by day" }],
})) {
console.log(update.status.state, update.status.message);
}
| Method | Purpose | Auth required |
| ---------------- | -------------------------------- | ------------- |
| message/send | Send a message, get a task back | Yes |
| message/stream | Send a message, stream responses | Yes |
| tasks/get | Get task status by ID | Yes |
| tasks/cancel | Cancel a running task | Yes |
Tasks go through these states:
submitted → working → completed
→ failed
→ canceled
→ input-required
status.messagetasks/cancelA2A uses bearer token auth. The server reads the token from the environment variable specified by apiKeyEnv:
A2A_API_KEY=my-secret-token in the server's environmentAuthorization: Bearer my-secret-token/.well-known/agent-card.json) is public — no auth needed for discoveryMessages contain typed parts:
| Part type | Fields | Use for |
| --------- | ----------------------------------- | -------------------------- |
| text | { type: "text", text: "..." } | Natural language messages |
| file | { type: "file", file: { ... } } | Files (bytes or URI) |
| data | { type: "data", data: { ... } } | Structured JSON data |
A mail agent calls an analytics agent to include data in an email draft:
// actions/draft-with-analytics.ts
import { callAgent } from "@agent-native/core/a2a";
import { writeAppState } from "@agent-native/core/application-state";
export default async function (args: string[]) {
// Ask the analytics agent for data
const stats = await callAgent(
process.env.ANALYTICS_AGENT_URL!,
"Summarize last week's key metrics in 3 bullet points",
{ apiKey: process.env.ANALYTICS_A2A_KEY },
);
// Create a draft with the analytics data
await writeAppState("compose-analytics-report", {
id: "analytics-report",
to: "[email protected]",
subject: "Weekly Analytics Summary",
body: `Hi team,\n\nHere are last week's numbers:\n\n${stats}\n\nBest`,
mode: "compose",
});
}
All types are exported from @agent-native/core/a2a:
import type {
A2AConfig,
A2AHandler,
A2AHandlerContext,
A2AHandlerResult,
AgentCard,
AgentSkill,
AgentCapabilities,
Task,
TaskState,
TaskStatus,
Message,
Part,
TextPart,
FilePart,
DataPart,
Artifact,
JsonRpcRequest,
JsonRpcResponse,
} from "@agent-native/core/a2a";
tools
Public booking flow — the state machine, animations, and URL/app-state sync.
tools
Trigger-based automations — reminders, follow-ups, webhooks — across the booking lifecycle.
tools
Team event types, round-robin assignment, collective bookings, host weights, and no-show calibration.
development
The pure `computeAvailableSlots` function — inputs, outputs, invariants, and debugging guide.