6. Interacting with the Server¶
With the Helloworld A2A server running, let's send some requests to it. The SDK includes a client (A2AClient) that simplifies these interactions.
The Helloworld Test Client¶
The examples/helloworld/test_client.py script demonstrates how to:
- Fetch the Agent Card from the server.
- Create an
A2AClientinstance. - Send both non-streaming (
message/send) and streaming (message/stream) requests.
Open a new terminal window, activate your virtual environment, and navigate to the a2a-python directory.
Activate virtual environment (Be sure to do this in the same directory where you created the virtual environment):
Run the test client:
Understanding the Client Code¶
Let's look at key parts of examples/helloworld/test_client.py:
-
Fetching the Agent Card & Initializing the Client:
# examples/helloworld/test_client.py async with httpx.AsyncClient() as httpx_client: client = await A2AClient.get_client_from_agent_card_url( httpx_client, 'http://localhost:9999' )The
A2AClient.get_client_from_agent_card_urlclass method is a convenience. It first fetches theAgentCardfrom the server's/.well-known/agent.jsonendpoint (based on the provided base URL) and then initializes the client with it. -
Sending a Non-Streaming Message (
send_message):# examples/helloworld/test_client.py from a2a.types import ( MessageSendParams, SendMessageRequest, SendStreamingMessageRequest, ) # ... send_message_payload: dict[str, Any] = { 'message': { 'role': 'user', 'parts': [{'type': 'text', 'text': 'how much is 10 USD in INR?'}], # Content doesn't matter for Helloworld 'messageId': uuid4().hex, }, } request = SendMessageRequest( params=MessageSendParams(**send_message_payload) ) response = await client.send_message(request) print(response.model_dump(mode='json', exclude_none=True))- The
send_message_payloadconstructs the data forMessageSendParams. - This is wrapped in a
SendMessageRequest. - It includes a
messageobject with theroleset to "user" and the content inparts. - The Helloworld agent's
executemethod will enqueue a single "Hello World" message. TheDefaultRequestHandlerwill retrieve this and send it as the response. - The
responsewill be aSendMessageResponseobject, which contains either aSendMessageSuccessResponse(with the agent'sMessageas the result) or aJSONRPCErrorResponse.
- The
-
Handling Task IDs (Illustrative Note for Helloworld): The Helloworld client (
examples/helloworld/test_client.py) doesn't attemptget_taskorcancel_taskdirectly because the simple Helloworld agent'sexecutemethod, when called viamessage/send, results in theDefaultRequestHandlerreturning a directMessageresponse rather than aTaskobject. More complex agents that explicitly manage tasks (like the LangGraph example) would return aTaskobject frommessage/send, and itsidcould then be used forget_taskorcancel_task. -
Sending a Streaming Message (
send_message_streaming):# examples/helloworld/test_client.py streaming_request = SendStreamingMessageRequest( params=MessageSendParams(**send_message_payload) # Same payload can be used ) stream_response = client.send_message_streaming(streaming_request) async for chunk in stream_response: print(chunk.model_dump(mode='json', exclude_none=True))- This method calls the agent's
message/streamendpoint. TheDefaultRequestHandlerwill invoke theHelloWorldAgentExecutor.executemethod. - The
executemethod enqueues one "Hello World" message, and then the event queue is closed. - The client will receive this single message as one
SendStreamingMessageResponseevent, and then the stream will terminate. - The
stream_responseis anAsyncGenerator.
- This method calls the agent's
Expected Output¶
When you run test_client.py, you'll see JSON outputs for:
- The non-streaming response (a single "Hello World" message).
- The streaming response (a single "Hello World" message as one chunk, after which the stream ends).
The id fields in the output will vary with each run.
// Non-streaming response
{"jsonrpc":"2.0","id":"xxxxxxxx","result":{"type":"message","role":"agent","parts":[{"type":"text","text":"Hello World"}],"messageId":"yyyyyyyy"}}
// Streaming response (one chunk)
{"jsonrpc":"2.0","id":"zzzzzzzz","result":{"type":"message","role":"agent","parts":[{"type":"text","text":"Hello World"}],"messageId":"wwwwwwww","final":true}}
(Actual IDs like xxxxxxxx, yyyyyyyy, zzzzzzzz, wwwwwwww will be different UUIDs/request IDs)
This confirms your server is correctly handling basic A2A interactions with the updated SDK structure!
Now you can shut down the server by typing Ctrl+C in the terminal window where __main__.py is running.