internet-court/a2a-protocol
>- Builds Agent-to-Agent (A2A) servers and clients following Google's open protocol for agent interoperability. Use when the user wants to create an A2A-compliant agent, build an Agent Card, implement task management, connect agents across frameworks, set up agent discovery, handle streaming responses, implement push notifications, or orchestrate multi-agent a2a server, a2a client, agent card, agent interoperability, agent collaboration, multi-agent, agent discovery, a2a sdk, a2a task.
npx skills add https://github.com/internet-court/internet-court-skill --skill a2a-protocol
Implements the Agent2Agent (A2A) open protocol for communication between AI agents built on different frameworks. A2A enables agents to discover each other via Agent Cards, negotiate interaction modalities, manage collaborative tasks, and exchange data — all without exposing internal state, memory, or tools. Supports JSON-RPC 2.0 over HTTP(S), streaming via SSE, gRPC, and async push notifications.
/.well-known/agent.json describing identity, capabilities, skills, endpoint, authpip install a2a-sdk # Core
pip install "a2a-sdk[http-server]" # With FastAPI/Starlette
pip install "a2a-sdk[grpc]" # With gRPC
from a2a.types import AgentCard, AgentSkill, AgentCapabilities
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.apps.starlette import A2AStarletteApplication
from a2a.server.request_handler import DefaultRequestHandler
from a2a.types import Message, TextPart, TaskState, TaskStatus
import uvicorn
agent_card = AgentCard(
name="Research Assistant",
description="Searches the web and answers questions with citations.",
url="https://research-agent.example.com",
version="1.0.0",
capabilities=AgentCapabilities(streaming=True, pushNotifications=True),
skills=[AgentSkill(
id="web-search", name="Web Search",
description="Search the web for current information",
tags=["search", "research"], examples=["Find the latest news about AI regulation"],
)],
defaultInputModes=["text/plain"],
defaultOutputModes=["text/plain", "application/json"],
)
class ResearchAgentExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue):
query = context.get_user_message().parts[0].text
await event_queue.enqueue_event(
TaskStatus(state=TaskState.working, message=Message(
role="agent", parts=[TextPart(text="Searching...")]
))
)
result = await self._research(query)
await event_queue.enqueue_event(
TaskStatus(state=TaskState.completed, message=Message(
role="agent", parts=[TextPart(text=result)]
))
)
async def cancel(self, context: RequestContext, event_queue: EventQueue):
await event_queue.enqueue_event(TaskStatus(state=TaskState.canceled))
async def _research(self, query: str) -> str:
return f"Research results for: {query}"
# Start server — Agent Card auto-served at /.well-known/agent.json
agent_executor = ResearchAgentExecutor()
request_handler = DefaultRequestHandler(agent_executor=agent_executor, task_store=InMemoryTaskStore())
app = A2AStarletteApplication(agent_card=agent_card, http_handler=request_handler)
uvicorn.run(app.build(), host="0.0.0.0", port=8000)
from a2a.client import A2AClient
from a2a.types import MessageSendParams, SendMessageRequest, Message, TextPart
client = await A2AClient.get_client_from_agent_card_url(
"https://research-agent.example.com/.well-known/agent.json"
)
# Synchronous request
request = SendMessageRequest(params=MessageSendParams(
message=Message(role="user", parts=[TextPart(text="Latest quantum computing developments?")])
))
response = await client.send_message(request)
if hasattr(response, 'status'):
print(f"Task {response.id}: {response.status.state}")
if response.status.message:
print(response.status.message.parts[0].text)
# Streaming response
async for event in client.send_message_streaming(request):
if hasattr(event, 'status') and event.status.message:
for part in event.status.message.parts:
if hasattr(part, 'text'):
print(part.text, end="", flush=True)
npm install @a2a-js/sdk
import { A2AServer, A2AClient, TaskState } from '@a2a-js/sdk';
// Server
const server = new A2AServer({
agentCard: {
name: 'Code Reviewer', description: 'Reviews code for bugs and best practices',
url: 'https://code-reviewer.example.com', version: '1.0.0',
capabilities: { streaming: true },
skills: [{ id: 'review', name: 'Code Review', description: 'Analyze code for issues', tags: ['code', 'review'] }],
defaultInputModes: ['text/plain'], defaultOutputModes: ['text/plain'],
},
async onMessage(context, eventQueue) {
const userText = context.getUserMessage().parts[0].text;
await eventQueue.enqueue({ status: { state: TaskState.WORKING, message: { role: 'agent', parts: [{ text: 'Reviewing...' }] } } });
const review = await reviewCode(userText);
await eventQueue.enqueue({ status: { state: TaskState.COMPLETED, message: { role: 'agent', parts: [{ text: review }] } } });
},
});
server.listen(8000);
// Client
const client = await A2AClient.fromAgentCardUrl('https://code-reviewer.example.com/.well-known/agent.json');
const response = await client.sendMessage({
message: { role: 'user', parts: [{ text: 'Review: function add(a,b) { return a + b; }' }] },
});
# Sequential: research → write → review
research_agent = await A2AClient.get_client_from_agent_card_url("https://research-agent.example.com/.well-known/agent.json")
writer_agent = await A2AClient.get_client_from_agent_card_url("https://writer-agent.example.com/.well-known/agent.json")
research_result = await research_agent.send_message(SendMessageRequest(
params=MessageSendParams(message=Message(role="user", parts=[TextPart(text="Research quantum computing breakthroughs 2025")]))
))
article = await writer_agent.send_message(SendMessageRequest(
params=MessageSendParams(message=Message(role="user", parts=[TextPart(text=f"Write blog post: {research_result.status.message.parts[0].text}")]))
))
# Parallel fan-out
import asyncio
results = await asyncio.gather(
query_agent(agent_a, "Analyze market trends"),
query_agent(agent_b, "Analyze competitor products"),
query_agent(agent_c, "Analyze customer feedback"),
)
| | A2A | MCP |
|---|---|---|
| Purpose | Agent-to-agent communication | Agent-to-tool communication |
| Actors | Agent ↔ Agent | Agent ↔ Tool/Data source |
| Tasks | Stateful, long-running, async | Stateless function calls |
| Use when | Delegating to another autonomous agent | Calling a specific tool/API |
Input: "Build an A2A server that acts as a customer support router. It receives customer queries and delegates to specialized agents: billing-agent, technical-agent, and sales-agent based on the query content."
Output: A2A server with Agent Card listing routing as its primary skill, message handler that classifies queries, A2A client connections to 3 downstream agents, task forwarding with context preservation, aggregated response, and fallback to human handoff.
Input: "Create a multi-agent code pipeline: code-writer generates code, test-writer creates tests, code-reviewer reviews both. Each is an independent A2A server. Build an orchestrator."
Output: 3 A2A server implementations each with Agent Card and execution logic, orchestrator client with sequential pipeline (write → test → review), streaming updates, and error handling with feedback loops on rejection.
/.well-known/agent.json — this is the standard discovery endpointinput-required state for human-in-the-loop scenariosTake internet-court/a2a-protocol from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip, npm.
Without those the skill loads but fails at the first command.