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.
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:
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.
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.
AWS demonstrates this with two agents:
LangChain orchestrator (ECS Fargate):
Strands agent (Bedrock AgentCore):
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.
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.
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:
Token expiration forces periodic re-authentication. Short-lived tokens (15 minutes) reduce blast radius if credentials leak. The adapter handles refresh transparently.
The capability manifest includes a version field. When an agent updates its tools, it increments the version. Calling agents can:
{
"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.
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:
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:
async: true
This prevents timeout failures when tool execution takes minutes.
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.
The AWS implementation uses:
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.
Use A2A when:
Avoid A2A when:
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.