The MutagenT SDK provides three levels of abstraction for manual tracing. Choose the one that fits your use case:
Approach
Best For
Effort
@trace() decorator
Class methods with automatic I/O capture
Lowest
withTrace() wrapper
Functions with manual enrichment via SpanHandle
Medium
startSpan() / endSpan()
Integration adapters, full manual control
Highest
All tracing functions are no-ops when tracing is not active, so you can safely leave instrumentation in place without runtime overhead when tracing is disabled. The @trace() decorator and withTrace() wrapper check for an existing tracing configuration only, while the low-level startSpan() / endSpan() API will additionally attempt lazy initialization from the MUTAGENT_API_KEY environment variable on first use.
A method decorator that wraps class methods with automatic span creation. It captures arguments as input, the return value as output, and sets the span status based on success or failure.Import:
import { trace } from "@mutagent/sdk/tracing";
Options:
interface TraceDecoratorOptions { kind: SpanKind; // Required -- the type of operation name?: string; // Span name (defaults to the method name) attributes?: Record<string, unknown>; // Custom attributes}
Behavior:
Captures method arguments as input.raw
Captures the return value as output.raw
Sets status: "ok" on successful return
Sets status: "error" with the error message on throw (re-throws the error)
Works with both synchronous and asynchronous methods
Preserves this context
Runs the method inside a span context, so nested @trace() calls or getCurrentSpan() calls see the correct parent
The @trace() decorator uses the legacy TypeScript decorator syntax (experimentalDecorators). Make sure your tsconfig.json includes "experimentalDecorators": true if you use this pattern.
Create and start a new span. If no trace is active, a new trace ID is generated. If a parent span exists in the current async context, the new span is automatically linked as a child.If tracing has not been explicitly initialized via initTracing(), startSpan() will attempt lazy initialization from the MUTAGENT_API_KEY environment variable (once per lifecycle).Import:
import { startSpan } from "@mutagent/sdk/tracing";
Signature:
function startSpan(options: SpanOptions): MutagentSpan | undefined;
Options:
interface SpanOptions { kind: SpanKind; // Required -- the type of operation name?: string; // Span name (defaults to kind) input?: SpanIO; // Structured input data attributes?: Record<string, unknown>; // Custom attributes parentSpanId?: string; // Explicit parent (overrides context)}
import { getCurrentTraceId } from "@mutagent/sdk/tracing";const traceId = getCurrentTraceId();// Useful for correlating logs with tracesconsole.log(`Processing request in trace: ${traceId}`);
Returns undefined if no trace is active or tracing is not initialized.
Run a function within the async context of a span. This makes the span visible to getCurrentSpan() and getCurrentTraceId() inside the function, and ensures any child spans created within are linked to this span as their parent.
import { startSpan, endSpan, runInSpanContext } from "@mutagent/sdk/tracing";const span = startSpan({ kind: "agent", name: "my-agent" });if (span) { const result = await runInSpanContext(span, async () => { // getCurrentSpan() returns `span` here // Any startSpan() calls here will have `span` as their parent return await doWork(); }); endSpan(span, { status: "ok", output: { raw: result } });}
If tracing is not initialized, the function runs directly without any context wrapping.
The MutagenT server exposes these REST endpoints for trace ingestion and retrieval. The SDK handles ingestion automatically, but you can also call these endpoints directly.All endpoints require workspace context via the x-workspace-id header and authentication via x-api-key (API key) or Authorization: Bearer (OAuth token).
Since PR #476, GET /api/traces returns a slim list of TraceListItem objects rather than full span trees. This reduces typical list-page payloads by ~98%.Default response shape (TraceListItem):
interface TraceListItem { traceId: string; name: string; source: string; environment: string; status: "running" | "completed" | "error"; traceKind: SpanKind; // Kind of the root span (e.g. "agent", "chain") startTime: string; // ISO 8601 endTime: string | null; // ISO 8601, null if still running durationMs: number | null; model: string | undefined; provider: string | undefined; inputTokens: number | undefined; outputTokens: number | undefined; totalTokens: number | undefined; costUsd: number | undefined; spanCount: number; // Total number of spans in the trace}
The traceKind field reflects the kind of the root span and is the primary way to distinguish agent traces (agent) from pipeline traces (chain) or LLM traces (llm.chat) at list-view without fetching spans.Fetching full spans:To retrieve the complete span tree for all traces in a list response, append ?include=spans:
# Slim list (default) -- fast, small payloadGET /api/traces?limit=20# Full span trees -- use only when spans are neededGET /api/traces?limit=20&include=spans
?include=spans can return large payloads for traces with many spans. Prefer fetching individual traces via GET /api/traces/:id when you need full span details for a specific trace.
TypeScript SDK example:
import { Mutagent } from "@mutagent/sdk";const client = new Mutagent({ apiKey: process.env.MUTAGENT_API_KEY! });// List recent agent traces (slim, no spans)const traces = await client.trace.listTraces({ source: "claude-code", limit: 20,});for (const trace of traces.data) { console.log(`${trace.traceId} kind=${trace.traceKind} spans=${trace.spanCount}`);}// Fetch full span tree for a specific traceconst full = await client.trace.getTrace({ id: traces.data[0].traceId });console.log(full.spans); // Full FinishedSpan[]
The Python SDK provides the same low-level API with snake_case naming. Decorators and wrappers are not yet available in Python — use the low-level API directly.
import asynciofrom mutagent.tracing import ( init_tracing, shutdown_tracing, start_span, end_span, get_current_span, get_current_trace_id, SpanOptions, SpanEndOptions, SpanIO, SpanMetrics, SpanKind, SpanStatus,)init_tracing(api_key="mt_xxxxxxxxxxxx")# Create a spanspan = start_span(SpanOptions( kind=SpanKind.CHAIN, name="rag-pipeline", input=SpanIO(text="What is MutagenT?"),))if span: # Do work... result = generate_answer("What is MutagenT?") # End the span end_span(span, SpanEndOptions( status=SpanStatus.OK, output=SpanIO(text=result), metrics=SpanMetrics( model="gpt-5.4", provider="openai", input_tokens=800, output_tokens=200, total_tokens=1000, ), ))asyncio.run(shutdown_tracing())
Python context propagation uses contextvars, which works across async/await and threads. Parent-child linking is automatic when spans are nested within the same execution context.