20 KiB
Agent Communication Protocol (ACP)
The Agent Communication Protocol enables parallel subagents with isolated contexts to coordinate and share information through a structured file-based system.
Core Principles
- Asynchronous - Agents don't block each other
- Discoverable - Any agent can read any report
- Persistent - Survives agent crashes and restarts
- Transparent - Complete event log for debugging
- Atomic - File operations are append-only or replace-whole
- Orchestratable - Coordinator manages dependencies
Directory Structure
.claude/agents/context/{session-id}/
├── coordination.json # Status tracking and dependencies
├── messages.jsonl # Append-only event log
├── reports/ # Standardized agent outputs
│ ├── {agent-name}.json
│ └── ...
└── data/ # Shared data artifacts
├── {artifact-name}.json
└── ...
Session ID
Each automation generation gets a unique session ID (UUID):
SESSION_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
export CLAUDE_SESSION_ID="${SESSION_ID}"
All agents receive this session ID and use it to locate the context directory.
Communication Components
1. Coordination File (coordination.json)
Central status tracking for all agents.
Structure:
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"started_at": "2025-01-23T10:00:00Z",
"project_type": "web_app",
"project_path": "/path/to/project",
"agents": {
"security-analyzer": {
"status": "completed",
"started_at": "2025-01-23T10:00:00Z",
"completed_at": "2025-01-23T10:05:00Z",
"report_path": "reports/security-analyzer.json",
"dependencies": [],
"progress": "Analysis complete"
},
"performance-analyzer": {
"status": "in_progress",
"started_at": "2025-01-23T10:00:00Z",
"progress": "Analyzing database queries...",
"dependencies": []
},
"skill-generator": {
"status": "waiting",
"dependencies": ["security-analyzer", "performance-analyzer", "code-quality-analyzer"],
"reason": "Waiting for analysis agents to complete"
}
}
}
Agent Status Values:
waiting- Not started, may have dependenciesin_progress- Currently executingcompleted- Finished successfullyfailed- Encountered error
Reading Coordination:
# Check all agent statuses
jq '.agents' .claude/agents/context/${SESSION_ID}/coordination.json
# Check specific agent
jq '.agents["security-analyzer"]' .claude/agents/context/${SESSION_ID}/coordination.json
# List completed agents
jq '.agents | to_entries | map(select(.value.status == "completed")) | map(.key)' \
.claude/agents/context/${SESSION_ID}/coordination.json
# List waiting agents with dependencies
jq '.agents | to_entries | map(select(.value.status == "waiting")) | map({name: .key, deps: .value.dependencies})' \
.claude/agents/context/${SESSION_ID}/coordination.json
Updating Coordination:
# Update status to in_progress
cat .claude/agents/context/${SESSION_ID}/coordination.json | \
jq '.agents["my-agent"] = {
"status": "in_progress",
"started_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"progress": "Starting analysis",
"dependencies": []
}' > /tmp/coord.json && \
mv /tmp/coord.json .claude/agents/context/${SESSION_ID}/coordination.json
# Update to completed
cat .claude/agents/context/${SESSION_ID}/coordination.json | \
jq '.agents["my-agent"].status = "completed" |
.agents["my-agent"].completed_at = "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" |
.agents["my-agent"].report_path = "reports/my-agent.json"' > /tmp/coord.json && \
mv /tmp/coord.json .claude/agents/context/${SESSION_ID}/coordination.json
2. Message Bus (messages.jsonl)
Append-only log of all events. Each line is a JSON object.
Event Types:
status- Progress updatesfinding- Discovery of issues or insightserror- Failures or problemsdata- Data artifact creationcompleted- Agent completion announcement
Event Format:
{"timestamp":"2025-01-23T10:00:00Z","from":"security-analyzer","type":"status","message":"Starting security analysis"}
{"timestamp":"2025-01-23T10:02:15Z","from":"security-analyzer","type":"finding","severity":"high","data":{"title":"SQL Injection Risk","location":"src/db/queries.ts:42"}}
{"timestamp":"2025-01-23T10:03:00Z","from":"security-analyzer","type":"data","artifact":"data/vulnerabilities.json","description":"Detailed vulnerability data"}
{"timestamp":"2025-01-23T10:05:00Z","from":"security-analyzer","type":"completed","message":"Analysis complete. Found 5 high-severity issues."}
Writing Events:
# Log status update
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-agent\",\"type\":\"status\",\"message\":\"Starting analysis\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Log finding
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-agent\",\"type\":\"finding\",\"severity\":\"high\",\"data\":{\"title\":\"Issue found\",\"location\":\"file:line\"}}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Log completion
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-agent\",\"type\":\"completed\",\"message\":\"Analysis complete\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
Reading Events:
# Watch live events
tail -f .claude/agents/context/${SESSION_ID}/messages.jsonl | jq
# Get events from specific agent
jq 'select(.from == "security-analyzer")' .claude/agents/context/${SESSION_ID}/messages.jsonl
# Get events by type
jq 'select(.type == "finding")' .claude/agents/context/${SESSION_ID}/messages.jsonl
# Get high-severity findings
jq 'select(.type == "finding" and .severity == "high")' .claude/agents/context/${SESSION_ID}/messages.jsonl
# Count events by type
jq -s 'group_by(.type) | map({type: .[0].type, count: length})' \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Timeline of agent activity
jq -s 'sort_by(.timestamp) | .[] | "\(.timestamp) [\(.from)] \(.type): \(.message // .data.title // "no message")"' \
.claude/agents/context/${SESSION_ID}/messages.jsonl -r
3. Agent Reports (reports/{agent-name}.json)
Standardized output from each agent.
Standard Report Format:
{
"agent_name": "security-analyzer",
"timestamp": "2025-01-23T10:05:00Z",
"status": "completed",
"summary": "Brief 2-3 sentence overview of findings",
"findings": [
{
"type": "issue|recommendation|info",
"severity": "high|medium|low",
"title": "Short title",
"description": "Detailed description of the finding",
"location": "file:line or component",
"recommendation": "What to do about it",
"example": "Code snippet or example (optional)"
}
],
"metrics": {
"items_analyzed": 150,
"issues_found": 5,
"high_severity": 2,
"medium_severity": 2,
"low_severity": 1,
"time_taken": "2m 34s"
},
"data_artifacts": [
"data/vulnerabilities.json",
"data/dependency-graph.json"
],
"next_actions": [
"Fix SQL injection in queries.ts",
"Update vulnerable dependencies",
"Add input validation to API routes"
],
"recommendations_for_automation": [
"Skill: SQL injection checker that runs on code changes",
"Command: /security-scan for quick manual checks",
"Hook: Validate queries on PreToolUse for Write operations",
"MCP: Integrate with security scanning service"
]
}
Writing Reports:
cat > .claude/agents/context/${SESSION_ID}/reports/my-agent.json << 'EOF'
{
"agent_name": "my-agent",
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"status": "completed",
"summary": "Your summary here",
"findings": [
{
"type": "issue",
"severity": "high",
"title": "Finding title",
"description": "Detailed description",
"location": "src/file.ts:42",
"recommendation": "How to fix"
}
],
"metrics": {
"items_analyzed": 100,
"issues_found": 3
},
"next_actions": ["Action 1", "Action 2"],
"recommendations_for_automation": ["Suggestion 1", "Suggestion 2"]
}
EOF
Reading Reports:
# Read specific report
cat .claude/agents/context/${SESSION_ID}/reports/security-analyzer.json | jq
# Get summaries from all reports
jq -r '.summary' .claude/agents/context/${SESSION_ID}/reports/*.json
# Get all high-severity findings
jq -s 'map(.findings[]) | map(select(.severity == "high"))' \
.claude/agents/context/${SESSION_ID}/reports/*.json
# Aggregate metrics
jq -s '{
total_findings: map(.findings | length) | add,
high_severity: map(.findings[] | select(.severity == "high")) | length,
automation_opportunities: map(.recommendations_for_automation) | flatten | length
}' .claude/agents/context/${SESSION_ID}/reports/*.json
# List all data artifacts
jq -s 'map(.data_artifacts) | flatten | unique' \
.claude/agents/context/${SESSION_ID}/reports/*.json
4. Data Artifacts (data/{artifact-name}.json)
Shared data files for detailed information exchange.
Agents can create data artifacts when:
- Report would be too large
- Other agents need raw data
- Detailed analysis is needed
- Data should be reusable
Example Artifacts:
# Vulnerability details
data/vulnerabilities.json
# Performance profiling results
data/performance-profile.json
# Dependency graph
data/dependency-graph.json
# Test coverage report
data/test-coverage.json
# Code complexity metrics
data/complexity-metrics.json
Creating Artifacts:
cat > .claude/agents/context/${SESSION_ID}/data/vulnerabilities.json << 'EOF'
{
"scan_date": "2025-01-23T10:05:00Z",
"vulnerabilities": [
{
"id": "SQL-001",
"type": "SQL Injection",
"severity": "high",
"file": "src/db/queries.ts",
"line": 42,
"code": "db.query(`SELECT * FROM users WHERE id = ${userId}`)",
"fix": "db.query('SELECT * FROM users WHERE id = ?', [userId])",
"cwe": "CWE-89",
"references": ["https://cwe.mitre.org/data/definitions/89.html"]
}
]
}
EOF
# Log artifact creation
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"security-analyzer\",\"type\":\"data\",\"artifact\":\"data/vulnerabilities.json\",\"description\":\"Detailed vulnerability data\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
Reading Artifacts:
# Read artifact
cat .claude/agents/context/${SESSION_ID}/data/vulnerabilities.json | jq
# Find all artifacts
ls .claude/agents/context/${SESSION_ID}/data/
# Check which agents created artifacts
jq 'select(.type == "data") | {from: .from, artifact: .artifact}' \
.claude/agents/context/${SESSION_ID}/messages.jsonl
Agent Workflows
Analysis Agent Workflow
# 1. Check coordination
jq '.agents' .claude/agents/context/${SESSION_ID}/coordination.json
# 2. Read prerequisite reports (if any)
cat .claude/agents/context/${SESSION_ID}/reports/dependency-analyzer.json | jq
# 3. Announce startup
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-analyzer\",\"type\":\"status\",\"message\":\"Starting analysis\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# 4. Update coordination
# [Update to in_progress as shown above]
# 5. Perform analysis and log progress
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-analyzer\",\"type\":\"status\",\"message\":\"Analyzed 50% of codebase\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# 6. Write report
# [Create report as shown above]
# 7. Create data artifacts (if needed)
# [Create artifacts as shown above]
# 8. Update coordination to completed
# [Update status as shown above]
# 9. Announce completion
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-analyzer\",\"type\":\"completed\",\"message\":\"Analysis complete. Found X issues.\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
Implementation Agent Workflow
# 1. Wait for analysis agents
while true; do
COMPLETED=$(jq -r '.agents | to_entries | map(select(.key | endswith("analyzer")) and .value.status == "completed") | length' \
.claude/agents/context/${SESSION_ID}/coordination.json)
TOTAL=$(jq -r '.agents | to_entries | map(select(.key | endswith("analyzer")) | length' \
.claude/agents/context/${SESSION_ID}/coordination.json)
if [ "$COMPLETED" -eq "$TOTAL" ]; then
break
fi
sleep 2
done
# 2. Read all analysis reports
for report in .claude/agents/context/${SESSION_ID}/reports/*-analyzer.json; do
cat "$report" | jq '.summary, .recommendations_for_automation'
done
# 3. Synthesize findings and make decisions
# [Aggregate recommendations, prioritize, decide what to generate]
# 4. Generate artifacts (skills, commands, hooks)
# [Create actual files in .claude/skills/, .claude/commands/, etc.]
# 5. Write report
# [Document what was generated and why]
# 6. Update coordination
# [Mark as completed]
Coordinator Agent Workflow
# 1. Launch analysis agents in parallel
echo "Launching analysis agents: security, performance, quality, dependency, documentation"
# 2. Monitor progress
watch -n 2 'cat .claude/agents/context/${SESSION_ID}/coordination.json | jq ".agents"'
# 3. Wait for all analysis agents to complete
while true; do
COMPLETED=$(jq -r '.agents | to_entries | map(select(.key | endswith("analyzer")) and .value.status == "completed") | length' \
.claude/agents/context/${SESSION_ID}/coordination.json)
TOTAL=$(jq -r '.agents | to_entries | map(select(.key | endswith("analyzer")) | length' \
.claude/agents/context/${SESSION_ID}/coordination.json)
if [ "$COMPLETED" -eq "$TOTAL" ]; then
break
fi
sleep 5
done
# 4. Synthesize all findings
jq -s '{
total_findings: map(.findings | length) | add,
high_severity: map(.findings[] | select(.severity == "high")) | length,
automation_suggestions: map(.recommendations_for_automation) | flatten
}' .claude/agents/context/${SESSION_ID}/reports/*-analyzer.json
# 5. Make decisions on what to generate
# [Based on synthesis, decide which skills/commands/hooks/MCP to create]
# 6. Launch implementation agents in parallel
echo "Launching implementation agents: skill-gen, command-gen, hook-gen, mcp-config"
# 7. Monitor implementation
# [Similar monitoring loop]
# 8. Launch validation agents sequentially
echo "Launching integration-tester"
# [Wait for completion]
echo "Launching documentation-validator"
# 9. Generate final documentation
# [Create AUTOMATION_README.md, QUICK_REFERENCE.md]
# 10. Report to user
# [Summarize what was created and how to use it]
Error Handling
Detecting Failures
# Check for failed agents
jq '.agents | to_entries | map(select(.value.status == "failed"))' \
.claude/agents/context/${SESSION_ID}/coordination.json
# Find error events
jq 'select(.type == "error")' .claude/agents/context/${SESSION_ID}/messages.jsonl
Recovery Strategies
- Retry Agent - Re-run the failed agent
- Continue Without - Proceed if agent was non-critical
- Manual Intervention - Fix issue and resume
- Partial Results - Check if agent wrote partial report
Logging Errors
# Log error with details
echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"my-agent\",\"type\":\"error\",\"message\":\"Failed to analyze X\",\"error\":\"Error details here\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Update coordination
cat .claude/agents/context/${SESSION_ID}/coordination.json | \
jq '.agents["my-agent"].status = "failed" |
.agents["my-agent"].error = "Error details"' > /tmp/coord.json && \
mv /tmp/coord.json .claude/agents/context/${SESSION_ID}/coordination.json
Best Practices
For Agents
- Check dependencies first - Read coordination before starting
- Log frequently - Write to message bus for transparency
- Standardize reports - Follow the exact JSON format
- Be atomic - Complete write-then-move for files
- Handle errors gracefully - Log errors, update status
- Provide actionable output - Clear recommendations
- Suggest automation - Think about reusable patterns
For Coordinators
- Launch in parallel when possible - Maximize concurrency
- Respect dependencies - Don't start agents before prerequisites
- Monitor actively - Check coordination periodically
- Synthesize thoroughly - Read all reports before decisions
- Validate results - Test generated automation
- Document completely - Explain what was created and why
For File Operations
- Append-only for logs - Use
>>for messages.jsonl - Replace-whole for state - Use write-to-temp-then-move for coordination.json
- Unique names - Avoid conflicts in data artifacts
- JSON formatting - Always use valid JSON
- Timestamps - ISO 8601 format (UTC)
Example Session
Full example of 3 agents communicating:
# Session starts
SESSION_ID="abc123"
mkdir -p ".claude/agents/context/${SESSION_ID}"/{reports,data}
# Agent 1: Security Analyzer starts
echo "{\"timestamp\":\"2025-01-23T10:00:00Z\",\"from\":\"security\",\"type\":\"status\",\"message\":\"Starting\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Agent 2: Performance Analyzer starts (parallel)
echo "{\"timestamp\":\"2025-01-23T10:00:01Z\",\"from\":\"performance\",\"type\":\"status\",\"message\":\"Starting\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Security finds issue
echo "{\"timestamp\":\"2025-01-23T10:02:00Z\",\"from\":\"security\",\"type\":\"finding\",\"severity\":\"high\",\"data\":{\"title\":\"SQL Injection\"}}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Security completes
echo "{\"timestamp\":\"2025-01-23T10:05:00Z\",\"from\":\"security\",\"type\":\"completed\",\"message\":\"Found 5 issues\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Creates report: reports/security.json
# Performance completes
echo "{\"timestamp\":\"2025-01-23T10:06:00Z\",\"from\":\"performance\",\"type\":\"completed\",\"message\":\"Found 3 bottlenecks\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Creates report: reports/performance.json
# Coordinator reads both reports
cat .claude/agents/context/${SESSION_ID}/reports/security.json | jq .summary
cat .claude/agents/context/${SESSION_ID}/reports/performance.json | jq .summary
# Coordinator launches implementation agent
echo "{\"timestamp\":\"2025-01-23T10:07:00Z\",\"from\":\"coordinator\",\"type\":\"status\",\"message\":\"Launching skill generator\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
# Skill generator reads analysis reports
jq -s 'map(.recommendations_for_automation) | flatten' \
.claude/agents/context/${SESSION_ID}/reports/*.json
# Skill generator creates artifacts
# [Generates skills based on recommendations]
# Complete
echo "{\"timestamp\":\"2025-01-23T10:10:00Z\",\"from\":\"coordinator\",\"type\":\"completed\",\"message\":\"Automation system ready\"}" >> \
.claude/agents/context/${SESSION_ID}/messages.jsonl
Protocol Guarantees
What ACP Guarantees
✅ Visibility - All agents can see all reports ✅ Ordering - Events have timestamps ✅ Persistence - Survives crashes ✅ Transparency - Complete audit trail ✅ Atomicity - No partial writes (when using temp files)
What ACP Does NOT Guarantee
❌ Real-time coordination - File-based, not instant ❌ Locking - No distributed locks (use temp files + move) ❌ Transactions - No multi-file atomicity ❌ Ordering of concurrent writes - Append-only log doesn't guarantee order
Summary
The Agent Communication Protocol provides a simple, robust way for parallel subagents to:
- Coordinate - Via coordination.json
- Communicate findings - Via standardized reports
- Share data - Via data artifacts
- Maintain transparency - Via message bus
- Enable orchestration - Via dependency tracking
All communication is file-based, making it:
- Easy to implement
- Easy to debug
- Easy to monitor
- Reliable and persistent
- Language-agnostic
This protocol enables the meta-skill to generate sophisticated multi-agent automation systems that work reliably in parallel.