A2A Client
The A2A client enables you to communicate with A2A-compliant agents over the network. It provides a complete implementation of the A2A protocol specification, handling agent discovery, message exchange, task management, and real-time streaming responses.
Overview
The A2A client acts as a bridge between your application and A2A-compliant agents. It orchestrates the entire communication lifecycle while maintaining protocol compliance and providing robust session management.
Core components
A2AClient
The main client class implementing the complete A2A protocol. It serves as the central coordinator that:
- Manages connections and agent discovery through pluggable resolvers
- Orchestrates message exchange and task operations with automatic protocol compliance
- Handles streaming responses and real-time communication when supported by agents
- Provides comprehensive error handling and fallback mechanisms for robust applications
The A2AClient
accepts two required parameters:
ClientTransport
which handles network communication layerAgentCardResolver
which handles agent discovery and metadata retrieval
The A2AClient
interface provides several key methods for interacting with A2A agents:
connect
method - To connect to the agent and retrieve its capabilities, which discovers what the agent can do and caches the AgentCardsendMessage
method - To send a message to the agent and receive a single response for simple request-response patternssendMessageStreaming
method - To send a message with streaming support for real-time responses, which returns a Flow of events including partial messages and task updatesgetTask
method - To query the status and details of a specific taskcancelTask
method - To cancel a running task if the agent supports cancellationcachedAgentCard
method - To get the cached agent card without making a network request, which returns null if connect hasn't been called yet
ClientTransport
The ClientTransport
interface handles the low-level network communication while the A2A client manages the protocol
logic.
It abstracts away transport-specific details, allowing you to use different protocols seamlessly.
HTTP JSON-RPC Transport
The most common transport for A2A agents:
val transport = HttpJSONRPCClientTransport(
url = "https://agent.example.com/a2a", // Agent endpoint URL
httpClient = HttpClient(CIO) { // Optional: custom HTTP client
install(ContentNegotiation) {
json()
}
install(HttpTimeout) {
requestTimeoutMillis = 30000
}
}
)
AgentCardResolver
The AgentCardResolver
interface retrieves agent metadata and capabilities.
It enables agent discovery from various sources and supports caching strategies for optimal performance.
URL Agent Card Resolver
Fetch agent cards from HTTP endpoints following A2A conventions:
val agentCardResolver = UrlAgentCardResolver(
baseUrl = "https://agent.example.com", // Base URL of the agent service
path = "/.well-known/agent-card.json", // Standard agent card location
httpClient = HttpClient(CIO), // Optional: custom HTTP client
)
Quickstart
1. Create the Client
Define the transport and agent card resolver and create the client.
// HTTP JSON-RPC transport
val transport = HttpJSONRPCClientTransport(
url = "https://agent.example.com/a2a"
)
// Agent card resolver
val agentCardResolver = UrlAgentCardResolver(
baseUrl = "https://agent.example.com",
path = "/.well-known/agent-card.json"
)
// Create client
val client = A2AClient(transport, agentCardResolver)
2. Connect and Discover
Connect to the agent and retrieve its card. Having agent's card enables you to query its capabilities and perform other operations, for example, check if it supports streaming.
// Connect and retrieve agent capabilities
client.connect()
val agentCard = client.cachedAgentCard()
println("Connected to: ${agentCard.name}")
println("Supports streaming: ${agentCard.capabilities.streaming}")
3. Send Messages
Send a message to the agent and receive a single response. The response can be either the message if the agent responded directly, or a task event if the agent is performing a task.
val message = Message(
messageId = UUID.randomUUID().toString(),
role = Role.User,
parts = listOf(TextPart("Hello, agent!")),
contextId = "conversation-1"
)
val request = Request(data = MessageSendParams(message))
val response = client.sendMessage(request)
// Handle response
when (val event = response.data) {
is Message -> {
val text = event.parts
.filterIsInstance<TextPart>()
.joinToString { it.text }
print(text) // Stream partial responses
}
is TaskEvent -> {
if (event.final) {
println("\nTask completed")
}
}
}
4. Send Messages Streaming
The A2A client supports streaming responses for real-time communication.
Instead of receiving a single response, it returns a Flow
of events including messages and task updates.
// Check if agent supports streaming
if (client.cachedAgentCard()?.capabilities?.streaming == true) {
client.sendMessageStreaming(request).collect { response ->
when (val event = response.data) {
is Message -> {
val text = event.parts
.filterIsInstance<TextPart>()
.joinToString { it.text }
print(text) // Stream partial responses
}
is TaskStatusUpdateEvent -> {
if (event.final) {
println("\nTask completed")
}
}
}
}
} else {
// Fallback to non-streaming
val response = client.sendMessage(request)
// Handle single response
}
5. Manage Tasks
A2A Client provides methods to control server tasks by asking for their status and cancelling them.
// Query task status
val taskRequest = Request(data = TaskQueryParams(taskId = "task-123"))
val taskResponse = client.getTask(taskRequest)
val task = taskResponse.data
println("Task state: ${task.status.state}")
// Cancel running task
if (task.status.state == TaskState.Working) {
val cancelRequest = Request(data = TaskIdParams(taskId = "task-123"))
val cancelledTask = client.cancelTask(cancelRequest).data
println("Task cancelled: ${cancelledTask.status.state}")
}