Skip to content

Tutorial: A2A Client

Discover an A2A agent and send messages using the runtime client.

Time: 10 minutes Level: Intermediate

A client that discovers a remote A2A agent and sends messages — both synchronous and streaming.

  • Create an A2A client
  • Discover an agent’s capabilities via its agent card
  • Send synchronous messages
  • Receive streaming events

package main
import (
"context"
"fmt"
"log"
"github.com/AltairaLabs/PromptKit/runtime/a2a"
)
func main() {
ctx := context.Background()
client := a2a.NewClient("http://localhost:9999")

You can configure the client with options:

client := a2a.NewClient("http://localhost:9999",
a2a.WithAuth("Bearer", "my-token"),
a2a.WithHTTPClient(customHTTPClient),
)

Fetch the agent card to learn what the agent can do:

card, err := client.Discover(ctx)
if err != nil {
log.Fatalf("Discover: %v", err)
}
fmt.Printf("Agent: %s\n", card.Name)
fmt.Printf("Description: %s\n", card.Description)
for _, skill := range card.Skills {
fmt.Printf(" Skill: %s%s\n", skill.ID, skill.Description)
}

The card is cached after the first call, so subsequent Discover calls are free.


Send a synchronous message with Blocking: true:

text := "Hello from the client!"
task, err := client.SendMessage(ctx, &a2a.SendMessageRequest{
Message: a2a.Message{
Role: a2a.RoleUser,
Parts: []a2a.Part{{Text: &text}},
},
Configuration: &a2a.SendMessageConfiguration{Blocking: true},
})
if err != nil {
log.Fatalf("SendMessage: %v", err)
}
fmt.Printf("Task state: %s\n", task.Status.State)
for _, artifact := range task.Artifacts {
for _, part := range artifact.Parts {
if part.Text != nil {
fmt.Printf("Agent: %s\n", *part.Text)
}
}
}

Expected output:

Agent: Echo Agent
Description: Echoes back whatever you send
Skill: echo — Echoes the input message back
Task state: completed
Agent: Echo: Hello from the client!

Use SendMessageStream to receive incremental updates:

streamText := "Tell me a story"
events, err := client.SendMessageStream(ctx, &a2a.SendMessageRequest{
Message: a2a.Message{
Role: a2a.RoleUser,
Parts: []a2a.Part{{Text: &streamText}},
},
})
if err != nil {
log.Fatalf("Stream: %v", err)
}
for event := range events {
if event.StatusUpdate != nil {
fmt.Printf("[status] %s\n", event.StatusUpdate.Status.State)
}
if event.ArtifactUpdate != nil {
for _, part := range event.ArtifactUpdate.Artifact.Parts {
if part.Text != nil {
fmt.Print(*part.Text)
}
}
}
}
fmt.Println()
}

Streaming requires the server to support it. If the server doesn’t support streaming, you’ll see a single artifact event followed by completion.


Retrieve or cancel tasks by ID:

// Get a task by ID.
task, err := client.GetTask(ctx, "task-123")
// Cancel a running task.
err = client.CancelTask(ctx, "task-123")
// List tasks by context.
tasks, err := client.ListTasks(ctx, &a2a.ListTasksRequest{
ContextID: "ctx-456",
})

  • How to create an A2A client and configure authentication
  • How to discover agents via their agent card
  • How to send synchronous and streaming messages
  • How to manage tasks (get, cancel, list)