providers · A2A (Agent2Agent)
This lesson swaps the LLM behind an agent for another agent, reached over the A2A protocol instead of a model API.
What this lesson demonstrates
Every other provider lesson in the Microsoft Agent Framework Go SDK hands the agent a chat-client for an LLM. This one is different: the “model” behind the agent is another agent running on a remote server, reached over the Agent2Agent (A2A) protocol. The agent holds an a2aclient.Client pointed at that server, and from the call site — RunText(...).Collect(), middleware, sessions — it behaves exactly like any other agent.Agent. Only what sits behind it changes.
There are three moves: (1) resolve the remote agent’s card from a URL, (2) open an A2A client to one of the transports the card advertises (here gRPC), and (3) wrap that client with a2aprovider.NewAgent so it looks like a normal agent.
One real excerpt
The network work is kept separate from the wiring. dialCard resolves the card and opens the client:
// Resolve the agent card advertised at the given URL.
card, err := agentcard.DefaultResolver.Resolve(ctx, url)
if err != nil {
return nil, fmt.Errorf("failed to resolve agent card at %s: %w", url, err)
}
// Insecure gRPC is used for example purposes; a real deployment would use TLS.
withInsecureGRPC := a2agrpc.WithGRPCTransport(grpc.WithTransportCredentials(insecure.NewCredentials()))
client, err := a2aclient.NewFromCard(ctx, card, withInsecureGRPC)
Then a2aprovider.NewAgent(client, a2aprovider.AgentConfig{...}) returns a normal *agent.Agent named "Joker", with the logging middleware attached.
What to notice — the gotcha
There is no LLM credential here; the “credential” is an address. No API key, no Azure TokenCredential. The remote agent’s location comes from A2A_AGENT_HOST (default http://127.0.0.1:9001) — the remote agent owns its own model and secrets. Authentication, if any, is negotiated per the agent card.
The card comes first, then the client: agentcard.DefaultResolver.Resolve fetches the *a2a.AgentCard, which lists the transports the remote agent supports; a2aclient.NewFromCard picks a compatible one. Here we force gRPC with an insecure transport (example only — use TLS in production). The gRPC transport dials lazily, which is why the offline test can build a real client from an in-memory card without ever opening a socket.
How it maps to the Agent Framework
This is the framework’s answer to distributed, multi-agent systems: a provider is just a way to produce responses, and the a2aprovider makes a remote agent look like a local one. a.ProviderName() returns "a2a". In Azure AI Foundry terms, A2A lets a Foundry-hosted agent delegate to — or be delegated to by — agents built on entirely different stacks, as long as they speak the protocol. The uniform agent.Agent surface means middleware and sessions you wrote for a Foundry agent work unchanged against an A2A-backed one.
Run it
A2A_AGENT_HOST=http://127.0.0.1:9001 go run ./tutorial/02-agents/providers/a2a
Without a running A2A server the program exits with a card-resolution error — that is the expected offline behavior. The structural tests run offline; the live remote call is gated behind AF_LIVE=1.
Next: providers · Anthropic (Claude)