Skip to main content
Agent2Agent (A2A) is Google’s protocol for enabling communication between conversational AI agents. LangSmith implements A2A support, allowing your agents to communicate with other A2A-compatible agents through a standardized protocol. The A2A endpoint is available in Agent Server at /a2a/{assistant_id}.

Protocol version

Agent Server speaks the A2A v1.0 JSON-RPC binding and also accepts the v0.3 method names, so existing v0.3 clients keep working. The agent card declares one interface:
The method name you send also selects the enum case in the response. A v1.0 name returns SCREAMING_SNAKE_CASE (TASK_STATE_WORKING, ROLE_AGENT); a v0.3 name returns lowercase (working, agent). Pick one family per client and stay on it.The envelope varies by method, not by family: SendMessage wraps the task in result.task, while GetTask and all v0.3 methods return it directly on result. ListTasks returns result.tasks.

Supported methods

Exactly four v0.3 names are accepted: message/send, message/stream, tasks/get and tasks/cancel. Anything else — including agent/getAuthenticatedExtendedCard and tasks/resubscribe — returns -32601 Method not found. Only the JSON-RPC binding is available. gRPC and HTTP+JSON are not implemented.

Task history in responses

A context holds many tasks. By default SendMessage, GetTask and ListTasks return the history of the whole context, not just the task you asked about. The second task in a context replays the first task’s messages, so a client that renders every history entry shows earlier turns again — including earlier tool results and A2UI payloads. Set historyScope to task to get back only the messages that belong to the task you asked about. The default stays context, so existing integrations are unaffected. Where the option goes depends on the method. SendMessage reads it from configuration:
GetTask and ListTasks read it directly from params:
If your client cannot add fields to the request body, send the header instead. An explicit value in the request wins over the header.
The agent card advertises this under capabilities.extensions, so you can detect support rather than assume it:
Three limits worth knowing:
  • Streaming ignores both history options. SendStreamingMessage reads neither historyScope nor historyLength, and returns no error if you send them — so do not rely on either over SSE.
  • historyLength caps at 10. A larger value returns -32602 with historyLength cannot exceed 10.
  • Scope is applied before historyLength, so you get the last N messages of that task.
An unrecognized value returns -32602 with historyScope must be 'context' or 'task'. A mis-cased key such as historyscope is not an error — it is ignored, and you silently get full-context history, so check the spelling if filtering appears not to work.
Do not resend a taskId from a completed task. Each new turn starts a new task inside the same context — send the contextId alone. A message naming a terminal task is rejected with -32004, and a taskId minted by another agent is rejected with -32001.

Agent card discovery

Each assistant automatically exposes an A2A Agent Card that describes its capabilities and provides the information needed for other agents to connect. You can retrieve the agent card for any assistant using:
The agent card includes the assistant’s name, description, available skills, supported input/output modes, and the A2A endpoint URL for communication.

Optional capabilities

These are configured per assistant through metadata.a2a on the Assistants API. langgraph.json cannot set assistant metadata, so patch the assistant after deploy.

Declare input and output modes

The values feed both the card’s defaultInputModes/defaultOutputModes and the generated skill’s modes. They are advertisement only — an undeclared mode is still accepted. Replacement is per field, so send both if you want both overridden, and note that an empty list is rejected.

File parts

FilePart works in both directions. Inbound file, image, audio and video parts become LangChain content blocks. Outbound content blocks map back to FilePart, in task history and in the final streamed artifact. MIME types, URIs and filenames pass through unchanged; inline data is re-encoded as standard base64.

A2UI v0.9

Opt in per assistant:
The card then advertises the extension and appends the canonical MIME type to both mode lists:
A client activates it by listing the URI in message.extensions. Payloads are validated in both directions against the v0.9 schemas, and A2UI parts are preserved in message/send, tasks/get and the final message/stream artifact. Responses carry metadata.mimeType: "application/a2ui+json". application/json+a2ui is accepted as an alias on input.

Filter tool results

By default every correlated tool result is published as a DataPart. To publish only some, set an allowlist of tool names on the deployment:
Unset means all tool results are published. The filter applies to both task history and streaming.

Requirements

A2UI v0.9 and historyScope landed after the 0.14.0 release candidates were cut, so they arrive in 0.15.0. That version is not published as a stable release yet — check capabilities.extensions on the agent card before relying on historyScope. Your graph’s state must include a messages key to accept A2A text and file parts. An assistant whose input schema has no messages field is rejected with an explanatory error.

Creating an A2A-compatible agent

