Agent-to-Agent Communication
Agent-to-Agent (A2A) enables agents to collaborate, delegate tasks, and communicate with each other—even across organizational boundaries.
Communication Model Overview
MeetLoyd has a layered communication model for agents:
| Scope | Protocol | When to Use |
|---|---|---|
| Intra-App | A2A | Direct agent-to-agent within same app |
| Cross-App | SLIM | Agents in different apps (same tenant) |
| Cross-Org | SLIM | Agents in different organizations |
A2A (this page) is for communication within an app—agents on the same team collaborating directly.
SLIM is for communication across app or organization boundaries, with explicit permission controls.
See SLIM Communication for cross-app and cross-org scenarios.
Why A2A?
Complex problems often require specialized skills:
- Specialization: Let each agent focus on what it does best
- Delegation: Manager agents coordinate specialist agents
- Federation: Agents from different organizations collaborate (via SLIM)
- Scalability: Distribute work across many agents
How A2A Works
MeetLoyd implements the A2A Protocol - an open standard for agent interoperability.
Agent A ──task/send──▶ Agent B ──executes──▶ Returns result
- Agent A sends a task to Agent B
- Agent B processes the task
- Agent B returns results (or status updates)
- Agent A continues with the results
A2A Protocol Basics
Agent Cards
Every A2A-capable agent has a discovery document:
GET /.well-known/agent-card.json
{
"name": "Data Analysis Agent",
"url": "https://app.meetloyd.com/api/agents/agent-123",
"version": "1.0.0",
"protocolVersion": "1.0",
"description": "Analyzes data and generates insights",
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true
},
"skills": [
{
"id": "analyze-csv",
"name": "CSV Analysis",
"description": "Analyze CSV data files",
"inputModes": ["text", "file"],
"outputModes": ["text", "data"]
},
{
"id": "generate-report",
"name": "Report Generation",
"description": "Generate analysis reports",
"inputModes": ["data"],
"outputModes": ["text", "file"]
}
],
"authentication": {
"schemes": ["bearer"]
}
}
Task Lifecycle
submitted ──▶ working ──▶ completed
│
├──▶ input-required (needs more info)
│
├──▶ failed
│
└──▶ cancelled
Enabling A2A
Make Agent Discoverable
await client.agents.update('agent-123', {
a2a: {
enabled: true,
public: true, // Listed in registry
skills: [
{
id: 'data-analysis',
name: 'Data Analysis',
description: 'Analyze datasets and extract insights',
inputModes: ['text', 'file'],
outputModes: ['text', 'data']
}
]
}
});
Configure Authentication
await client.agents.update('agent-123', {
a2a: {
enabled: true,
authentication: {
schemes: ['bearer', 'api-key'],
allowedClients: ['agent-456', 'agent-789'] // Whitelist
}
}
});
Sending Tasks
Basic Task Send
const task = await client.a2a.sendTask({
targetAgent: 'agent-456',
message: {
role: 'user',
parts: [
{ type: 'text', text: 'Analyze this sales data and identify trends' },
{ type: 'file', uri: 'https://storage.example.com/sales-q4.csv' }
]
}
});
console.log(task.id); // task-789
console.log(task.status); // 'working'
With Streaming Updates
const stream = await client.a2a.sendTaskSubscribe({
targetAgent: 'agent-456',
message: {
role: 'user',
parts: [{ type: 'text', text: 'Generate a comprehensive analysis' }]
}
});
for await (const update of stream) {
if (update.type === 'status') {
console.log('Status:', update.status);
} else if (update.type === 'artifact') {
console.log('Artifact:', update.artifact.name);
}
}
Query Task Status
const task = await client.a2a.getTask('task-789');
console.log({
status: task.status,
history: task.history, // State transitions
artifacts: task.artifacts
});
Cancel Task
await client.a2a.cancelTask('task-789');
Message Parts
A2A messages can contain multiple parts:
Text Part
{ type: 'text', text: 'Analyze this data' }
File Part
{
type: 'file',
uri: 'https://storage.example.com/data.csv',
mimeType: 'text/csv',
name: 'sales-data.csv'
}
Data Part
{
type: 'data',
data: {
customers: [...],
orders: [...],
metrics: {...}
}
}
Artifacts
Agents return results as artifacts:
{
"artifacts": [
{
"name": "analysis-report",
"parts": [
{
"type": "text",
"text": "# Sales Analysis Report\n\n## Key Findings..."
}
],
"metadata": {
"format": "markdown",
"generatedAt": "2024-01-15T10:00:00Z"
}
},
{
"name": "chart-data",
"parts": [
{
"type": "data",
"data": {
"labels": ["Q1", "Q2", "Q3", "Q4"],
"values": [100, 150, 200, 250]
}
}
]
}
]
}
Agent Delegation
Manager-Worker Pattern
Create a manager agent that delegates to specialists:
// Manager agent's system prompt
const managerPrompt = `
You are a project manager agent that coordinates specialist agents.
Available specialists:
- data-analyst: Analyzes data, creates reports
- writer: Creates content, documentation
- coder: Writes and reviews code
- researcher: Researches topics, gathers information
When you receive a request:
1. Break it into subtasks
2. Delegate each subtask to the appropriate specialist
3. Combine their outputs into a final deliverable
Use the delegate_task tool to send work to specialists.
`;
// Manager can delegate via tool
const managerAgent = await client.agents.create({
name: 'Project Manager',
systemPrompt: managerPrompt,
tools: [
{
name: 'delegate_task',
description: 'Delegate a task to a specialist agent',
inputSchema: {
type: 'object',
properties: {
specialist: { type: 'string', enum: ['data-analyst', 'writer', 'coder', 'researcher'] },
task: { type: 'string', description: 'Task description' },
context: { type: 'object', description: 'Additional context' }
},
required: ['specialist', 'task']
}
}
],
a2a: {
enabled: true,
delegationTargets: ['data-analyst-agent', 'writer-agent', 'coder-agent', 'researcher-agent']
}
});
Hierarchical Delegation
┌──────────────────┐
│ Executive Agent │
└────────┬─────────┘
│
┌─────────┼─────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│Sales │ │Tech │ │Support│
│Manager│ │Manager│ │Manager│
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
┌┴┐ ┌┴┐ ┌┴┐
│ │ │ │ │ │
Specialist Agents
Agent Discovery
Agent Registry
Find agents by capability:
const agents = await client.a2a.searchAgents({
skills: ['data-analysis'],
category: 'analytics',
rating: { min: 4.0 },
limit: 10
});
for (const agent of agents.data) {
console.log({
name: agent.name,
skills: agent.skills,
rating: agent.rating,
url: agent.url
});
}
Register External Agent
Connect to agents outside MeetLoyd:
await client.a2a.registerExternalAgent({
url: 'https://external-agent.example.com',
name: 'External Data Agent',
authentication: {
type: 'bearer',
token: 'external-api-key'
}
});
Secure Delegation with Token Exchange
When Agent A delegates a task to Agent B that requires tool access, MeetLoyd uses OAuth 2.0 Token Exchange (RFC 8693) for secure delegation:
- Agent A proves its identity with a SPIFFE JWT-SVID
- Agent A exchanges the SVID for a scoped access token targeting Agent B
- The token contains only the tools Agent A is allowed to delegate
- Agent B verifies the token and checks tool-level scopes before executing
This ensures agents can never delegate more permissions than they have, and every delegation is audited.
See Agent Identity for full details on the identity stack — SPIFFE IDs, Badges, SVIDs, and token exchange.
Federation
Cross-Organization A2A
Agents from different organizations can collaborate:
Organization A Organization B
┌──────────────┐ ┌──────────────┐
│ Agent A1 │◀─────▶│ Agent B1 │
└──────────────┘ └──────────────┘
A2A Protocol
Trust Levels
| Level | Description | Verification |
|---|---|---|
unverified | Unknown agent | None |
verified | Identity confirmed | SPIFFE trust bundle |
trusted | Trusted partner | Mutual agreement + Badges |
internal | Same organization | Same tenant |
Configure Trust
await client.a2a.setTrustPolicy({
defaultTrust: 'verified',
trustedDomains: ['partner.com', 'subsidiary.com'],
blockedDomains: ['competitor.com']
});
A2A in Workflows
Use A2A within workflows:
{
id: 'external-analysis',
type: 'a2a-task',
targetAgent: 'external-data-agent',
message: {
parts: [
{ type: 'text', text: 'Analyze this dataset' },
{ type: 'data', data: '$.input.data' }
]
},
timeout: 300000, // 5 minute timeout
outputMapping: {
analysis: '$.artifacts[0].parts[0].text'
}
}
JSON-RPC Methods
A2A uses JSON-RPC 2.0:
| Method | Description |
|---|---|
task/send | Send task to agent |
task/sendSubscribe | Send with streaming |
task/get | Get task status |
task/cancel | Cancel task |
task/statusUpdate | Push status update |
task/artifactUpdate | Push artifact update |
Example Request
{
"jsonrpc": "2.0",
"method": "task/send",
"params": {
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Analyze sales data" }]
},
"metadata": {
"priority": "high"
}
},
"id": "req-123"
}
Example Response
{
"jsonrpc": "2.0",
"result": {
"id": "task-456",
"status": "working",
"createdAt": "2024-01-15T10:00:00Z"
},
"id": "req-123"
}
Best Practices
1. Define Clear Skills
skills: [
{
id: 'invoice-processing',
name: 'Invoice Processing',
description: 'Extracts data from invoices and validates against orders',
inputModes: ['file'],
outputModes: ['data']
}
]
2. Handle Failures Gracefully
try {
const task = await client.a2a.sendTask({ ... });
// Wait for completion
} catch (error) {
if (error.code === 'AGENT_UNAVAILABLE') {
// Try alternative agent or fallback
}
}
3. Set Appropriate Timeouts
const task = await client.a2a.sendTask({
targetAgent: 'slow-agent',
message: { ... },
timeout: 600000 // 10 minutes for complex tasks
});
4. Use Streaming for Long Tasks
// Better UX for tasks that take time
const stream = await client.a2a.sendTaskSubscribe({ ... });
5. Validate Agent Capabilities
// Check agent supports needed skill
const card = await client.a2a.getAgentCard('agent-456');
const hasSkill = card.skills.some(s => s.id === 'data-analysis');
Next: Learn about MCP Integration for connecting agents to external tools and resources.