How A2A Protocol Integration Works in Embabel for Agent-to-Agent Communication
Embabel implements the Google A2A protocol via a Spring Boot auto-configuration module that registers JSON-RPC endpoints for agent discovery and bidirectional communication, supporting both synchronous requests and server-sent event streaming.
The A2A protocol integration in Embabel provides a full-stack implementation of Google’s Agent-to-Agent specification within the embel-agent repository. When enabled, the framework exposes standardized HTTP endpoints that allow any A2A-compliant client to discover agent capabilities via an AgentCard and exchange messages through JSON-RPC. The architecture separates endpoint registration, request handling, and streaming concerns into distinct Spring components that activate automatically under the a2a profile.
How the A2A Server Auto-Configures in Spring Boot
Activation begins with Spring’s profile system. Adding the a2a profile to your application.yml triggers the embabel-agent-a2a-autoconfigure module, which imports AgentA2AAutoConfiguration into the application context.
# application.yml
spring:
profiles:
active: a2a
Located at embabel-agent-autoconfigure/embabel-agent-a2a-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/a2a/AgentA2AAutoConfiguration.java, this configuration class wires the core infrastructure beans: A2AEndpointRegistrar, AutonomyA2ARequestHandler, A2AStreamingHandler, and the Jackson converters required for JSON-RPC serialization. The auto-configuration runs only when the profile is present, ensuring zero overhead for applications that do not require A2A interoperability.
Dynamic Endpoint Registration with A2AEndpointRegistrar
On ApplicationReadyEvent, the A2AEndpointRegistrar scans the Spring context for all beans implementing the AgentCardHandler interface. For each discovered handler, the registrar dynamically mounts two routes:
GET /<path>/.well-known/agent.json— Returns the AgentCard JSON describing the agent’s capabilities, version, and supported methods.POST /<path>— Accepts JSON-RPC 2.0 requests for both streaming and non-streaming operations.
This registration logic resides in embabel-agent-a2a/src/main/kotlin/com/embabel/agent/a2a/server/support/A2AEndpointRegistrar.kt (lines 46–61). The registrar acts as a bridge between the Spring Web MVC infrastructure and the A2A protocol handlers, eliminating the need for manual route configuration while allowing multiple agents to coexist within a single application under distinct paths.
Handling A2A JSON-RPC Requests
The AutonomyA2ARequestHandler class implements the A2ARequestHandler interface and serves as the primary entry point for A2A protocol logic. Located at embabel-agent-a2a/src/main/kotlin/com/embabel/agent/a2a/server/support/AutonomyA2ARequestHandler.kt, this component distinguishes between synchronous and asynchronous operations:
Non-streaming methods (sendMessage, getTask, cancelTask) delegate to handleJsonRpc, returning standard JSON-RPC responses with a single result object.
Streaming methods (sendStreamingMessage, TaskResubscriptionRequest) invoke handleJsonRpcStream, which creates an SseEmitter to maintain a persistent connection. The handler converts incoming request maps using Jackson, routes them to the internal Autonomy service, and emits TaskStatusUpdateEvent objects until the final status flag ("final": true) terminates the stream.
Throughout the lifecycle, the handler fires A2ARequestEvent and A2AResponseEvent through the Spring application event publisher, allowing observability components to audit or log agent interactions without modifying core business logic.
Streaming Architecture and SSE Implementation
Streaming support relies on the A2AStreamingHandler, defined in embabel-agent-a2a/src/main/kotlin/com/embabel/agent/a2a/server/support/A2AStreamingHandler.kt. This component manages the lifecycle of server-sent events (SSE) by:
- Creating an
SseEmitterinstance for each streaming request. - Periodically emitting
TaskStatusUpdateEventJSON objects representing incremental task progress. - Handling client disconnections and completion signals to prevent resource leaks.
The streaming endpoint returns text/event-stream content type, enabling A2A clients to receive real-time updates for long-running agent tasks such as content generation or complex data processing workflows.
Practical Configuration and Usage Examples
Once the a2a profile is active, the endpoints become immediately accessible. Use the following examples to interact with an Embabel agent running on port 8080.
Retrieve the AgentCard for discovery:
curl http://localhost:8080/a2a/.well-known/agent.json
Send a non-streaming message:
curl -X POST http://localhost:8080/a2a \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "msg-1",
"method": "sendMessage",
"params": {
"message": {
"taskId": "task-123",
"contextId": "ctx-456",
"parts": [{ "type": "text", "text": "Summarize the latest news" }]
}
}
}'
Initiate a streaming request:
curl -N -X POST http://localhost:8080/a2a \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "stream-1",
"method": "sendStreamingMessage",
"params": {
"message": {
"taskId": "task-789",
"contextId": "ctx-789",
"parts": [{ "type": "text", "text": "Generate a poem about AI" }]
}
}
}'
The streaming response delivers incremental JSON events until the final status update completes the sequence.
Summary
- Auto-configuration: The
a2aSpring profile triggersAgentA2AAutoConfiguration, which wires all A2A infrastructure beans without manual setup. - Dynamic registration:
A2AEndpointRegistrarautomatically exposes/.well-known/agent.jsonand JSON-RPC POST endpoints for everyAgentCardHandlerbean. - Protocol handling:
AutonomyA2ARequestHandlerprocesses both synchronous JSON-RPC calls and asynchronous SSE streams, delegating to the core Autonomy service. - Observability: The implementation fires
A2ARequestEventandA2AResponseEventevents for cross-cutting concerns like logging and monitoring. - Standards compliance: The endpoints follow Google’s A2A specification exactly, enabling interoperability with any A2A-compatible client or agent framework.
Frequently Asked Questions
How do I enable A2A protocol support in an Embabel application?
Activate the a2a Spring profile in your application.yml or via command-line argument (--spring.profiles.active=a2a). This imports the embabel-agent-a2a-autoconfigure module, which instantiates AgentA2AAutoConfiguration and registers the required endpoints automatically.
What is the difference between streaming and non-streaming A2A requests?
Non-streaming requests (such as sendMessage or getTask) return a single JSON-RPC response immediately after processing. Streaming requests (such as sendStreamingMessage) open an SSE connection via A2AStreamingHandler and emit multiple TaskStatusUpdateEvent objects incrementally until the task reaches a final state.
Where does Embabel store the AgentCard metadata used for discovery?
The AgentCard JSON is generated by beans implementing the AgentCardHandler interface. The A2AEndpointRegistrar maps these handlers to GET /<path>/.well-known/agent.json routes at startup, serving the metadata dynamically from the handler’s implementation rather than a static file.
Can I monitor A2A request lifecycle events for auditing purposes?
Yes. AutonomyA2ARequestHandler publishes A2ARequestEvent before processing begins and A2AResponseEvent after completion. You can create Spring event listeners to capture these events for logging, metrics, or security auditing without modifying the core protocol handlers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →