Skip to main content
Available for: Python and TypeScript.
Zespan provides first-class tracing for Google’s Agent Development Kit (ADK). Every model call, tool invocation, and agent-to-agent delegation in your ADK workflow is captured as a linked span — without modifying your agent definitions.

TypeScript

Installation

Four integration patterns

Choose the integration pattern that fits your setup:
instrumentADK wraps both your coordinator agent and its runner in a single call. Use this when building production ADK applications.

wrapADKRunner — runner-level wrapping

Wraps InMemoryRunner.runEphemeral() and Runner.run() by intercepting the ADK event stream. Emits one span per agent author turn, one span per tool call, and handoff spans on delegation.
wrapADKRunner options:
string
required
Display name for the root agent span in Zespan.
string
default:"coordinator"
Role hint for the agent registry. Use "coordinator" for orchestrators and "specialist" for sub-agents.
string
The model name used by the root agent. Shown in the trace detail panel.
string
Associates all spans from this run with a session in the Sessions view.
Record<string, string> | (agentName: string) => Record<string, string>
Extra key-value metadata merged into each span’s metadata, for example the routing surface or tier an agent runs on. Pass a function to give each agent its own values. wrapADKAgent accepts it too and passes it to sub-agents.
wrapADKAgent reads the model from the agent’s model — a string, or an ADK BaseLlm instance (its model id is used). Per-call spans record the model that actually answered when the response reports a modelVersion, for example after a failover to another model.

ZespanADKCallbackHandler — native ADK callbacks

Uses ADK’s built-in callback system (beforeAgentCallback, afterModelCallback, etc.). Create one handler instance and spread .callbacks into your LlmAgent config. Captures agent spans, LLM spans with full token counts, and tool call spans.
ADK for TypeScript reports a tool that throws only to plugins, never to the agent callbacks. Without plugins: [tracer.plugin], a tool that throws is recorded as a successful tool span, and the SDK prints one warning the first time a tool returns nothing. Register the plugin next to the callbacks: it adds no second span, never throws into your run and leaves your tool’s error unchanged. tracer.plugin is never null. Use one handler per runner (ADK refuses two plugins with the same name). ADK does not report a thrown value that is not an Error (for example a string) to plugins, so such a tool is still recorded as successful: throw Error objects.
Python needs no plugin: ADK for Python calls the agent’s error callback, which handler.callbacks includes. For multi-agent systems, use the same handler instance across all agents so spans share the same trace:
ZespanADKCallbackHandler is also exported as ADKCallbackHandler for shorter imports:

wrapADKAgent — agent-level wrapping

Wraps agent.run() directly. Best for unit tests and simple scripts. Recursively wraps subAgents by default.

What gets captured

All three wrappers capture the same data from the ADK event stream:

Capture v2 (off by default)

Turn on capture_v2=True (Python) or captureV2: true (TypeScript), or set ZESPAN_CAPTURE_V2=1, and ZespanADKCallbackHandler also records what the trace checks read:
  • Tool results as JSON (redacted before they are cut), instead of a text copy of an object. An empty result (None, [], {}) is kept and marked empty, and a result cut at 10,240 characters is marked as cut.
  • Tool definitions the model was offered, with parameter schemas, and the model’s requested tool calls with their ids, so a request can be matched to the tool span that ran it, and a request for a tool that does not exist (which ADK refuses before any tool runs) can be seen.
  • The conversation as structured messages (text, function calls and function responses with roles), instead of one block of role: text lines, plus the system instructions and the output schema.
  • Agent text: each agent span carries the text its agent received and the last text it produced, so an answer written by a sub-agent after a transfer is found. Each agent span’s parent_agent_id is its direct parent.
  • A small marker per span saying which of these were captured, absent, cut or unavailable.
All of it follows prompt and tool storage and goes through the same redaction as stored prompts. With v2 on and the GenAI client also instrumented, one model call is still one span. ZESPAN_CAPTURE_V2=0 turns it off. Upgrade note: older versions of the handler stored tool results as text copies of Python objects; with v2 on, new traces store JSON. See Capture fields.

