Chapter 4

Trace One Request From User to Artifact

We've taken apart the individual messages - now let's assemble one end-to-end path from them and see what's easy to confuse in it. The trace of a request from the user to a finished artifact runs through three different levels: the product goal, the inter-agent task, and the capability calls. Their states live separately, and the beginner's main mistake is to merge the conversation history, the A2A Task id, and the state of the MCP requests into one heap.

Let's lay the same release-review scenario out step by step - who owns state at each one and what fact can be observed from outside.

Step · Contract · Owner of state · Observable fact

  • 1. The coordinator delegates the check - A2A Message; A2A client; messageId, accepted modes
  • 2. The Review Agent accepts the work - A2A Task; A2A server/task store; submitted, then working
  • 3. The agent looks for capabilities - MCP discovery; MCP host/client; Lists of tools and resources
  • 4. The agent reads the evidence - MCP resource/tool; MCP server; The runbook and the release snapshot
  • 5. The agent publishes the result - A2A Artifact; A2A task store; Markdown and JSON
  • 6. The coordinator makes a decision - Product code; Your application; Authorization and the action outside the protocol

Read the table by the "owner of state" column. The coordinator owns the product decision, the A2A server owns the Task lifecycle, the MCP host owns the capability-access session, and the MCP server owns the data itself. None of them should look into another's state: the coordinator knows nothing about the MCP requests inside the agent, and the MCP server knows nothing about the A2A Task it was called within. It's exactly this separation that makes the system replaceable part by part.

To trace how one action turns into a concrete message of one protocol or the other, use the inspector - pick a protocol and an action and look at the resulting wire contract.

Interactive lab 2

Message inspector

{
  "protocol": "MCP",
  "operation": "tools/list",
  "returns": "Tool[] + ttlMs + cacheScope",
  "correlation": "JSON-RPC request id"
}

Hence an important rule about identifiers that even experienced teams stumble over.

One trace id, different protocol ids. Propagate a shared OpenTelemetry trace context across boundaries for observability, but don't let it replace the JSON-RPC id, the MCP request handle, the A2A taskId, or the contextId. Each identifier has its own semantics and lifetime; mix them and correlation in production starts to lie.

To sort out which identifier is for what, use the untangler.

Interactive lab 8

Untangle the identifiers

trace id (OpenTelemetry).
Don't mix with: authorization proof.

Now that the mental model is assembled, it's time to move from diagrams to working code - and to do it on a project you can run and check right now.

Links