A2A Protocol Adapter SDK for integrating various agent frameworks
Project description
A2A Adapter
Open Source A2A Protocol Adapter SDK for Different Agent Frameworks
A Python SDK that enables seamless integration of various agent frameworks (n8n, CrewAI, LangChain, etc.) with the A2A (Agent-to-Agent) Protocol. Build interoperable AI agent systems that can communicate across different platforms and frameworks.
Features
โจ Framework Agnostic: Integrate n8n workflows, CrewAI crews, LangChain chains, or custom agents ๐ Simple API: 3-line setup to expose any agent as A2A-compliant ๐ Streaming Support: Built-in streaming for LangChain and custom adapters ๐ฏ Type Safe: Leverages official A2A SDK types ๐ง Extensible: Easy to add custom adapters for new frameworks ๐ฆ Minimal Dependencies: Optional dependencies per framework
Architecture
โโโโโโโโโโโโโโโโโโโ
โ A2A Caller โ (Other A2A Agents)
โโโโโโโโโโฌโโโโโโโโโ
โ A2A Protocol (HTTP + JSON-RPC 2.0)
โผ
โโโโโโโโโโโโโโโโโโโ
โ A2A Adapter โ (This SDK)
โ - N8n โ
โ - CrewAI โ
โ - LangChain โ
โ - Custom โ
โโโโโโโโโโฌโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโ
โ Your Agent โ (n8n workflow / CrewAI crew / Chain)
โโโโโโโโโโโโโโโโโโโ
Single-Agent Design: Each server hosts exactly one agent. Multi-agent orchestration is handled externally via A2A protocol or orchestration frameworks like LangGraph.
See ARCHITECTURE.md for detailed design documentation.
Installation
Basic Installation
pip install a2a-adapter
With Framework Support
# For n8n (HTTP webhooks)
pip install a2a-adapter
# For CrewAI
pip install a2a-adapter[crewai]
# For LangChain
pip install a2a-adapter[langchain]
# For LangGraph
pip install a2a-adapter[langgraph]
# Install all frameworks
pip install a2a-adapter[all]
# For development
pip install a2a-adapter[dev]
Quick Start
๐ Easy Start with Examples
For the fastest way to get started, use the included examples:
# Clone and setup
git clone <repository>
cd a2a-adapter
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e .
# Start an agent
./run_agent.sh n8n # N8n workflow agent
./run_agent.sh crewai # CrewAI agent
./run_agent.sh langchain # LangChain agent
# Stop with Ctrl+C
Environment Variables:
export N8N_WEBHOOK_URL="https://your-n8n.com/webhook/your-workflow"
๐ Manual Setup
1. N8n Workflow Agent
Expose an n8n workflow as an A2A agent:
import asyncio
from a2a_adapter import load_a2a_agent, serve_agent
from a2a.types import AgentCard
async def main():
# Load adapter
adapter = await load_a2a_agent({
"adapter": "n8n",
"webhook_url": "https://n8n.example.com/webhook/math",
"timeout": 30
})
# Define agent card
card = AgentCard(
name="Math Agent",
description="Performs mathematical calculations via n8n"
)
# Start server
serve_agent(agent_card=card, adapter=adapter, port=9000)
asyncio.run(main())
2. CrewAI Agent
Expose a CrewAI crew as an A2A agent:
import asyncio
from crewai import Crew, Agent, Task
from a2a_adapter import load_a2a_agent, serve_agent
from a2a.types import AgentCard
# Create your crew
crew = Crew(
agents=[...],
tasks=[...],
verbose=True
)
async def main():
adapter = await load_a2a_agent({
"adapter": "crewai",
"crew": crew
})
card = AgentCard(
name="Research Crew",
description="Multi-agent research team"
)
serve_agent(agent_card=card, adapter=adapter, port=8001)
asyncio.run(main())
3. LangChain Agent (with Streaming)
Expose a LangChain chain with streaming support:
import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from a2a_adapter import load_a2a_agent, serve_agent
from a2a.types import AgentCard
# Create chain
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("user", "{input}")
])
llm = ChatOpenAI(model="gpt-4o-mini", streaming=True)
chain = prompt | llm
async def main():
adapter = await load_a2a_agent({
"adapter": "langchain",
"runnable": chain,
"input_key": "input"
})
card = AgentCard(
name="Chat Agent",
description="Streaming chat agent powered by GPT-4"
)
serve_agent(agent_card=card, adapter=adapter, port=8002)
asyncio.run(main())
4. Custom Adapter
Create a custom agent with any async function:
import asyncio
from a2a_adapter import load_a2a_agent, serve_agent
from a2a.types import AgentCard
async def my_agent_function(inputs: dict) -> str:
"""Your custom agent logic."""
message = inputs["message"]
return f"Echo: {message}"
async def main():
adapter = await load_a2a_agent({
"adapter": "callable",
"callable": my_agent_function
})
card = AgentCard(
name="Echo Agent",
description="Simple echo agent"
)
serve_agent(agent_card=card, adapter=adapter, port=8003)
asyncio.run(main())
Advanced Usage
Custom Adapter Class
For full control, subclass BaseAgentAdapter:
from a2a_adapter import BaseAgentAdapter
from a2a.types import Message, MessageSendParams, TextPart
class SentimentAnalyzer(BaseAgentAdapter):
async def to_framework(self, params: MessageSendParams):
# Extract user message
text = params.messages[-1].content[0].text
return {"text": text}
async def call_framework(self, framework_input, params):
# Your analysis logic
sentiment = analyze_sentiment(framework_input["text"])
return {"sentiment": sentiment}
async def from_framework(self, framework_output, params):
# Convert to A2A Message
return Message(
role="assistant",
content=[TextPart(
type="text",
text=f"Sentiment: {framework_output['sentiment']}"
)]
)
# Use your custom adapter
adapter = SentimentAnalyzer()
serve_agent(agent_card=card, adapter=adapter, port=8004)
Streaming Custom Adapter
Implement handle_stream() for streaming responses:
class StreamingAdapter(BaseAgentAdapter):
async def handle_stream(self, params: MessageSendParams):
"""Yield SSE-compatible events."""
for chunk in generate_response_chunks():
yield {
"event": "message",
"data": json.dumps({"type": "content", "content": chunk})
}
yield {
"event": "done",
"data": json.dumps({"status": "completed"})
}
def supports_streaming(self):
return True
Using with LangGraph
Integrate A2A agents into LangGraph workflows:
from langgraph.graph import StateGraph
from a2a.client import A2AClient
# Create A2A client
math_agent = A2AClient(base_url="http://localhost:9000")
# Use in LangGraph node
async def call_math_agent(state):
response = await math_agent.send_message(
MessageSendParams(messages=[...])
)
return {"result": response}
# Add to graph
graph = StateGraph(...)
graph.add_node("math", call_math_agent)
See examples/06_langgraph_single_agent.py for complete example.
Configuration
N8n Adapter
{
"adapter": "n8n",
"webhook_url": "https://n8n.example.com/webhook/agent", # Required
"timeout": 30, # Optional, default: 30
"headers": { # Optional
"Authorization": "Bearer token"
}
}
CrewAI Adapter
{
"adapter": "crewai",
"crew": crew_instance, # Required: CrewAI Crew object
"inputs_key": "inputs" # Optional, default: "inputs"
}
LangChain Adapter
{
"adapter": "langchain",
"runnable": chain, # Required: Any Runnable
"input_key": "input", # Optional, default: "input"
"output_key": None # Optional, extracts specific key from output
}
Callable Adapter
{
"adapter": "callable",
"callable": async_function, # Required: async function
"supports_streaming": False # Optional, default: False
}
Examples
The examples/ directory contains complete working examples:
- 01_single_n8n_agent.py - N8n workflow agent
- 02_single_crewai_agent.py - CrewAI multi-agent crew
- 03_single_langchain_agent.py - LangChain streaming agent
- 04_single_agent_client.py - A2A client for testing
- 05_custom_adapter.py - Custom adapter implementations
- 06_langgraph_single_agent.py - LangGraph + A2A integration
Run any example:
# Start an agent server
python examples/01_single_n8n_agent.py
# In another terminal, test with client
python examples/04_single_agent_client.py
Testing
# Install dev dependencies
pip install a2a-adapter[dev]
# Run unit tests
pytest tests/unit/
# Run integration tests (requires framework dependencies)
pytest tests/integration/
# Run all tests
pytest
API Reference
Core Functions
load_a2a_agent(config: Dict[str, Any]) -> BaseAgentAdapter
Factory function to create an adapter from configuration.
Args:
config: Dictionary with"adapter"key and framework-specific options
Returns:
- Configured
BaseAgentAdapterinstance
Raises:
ValueError: If adapter type is unknown or required config is missingImportError: If required framework package is not installed
build_agent_app(agent_card: AgentCard, adapter: BaseAgentAdapter) -> ASGIApp
Build an ASGI application for serving an A2A agent.
Args:
agent_card: A2A AgentCard describing the agentadapter: Adapter instance
Returns:
- ASGI application ready to be served
serve_agent(agent_card, adapter, host="0.0.0.0", port=9000, **kwargs)
Start serving an A2A agent (convenience function).
Args:
agent_card: A2A AgentCardadapter: Adapter instancehost: Host address (default: "0.0.0.0")port: Port number (default: 9000)**kwargs: Additional arguments passed touvicorn.run()
BaseAgentAdapter
Abstract base class for all adapters.
Methods
async def handle(params: MessageSendParams) -> Message | Task
Handle a non-streaming A2A message request.
async def handle_stream(params: MessageSendParams) -> AsyncIterator[Dict]
Handle a streaming A2A message request. Override in subclasses that support streaming.
@abstractmethod async def to_framework(params: MessageSendParams) -> Any
Convert A2A message parameters to framework-specific input.
@abstractmethod async def call_framework(framework_input: Any, params: MessageSendParams) -> Any
Execute the underlying agent framework.
@abstractmethod async def from_framework(framework_output: Any, params: MessageSendParams) -> Message | Task
Convert framework output to A2A Message or Task.
def supports_streaming() -> bool
Check if this adapter supports streaming responses.
Framework Support
| Framework | Adapter | Streaming | Status |
|---|---|---|---|
| n8n | N8nAgentAdapter |
โ | โ Stable |
| CrewAI | CrewAIAgentAdapter |
โ | โ Stable |
| LangChain | LangChainAgentAdapter |
โ | โ Stable |
| Custom Function | CallableAgentAdapter |
โ Optional | โ Stable |
| AutoGen | - | - | ๐ Planned |
| Semantic Kernel | - | - | ๐ Planned |
Contributing
We welcome contributions! To add support for a new framework:
- Create
a2a_adapter/integrations/{framework}.py - Implement a class extending
BaseAgentAdapter - Add to
loader.pyfactory function - Update
integrations/__init__.py - Add optional dependency to
pyproject.toml - Create an example in
examples/ - Add tests in
tests/ - Update this README
See ARCHITECTURE.md for detailed guidance.
Roadmap
- Core adapter abstraction
- N8n adapter
- CrewAI adapter
- LangChain adapter with streaming
- Callable adapter
- Comprehensive examples
- Task support (async execution pattern)
- Artifact support (file uploads/downloads)
- AutoGen adapter
- Semantic Kernel adapter
- Haystack adapter
- Middleware system (logging, metrics, rate limiting)
- Configuration validation with Pydantic
- Docker images for quick deployment
FAQ
Q: Can I run multiple agents in one process?
A: This SDK is designed for single-agent-per-process. For multi-agent systems, run multiple A2A servers and orchestrate them externally using the A2A protocol or tools like LangGraph.
Q: Does this support the latest A2A protocol version?
A: Yes, we use the official A2A SDK which stays up-to-date with protocol changes.
Q: Can I use this with my custom agent framework?
A: Absolutely! Use the CallableAgentAdapter for simple cases or subclass BaseAgentAdapter for full control.
Q: What about authentication and rate limiting?
A: These concerns are handled at the infrastructure level (reverse proxy, API gateway) or by the official A2A SDK. Adapters focus solely on framework integration.
Q: How do I debug adapter issues?
A: Set log_level="debug" in serve_agent() and check logs. Each adapter logs framework calls and responses.
License
MIT License - see LICENSE file for details.
Credits
Built with โค๏ธ by HYBRO AI
Powered by the A2A Protocol
Support
- ๐ Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
Star โญ this repo if you find it useful!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file a2a_adapter-0.1.0.tar.gz.
File metadata
- Download URL: a2a_adapter-0.1.0.tar.gz
- Upload date:
- Size: 29.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d70d2c3257274c1a4005164f93d72295d39943b7def18c7fad05bd0573f5a20
|
|
| MD5 |
0f223ee88ac37db1f4dd4b71e8135eb3
|
|
| BLAKE2b-256 |
f8127544934d7d2fde14e29f39a851b673b8f1382a7da90ec0fe1a24976deee0
|
File details
Details for the file a2a_adapter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: a2a_adapter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a392063b245674723c90ca37e65582a913f3da4b080c20adeef1efc6c7c059cf
|
|
| MD5 |
4cf4179ef880f903f930e0a9dd3f31d7
|
|
| BLAKE2b-256 |
bcdf7203a26fe18ffe11cc80754ccedc86ef2c888b057a1ac7dae1d8df26a176
|