> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/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.

<View title="Go" icon="/images/sdk-icons/go.svg">
  <h2 id="setup-go">
    Setup
  </h2>

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

  <Steps>
    <Step title="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
      ```
    </Step>

    <Step title="Set environment variables">
      ```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      BRAINTRUST_API_KEY=your-braintrust-api-key
      ```
    </Step>
  </Steps>

  <h2 id="auto-instrumentation-go">
    Auto-instrumentation
  </h2>

  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.

  <Steps>
    <Step title="Install Orchestrion">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      go install github.com/DataDog/orchestrion@latest
      ```
    </Step>

    <Step title="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"
      )
      ```
    </Step>

    <Step title="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
      }
      ```
    </Step>

    <Step title="Build and run with Orchestrion">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      orchestrion go run .
      ```
    </Step>
  </Steps>

  <h2 id="manual-instrumentation-go">
    Manual instrumentation
  </h2>

  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
  }
  ```

  <h2 id="what-traced-go">
    What Braintrust traces
  </h2>

  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.

  <h2 id="resources-go">
    Resources
  </h2>

  * [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](/docs/instrument/trace-application-logic)
</View>
