> ## Documentation Index
> Fetch the complete documentation index at: https://docs.braintrust.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# A2A (Agent2Agent)

> Trace A2A protocol client and server calls in Braintrust to debug and evaluate multi-agent interactions

The [A2A (Agent2Agent) protocol](https://github.com/a2aproject/a2a-go) is an open standard for agent-to-agent communication. Braintrust traces A2A client calls and server request handling, capturing inputs, outputs, and timing across distributed agent interactions.

**Go**

## Setup

Install the Braintrust Go SDK alongside the A2A Go SDK, then configure your API key.

**1. Install packages**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
go get github.com/braintrustdata/braintrust-sdk-go
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/a2a
go get github.com/a2aproject/a2a-go
```

**2. Set environment variables**

```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
BRAINTRUST_API_KEY=your-braintrust-api-key
```

## Auto-instrumentation

To trace A2A calls without modifying your application code, build your app with [Orchestrion](https://github.com/DataDog/orchestrion). Orchestrion appends `tracea2a.NewClientInterceptor()` to `a2aclient.NewFromCard()`, `a2aclient.NewFromEndpoints()`, and `a2aclient.NewFactory()` calls, and appends `tracea2a.InstrumentServer()` to `a2asrv.NewHandler()` calls at compile time.

**1. Install Orchestrion**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
go install github.com/DataDog/orchestrion@latest
```

**2. Create orchestrion.tool.go in your project root**

```go title="orchestrion.tool.go" #skip-compile theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
//go:build tools

package main

import (
_ "github.com/DataDog/orchestrion"
_ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/a2a"
)
```

**3. Initialize Braintrust and use the A2A SDK as normal**

```go Go #skip-compile theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
package main

import (
"context"
"log"

"github.com/a2aproject/a2a-go/a2a"
"github.com/a2aproject/a2a-go/a2aclient"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/sdk/trace"

"github.com/braintrustdata/braintrust-sdk-go"
)

func main() {
tp := trace.NewTracerProvider()
defer tp.Shutdown(context.Background())
otel.SetTracerProvider(tp)

_, err := braintrust.New(tp, braintrust.WithProject("my-project"))
if err != nil {
	log.Fatal(err)
}

// Orchestrion instruments NewFromEndpoints at compile time.
client, err := a2aclient.NewFromEndpoints(context.Background(), []a2a.AgentInterface{
	{Transport: a2a.TransportProtocolJSONRPC, URL: "http://localhost:8080/invoke"},
})
if err != nil {
	log.Fatal(err)
}
defer client.Destroy() //nolint:errcheck

result, err := client.SendMessage(context.Background(), &a2a.MessageSendParams{
	Message: a2a.NewMessage(a2a.MessageRoleUser, a2a.TextPart{Text: "Hello"}),
})
if err != nil {
	log.Fatal(err)
}
_ = result
}
```

**4. Build and run with Orchestrion**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
orchestrion go run .
```

## Manual instrumentation

Call `tracea2a.InstrumentClient()` after creating a client, and pass `tracea2a.InstrumentServer()` as an option to `a2asrv.NewHandler()`.

```go Go #skip-compile theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
package main

import (
	"context"
	"log"
	"net/http"
	"net/http/httptest"

	"github.com/a2aproject/a2a-go/a2a"
	"github.com/a2aproject/a2a-go/a2aclient"
	"github.com/a2aproject/a2a-go/a2asrv"
	"github.com/a2aproject/a2a-go/a2asrv/eventqueue"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/sdk/trace"

	"github.com/braintrustdata/braintrust-sdk-go"
	tracea2a "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/a2a"
)

type greetingAgent struct{}

func (greetingAgent) Execute(ctx context.Context, _ *a2asrv.RequestContext, q eventqueue.Queue) error {
	return q.Write(ctx, a2a.NewMessage(a2a.MessageRoleAgent, a2a.TextPart{Text: "Hello from the A2A agent!"}))
}

func (greetingAgent) Cancel(context.Context, *a2asrv.RequestContext, eventqueue.Queue) error {
	return nil
}

func main() {
	tp := trace.NewTracerProvider()
	defer tp.Shutdown(context.Background())
	otel.SetTracerProvider(tp)

	_, err := braintrust.New(tp, braintrust.WithProject("my-project"))
	if err != nil {
		log.Fatal(err)
	}

	// Server side: pass InstrumentServer() to NewHandler.
	handler := a2asrv.NewHandler(greetingAgent{}, tracea2a.InstrumentServer())
	mux := http.NewServeMux()
	mux.Handle("/invoke", a2asrv.NewJSONRPCHandler(handler))
	server := httptest.NewServer(mux)
	defer server.Close()

	// Client side: call InstrumentClient after creating the client.
	client, err := a2aclient.NewFromEndpoints(context.Background(), []a2a.AgentInterface{
		{Transport: a2a.TransportProtocolJSONRPC, URL: server.URL + "/invoke"},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer client.Destroy() //nolint:errcheck
	tracea2a.InstrumentClient(client)

	result, err := client.SendMessage(context.Background(), &a2a.MessageSendParams{
		Message: a2a.NewMessage(a2a.MessageRoleUser, a2a.TextPart{Text: "Hello"}),
	})
	if err != nil {
		log.Fatal(err)
	}
	_ = result
}
```

## What Braintrust traces

Braintrust captures:

* `SendMessage` and `SendStreamingMessage` client calls as `task` spans with span kind `client`, named `a2a.SendMessage` and `a2a.SendStreamingMessage`
* `OnSendMessage` and `OnSendMessageStream` server request handling as `task` spans with span kind `server`
* Input: the `Message` field of `MessageSendParams`
* Output: the response message for non-streaming calls, and for streaming calls the accumulated content across all events (artifact parts and final status merged into the `Task` shape)
* Metadata: A2A method name, protocol (`a2a`), and role (`client` or `server`)
* `time_to_first_token` in span metrics, measured when the first content-bearing event arrives
* Error status and exception event when a call fails
* W3C TraceContext propagation across the A2A client-server boundary, so client and server spans share the same trace

Detached task lifecycle methods (`GetTask`, `CancelTask`), push notification methods, and agent card requests are not instrumented. Non-blocking `SendMessage` calls (where `Config.Blocking` is `false`) are also excluded.

## Resources

* [A2A example (Go SDK)](https://github.com/braintrustdata/braintrust-sdk-go/blob/main/examples/a2a/main.go)
* [A2A Go SDK](https://github.com/a2aproject/a2a-go)
* [Trace application logic](https://docs.braintrust.dev/instrument/trace-application-logic.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.