The Agent-to-Agent (A2A) protocol solves a specific problem: how do you let agents built in different frameworks talk to each other without either side depending on the other’s SDK or deployment schedule? AWS just published a production implementation guide showing LangChain orchestrators on ECS calling Bedrock agents across organizational boundaries using OAuth-authenticated HTTP.
This is not about workflow orchestration inside a single framework. This is about the missing interoperability layer when your LangChain agent needs to invoke a Bedrock agent owned by a different team, and neither side can dictate the other’s technology stack.
The Framework Lock-In Problem
Most multi-agent systems assume all agents run in the same framework. LangGraph agents call other LangGraph agents. Bedrock agents invoke Bedrock action groups. When you need cross-framework communication, you typically resort to:
- Shared message queues with custom serialization
- REST APIs with bespoke authentication
- Monolithic orchestrators that embed multiple frameworks
- Manual translation layers that break when either side updates
A2A provides a standard protocol for agent-to-agent calls that works across framework boundaries. The key insight: treat each agent as an HTTP service with a capability manifest, OAuth tokens for authentication, and JSON-RPC for bidirectional calls.
Protocol Architecture
The A2A protocol defines three layers:
Discovery layer: Each agent exposes a capability manifest at a well-known endpoint. The manifest lists available tools, required parameters, and authentication requirements. Agents query this manifest before making calls.
Authentication layer: OAuth 2.0 client credentials flow. The calling agent obtains a token from the target agent’s authorization server. Tokens are scoped to specific capabilities and include expiration.
Invocation layer: JSON-RPC 2.0 over HTTPS. The calling agent sends a request with method name, parameters, and correlation ID. The target agent processes the request and returns a response or error.
State and context do not automatically flow between agents. The calling agent must explicitly pass any required context as parameters. This prevents tight coupling and makes failure boundaries explicit.
Implementation Shape
AWS demonstrates this with two agents:
LangChain orchestrator (ECS Fargate):
- Runs a LangChain agent with custom tools
- Exposes A2A endpoints via Flask
- Manages OAuth token lifecycle
- Translates LangChain tool calls to A2A invocations
Strands agent (Bedrock AgentCore):
- Bedrock agent with action groups
- A2A adapter translates action group calls to A2A protocol
- Returns responses to orchestrator
- No knowledge of LangChain internals
Neither agent imports the other’s SDK. The LangChain agent does not depend on boto3 or Bedrock libraries. The Bedrock agent does not depend on LangChain packages.
# LangChain orchestrator calling Bedrock agent via A2A
class A2AToolAdapter:
def __init__(self, agent_url, oauth_config):
self.agent_url = agent_url
self.oauth_config = oauth_config
self.token = None
def get_token(self):
if self.token and not self.token_expired():
return self.token
response = requests.post(
self.oauth_config['token_url'],
data={
'grant_type': 'client_credentials',
'client_id': self.oauth_config['client_id'],
'client_secret': self.oauth_config['client_secret'],
'scope': self.oauth_config['scope']
}
)
self.token = response.json()
return self.token
def invoke_tool(self, method, params):
token = self.get_token()
response = requests.post(
f"{self.agent_url}/a2a/invoke",
headers={
'Authorization': f"Bearer {token['access_token']}",
'Content-Type': 'application/json'
},
json={
'jsonrpc': '2.0',
'method': method,
'params': params,
'id': str(uuid.uuid4())
}
)
result = response.json()
if 'error' in result:
raise Exception(f"A2A call failed: {result['error']}")
return result['result']
The orchestrator treats the Bedrock agent as a remote tool. When the LangChain agent decides to use that tool, the adapter handles token refresh, request formatting, and error translation.
Authentication and Authorization Boundaries
OAuth client credentials flow means each agent acts as both client and server. The LangChain orchestrator is a client when calling Bedrock, but a server when exposing its own tools to other agents.
Token scopes limit what each agent can invoke. A token scoped to strands:analyze cannot invoke strands:execute. This prevents privilege escalation when agents cross organizational boundaries.
The authorization server can be:
- AWS Cognito for managed identity
- Custom OAuth provider for fine-grained control
- Third-party IdP for cross-organization scenarios
Token expiration forces periodic re-authentication. Short-lived tokens (15 minutes) reduce blast radius if credentials leak. The adapter handles refresh transparently.
Versioning and Capability Evolution
The capability manifest includes a version field. When an agent updates its tools, it increments the version. Calling agents can:
- Pin to a specific version for stability
- Request latest version for new features
- Gracefully degrade if required version unavailable
{
"version": "2.1.0",
"capabilities": [
{
"name": "analyze_strands",
"description": "Analyze financial transaction patterns",
"parameters": {
"account_id": {"type": "string", "required": true},
"date_range": {"type": "object", "required": false}
},
"deprecated": false
},
{
"name": "legacy_analyze",
"description": "Old analysis method",
"parameters": {},
"deprecated": true,
"sunset_date": "2027-01-01"
}
]
}
Deprecation warnings let calling agents migrate before breaking changes. Sunset dates provide hard deadlines. This prevents the silent breakage that happens when one team updates an internal API without coordinating.
State Management and Context Passing
A2A does not maintain session state between calls. Each invocation is stateless. If the orchestrator needs to maintain context across multiple tool calls, it must:
- Store state locally (in-memory, Redis, DynamoDB)
- Pass relevant context as parameters on each call
- Use correlation IDs to link related calls
This stateless design prevents tight coupling but requires explicit context management. The orchestrator cannot assume the Bedrock agent remembers previous calls.
For long-running operations, the protocol supports async patterns:
- Orchestrator invokes tool with
async: true - Bedrock agent returns job ID immediately
- Orchestrator polls status endpoint
- Bedrock agent returns result when ready
This prevents timeout failures when tool execution takes minutes.
Failure Modes and Circuit Breaking
Cross-framework calls introduce new failure modes:
| Failure Type | Detection | Mitigation |
|---|---|---|
| Network timeout | HTTP client timeout | Retry with exponential backoff |
| OAuth token expired | 401 response | Refresh token and retry once |
| Tool not found | 404 in capability manifest | Graceful degradation to alternative tool |
| Parameter validation | 400 response with error details | Log and surface to user |
| Circular dependency | Correlation ID tracking | Reject calls that create loops |
| Rate limiting | 429 response | Back off and queue requests |
The orchestrator implements circuit breakers per target agent. After N consecutive failures, the circuit opens and requests fail fast for M seconds. This prevents cascading failures when a downstream agent becomes unhealthy.
Correlation IDs prevent infinite loops. Each call includes a chain of parent IDs. If an agent sees its own ID in the chain, it rejects the call. This stops A calls B calls C calls A scenarios.
Deployment Topology
The AWS implementation uses:
- ECS Fargate for LangChain orchestrator (auto-scaling, no server management)
- Bedrock AgentCore for Strands agent (managed service, built-in scaling)
- Application Load Balancer for HTTPS termination and routing
- Cognito for OAuth token issuance
- CloudWatch for observability across both agents
Each agent runs in a separate VPC. Security groups restrict traffic to HTTPS on port 443. The load balancer handles TLS termination and forwards to agents over HTTP internally.
Observability requires correlation ID propagation. The orchestrator includes the correlation ID in CloudWatch logs. The Bedrock agent logs the same ID. This lets you trace a request across both systems.
When to Use A2A
Use A2A when:
- You need agents in different frameworks to collaborate
- Organizational boundaries prevent shared dependencies
- Different teams own different agents with different deployment schedules
- You want to expose agent capabilities as reusable services
- Security requires OAuth-level authentication between agents
Avoid A2A when:
- All agents run in the same framework (use native orchestration)
- Latency budget cannot tolerate HTTP round trips (use in-process calls)
- You control both sides and can coordinate deployments (simpler to use shared SDK)
- The overhead of OAuth token management exceeds the benefit
- Your agents need tight coupling and shared state (A2A enforces loose coupling)
Technical Verdict
A2A is the first production-ready protocol for cross-framework agent communication. It solves the real problem of letting LangChain and Bedrock agents talk without either side depending on the other’s SDK. The OAuth authentication and capability versioning make it viable across organizational boundaries.
The stateless design and explicit context passing prevent tight coupling but require more orchestration code. You trade simplicity for flexibility. If you control both agents and they live in the same framework, native orchestration is simpler. If you need interoperability across teams or vendors, A2A provides the missing layer.
The biggest risk is operational complexity. You now have OAuth tokens to manage, capability manifests to version, and circuit breakers to tune. The AWS implementation guide provides working code, but you will need to adapt it to your authentication provider and deployment topology.
Use this when framework lock-in is blocking collaboration. Skip it when you can standardize on a single framework.