This example creates an A2A-compatible agent that processes incoming messages using OpenAI’s API and maintains conversational state. The agent defines a message-based state structure and handles the A2A protocol’s message format. To be compatible with the A2A “text” parts, the agent must have a messages key in state. The A2A protocol uses two identifiers to maintain conversational continuity:
  • contextId: Groups messages into a conversation thread (like a session ID)
  • taskId: Identifies each individual request within that conversation
On the first message, omit both - the agent generates and returns them. For all subsequent messages in the conversation, send back the contextId from the prior response and omit taskId, so each turn opens a new task inside the same conversation. Send a taskId only to add to a task that is still running, such as one waiting on input. LangSmith Tracing: The Langsmith Deployment A2A endpoint automatically converts the A2A contextId to thread_id for LangSmith tracing, grouping all messages in the conversation under a single thread. For example:

Agent-to-agent communication

Once your agents are running locally via langgraph dev or deployed to production, you can facilitate communication between them using the A2A protocol. This example demonstrates how two agents can communicate by sending JSON-RPC messages to each other’s A2A endpoints. The script simulates a multi-turn conversation where each agent processes the other’s response and continues the dialogue.
For complete working examples, see:

Distributed tracing

When multiple agents communicate over A2A, LangSmith can group all their traces into a single thread, which gives you a unified view of the entire multi-agent conversation.

How contextId maps to thread_id

The Agent Server A2A endpoint automatically converts the A2A contextId to thread_id for LangSmith tracing. This means every message in a conversation, across all participating agents, is grouped under the same thread in LangSmith without any extra configuration on your part. The flow works as follows:
  1. On the first message, the client omits contextId. The server generates one and returns it in the response.
  2. The client passes the contextId in all subsequent messages to maintain conversation continuity.
  3. Agent Server maps the contextId to thread_id in LangSmith metadata, so all turns appear in the same thread.
The contextId is used directly as the LangGraph thread_id, so it must be a UUID. Echo back the one the server returned rather than minting your own identifier. A contextId such as session-42 is rejected with -32602 and the message Failed to create run: Invalid thread ID.

Tracing across multiple agents

When agents from different frameworks communicate over A2A, contextId is what unifies their traces. Reuse the contextId returned by the first agent on every later request, to that agent and to the others.
Agent Server does not read a top-level metadata field on the JSON-RPC payload. There is no way for a client to set the LangGraph thread_id directly — it is always the contextId. Sending metadata.thread_id to an Agent Server deployment has no effect.
The following code snippet demonstrates the key concepts. For a complete runnable implementation with two agents, refer to the Google ADK + LangChain example.
1. Build the message: Include contextId inside the message object on follow-up turns so the server can associate them with the ongoing conversation. Omit it on the first message, because the server generates a contextId and returns it in the response. Do not resend taskId from a finished turn. 2. Send it: The contextId travels inside params.message. Agent Server uses it as the LangGraph thread_id, so there is no separate tracing field to set. 3. Share the context across agents: Let the first agent mint the contextId, then pass that same value to every agent for the rest of the conversation. That is what groups their traces into one thread.

Receive thread_id in non-LangGraph agents

The previous section covers the client side — propagating contextId when sending messages. If one of your agents is not built on LangGraph, it also needs to read that contextId on the receiving end and attach it as the thread identifier, so its traces land in the same LangSmith thread. Use langsmith.integrations.otel.configure() to set up automatic tracing, and read params.message.contextId from the incoming A2A request.
Register your agent routes on app after this middleware.
Set LANGSMITH_API_KEY and optionally LANGSMITH_PROJECT in your environment to enable tracing. All agents in the conversation should use the same project so their traces are visible together.

View traces in LangSmith

After running a multi-agent conversation, open the LangSmith UI and navigate to Threads. All turns from all participating agents will appear under a single thread, identified by the shared thread_id.

Test your integration

Against your own deployment

Fetch the card, then send a message:
The response contains result.task.id and result.task.contextId. Reuse the contextId on the next message to continue the conversation. For streaming, send Accept: text/event-stream and use SendStreamingMessage. The first event is the Task; status and artifact updates follow.

Against the official conformance suite

A2A publishes a Technology Compatibility Kit at a2aproject/a2a-tck. It grades an implementation by RFC 2119 level and works against any A2A endpoint, including yours.
The TCK drives some scenarios through messageId prefixes such as tck-input-required, described in its docs/SUT_REQUIREMENTS.md. A graph that does not implement those prefixes will report those requirements as skipped rather than failed.

What Agent Server currently fails

Agent Server runs the TCK on every CI build as a required check, gated against a checked-in list of known failures. CI fails if a new failure appears, and also if a listed requirement starts passing, so the list cannot drift from what the server actually does. Read this before you build against a capability:

Disable A2A

To disable the A2A endpoint, set disable_a2a to true in your langgraph.json configuration file: