Skip to main content

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:

ScopeProtocolWhen to Use
Intra-AppA2ADirect agent-to-agent within same app
Cross-AppSLIMAgents in different apps (same tenant)
Cross-OrgSLIMAgents in different organizations
A2A vs SLIM

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
  1. Agent A sends a task to Agent B
  2. Agent B processes the task
  3. Agent B returns results (or status updates)
  4. 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:

  1. Agent A proves its identity with a SPIFFE JWT-SVID
  2. Agent A exchanges the SVID for a scoped access token targeting Agent B
  3. The token contains only the tools Agent A is allowed to delegate
  4. 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.

tip

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

LevelDescriptionVerification
unverifiedUnknown agentNone
verifiedIdentity confirmedSPIFFE trust bundle
trustedTrusted partnerMutual agreement + Badges
internalSame organizationSame 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:

MethodDescription
task/sendSend task to agent
task/sendSubscribeSend with streaming
task/getGet task status
task/cancelCancel task
task/statusUpdatePush status update
task/artifactUpdatePush 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.