Agent steps and handoffs

ZespanADKCallbackHandler records on each agent span the number of model calls the agent made (zespan.agent.step), how many sub-agents it could hand to (zespan.agent.route_candidates) and, when the model asked to transfer to another agent, zespan.agent.stop_reason: handoff with the target in zespan.agent.route. wrapADKRunner and, in Python, wrap_adk_agent record a handoff span when work passes from one agent to another (in Python, when a wrapped agent runs inside another wrapped agent; the span carries both agents’ names). See Agent steps.

Tool calls join the tool’s trace

With the callback handler, a model, MCP or HTTP call made inside a tool belongs to the tool’s span and to the run’s trace: the handler makes the tool’s span the current context in beforeToolCallback / before_tool_callback and restores it in the after callback. In Python, a tool that raises is handled by on_tool_error_callback, which is included in handler.callbacks when your ADK version has that callback. In TypeScript the plugin (plugins: [handler.plugin]) does this for a tool that throws, because ADK for TypeScript calls its error hook only on plugins. Tools running in parallel each see their own span.

Prompt storage off

With storePrompts: false (TypeScript) or store_prompts=False (Python), the Google ADK integration sends no content from your agents — the same in both SDKs:
  • No prompt or completion text
  • No tool-call arguments, tool results, or the model’s decision rationale
  • On an agent error event, only the error class name (for example RateLimitError) instead of the provider’s error text, which can quote the prompt. The error is still classified.
Tool names are still sent, so tool usage still shows in the dashboard. With prompt storage on (the default), content is sent as before, and ADK error messages go through the same redaction as stored prompts. See PII redaction.

Python

Installation

Three integration patterns


ZespanADKCallbackHandler — native ADK callbacks

Uses ADK’s built-in callback system. Create one handler and spread .callbacks into each LlmAgent constructor. Captures agent spans, LLM spans with full token counts, and tool spans. ADK Python is async — run agents with InMemoryRunner inside asyncio.run(). Single agent:
Multi-agent system: Use the same handler instance across all agents — ADK shares the same invocation_id across coordinator and sub-agents, so all spans are automatically linked under one trace.
Tool functions are plain Python functions with type hints and docstrings — ADK auto-generates the function declarations. Return dict or any JSON-serializable value.

ZespanADKTracer — attach to an existing agent

Pass your ADK agent to ZespanADKTracer after creating it. The tracer intercepts agent.run to capture every model call, tool invocation, and agent session as hierarchical spans.

wrap_adk_agent — callback-handler style

wrap_adk_agent is the functional equivalent: it attaches tracing and returns the same agent. Use this when you want a one-liner or are composing agents inline.
wrap_adk_agent is deprecated and warns once. Current ADK agents do not have the method it patches, and it never sent the agent’s instructions. Use ZespanADKCallbackHandler.
Both ZespanADKTracer and wrap_adk_agent accept an optional guardrails argument:

Multi-agent systems

Attach ZespanADKTracer (or call wrap_adk_agent) after the agent is fully configured. Attaching before sub_agents are set means sub-agent spans won’t be linked correctly.

How multi-agent traces look in the dashboard

A coordinator + specialist ADK run produces a trace like this:
Each agent’s total token cost is shown separately in the agent registry view, which also maps the coordinator-specialist hierarchy visually.
Multi-agent trace visualization in Zespan showing coordinator and specialist agent spans

Session and user context

To associate ADK traces with a user session for the Sessions dashboard view, set sessionId and userId in the wrapper options (TypeScript) or via with_zespan_context (Python):

Next steps

  • Capture fields — what each check reads and which switch controls it.
  • MCP — tool spans from an ADK MCP toolset carry the tool and server name, and the calls the toolset makes through the MCP client are traced.
  • Agent tracing — add agent identity, tool roles and approvals.