Skip to content
Monitoring
Skill

/maple-agent-tracing-claude-agent-sdk

Trace Claude Agent SDK agents (TypeScript and Python) and Claude Code CLI sessions with Maple: configure Claude Code's built-in OpenTelemetry so each conversation becomes one Maple Agent Session with prompts, model calls, tool calls and tokens. Triggers on 'trace my claude agent

BOOST
From plugin
maple
1.8k37 skills
Install
$ npx -y skills add mapletechlabs/maple --skill maple-agent-tracing-claude-agent-sdk --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/maple-agent-tracing-claude-agent-sdk

Context preview

The summary Claude sees to decide when to auto-load this skill.

Trace Claude Agent SDK agents (TypeScript and Python) and Claude Code CLI sessions with Maple: configure Claude Code's built-in OpenTelemetry so each conversation becomes one Maple Agent Session with prompts, model calls, tool calls and tokens. Triggers on 'trace my claude agent

SKILL.md

maple-agent-tracing-claude-agent-sdk.SKILL.md
name: maple-agent-tracing-claude-agent-sdk
description: "Trace Claude Agent SDK agents (TypeScript and Python) and Claude Code CLI sessions with Maple: configure Claude Code's built-in OpenTelemetry so each conversation becomes one Maple Agent Session with prompts, model calls, tool calls and tokens. Triggers on 'trace my claude agent sdk agent', 'add Maple to claude agent sdk', 'agent sessions for claude code', 'OpenTelemetry for claude agent sdk', 'send my claude code sessions to Maple'."

Maple agent tracing: Claude Agent SDK and Claude Code

Goal

One conversation = one Maple Agent Session, one turn per user message, with the user prompts, every model call (model, tokens, TTFT), every tool call (name, args, result, failures).

How it works: the Agent SDK emits nothing itself. `query()` spawns the Claude Code CLI, which has OpenTelemetry built in and exports spans `claude_code.interaction` (turn), `claude_code.llm_request` (model call), `claude_code.tool` (tool call), with phase children `claude_code.tool.blocked_on_user` / `claude_code.tool.execution`. All configuration is environment variables for that child process. No instrumentation package, no TracerProvider.

Known gaps (tell the user, don't try to fix): assistant reply text and cost are only on OTLP log events, which Maple's session views don't read, so transcripts have no assistant text and sessions show "unpriced"; no `gen_ai.agent.name`, so sub-agents get no separate lanes; tool arguments shown only for Bash (command) and Read/Edit/Write (file path).

Step 0: Detect

  • Which surface:
  • TypeScript: `@anthropic-ai/claude-agent-sdk` in `package.json`. Need >= 0.3.283 (bundles Claude Code 2.1.283). Upgrade if older.
  • Python: `claude-agent-sdk` in `pyproject.toml` / `requirements*.txt`. Need >= 0.2.160 (bundles 2.1.283). Upgrade if older.
  • The user wants their own `claude` CLI / IDE / desktop sessions in Maple: go to Step 2c. Check `claude --version` >= 2.1.283.
  • Check for `pathToClaudeCodeExecutable` (TS) / `cli_path` (Py): a custom CLI binary must also be >= 2.1.283.
  • Existing OpenTelemetry in the app: keep it. It can't carry the CLI's spans (the CLI exports on its own), but if the app has an active span when `query()` runs, both SDKs pass it as `TRACEPARENT` and the turn nests under it. Do not add a second SDK/exporter for the agent.
  • Remove any hook-based instrumentor for the Agent SDK (OpenInference `openinference-instrumentation-claude-agent-sdk`, Langfuse/LangSmith/Opik wrappers) if the user agrees: it duplicates every model call and its spans don't group by session in Maple.
  • Find where env is already set for the CLI: `options.env` / `ClaudeAgentOptions(env=...)`, Dockerfile, deploy manifests.
  • Settings files beat `options.env`: when `settingSources` / `setting_sources` is omitted (all sources) or includes `user`/`project`, an `env` block in `~/.claude/settings.json` or the repo's `.claude/settings.json` overrides the same keys passed in `options.env` (verified with `OTEL_SERVICE_NAME`). If those files set `OTEL_*` / `CLAUDE_CODE_*` keys, tell the user; for server apps that don't need file settings, suggest `settingSources: []` (Py `setting_sources=[]`). Omitted `settingSources` also loads the developer's personal plugins and MCP servers into the agent and its telemetry.
  • Find the conversation boundary: how the app calls `query()` per user message, and whether it stores a session id (`resume`, `sessionId`, `session_id`, `continue`, `ClaudeSDKClient`).

Step 1: Key and region

  • US endpoint `https://ingest.maple.dev`, EU endpoint `https://ingest.eu.maple.dev`. Header `Authorization=Bearer <key>`.
  • Key in the user's prompt: use it. No key: use the literal `MAPLE_TEST` (ingest accepts and discards it) and tell the user to replace it with their key from Settings → Ingestion.
  • Private `maple_sk_` keys never go in browser code. Ingest keys are write-only.
  • Follow the repo's existing secret/env convention (e.g. `MAPLE_INGEST_KEY` in `.env`). If there is none, inline the literal key; never ship a lookup that can come out `undefined` (`Bearer undefined` is an opaque 401).
  • A 401 `ingest_unauthorized` ("Invalid ingest key") with a key you trust usually means the key belongs to the other region (keys are region-bound): try the other endpoint.

Step 2a: TypeScript SDK

`npm install @anthropic-ai/claude-agent-sdk@latest zod`. Peers: zod ^4, `@anthropic-ai/sdk`, `@modelcontextprotocol/sdk`; install them explicitly if the package manager doesn't.

`options.env` REPLACES the child environment. Always spread `process.env`, and drop inherited `TRACEPARENT`/`TRACESTATE`. Build the env when calling `query()`, not at import, so values loaded later (dotenv) are included. Create `maple-env.ts` (adapt service name and environment; with no env convention, replace the key lookup and the throw with the literal key):

let warnedNoKey = false

export function mapleEnv(): Record<string, string | undefined> {
	const env: Record<string, string | undefined> = { ...process.env }
	delete env.TRACEPARENT
	delete env.TRACESTATE
	const key = process.env.MAPLE_INGEST_KEY
	if (!key) {
		// A missing key turns telemetry off; the agent still runs.
		if (!warnedNoKey) console.warn("MAPLE_INGEST_KEY is not set; Maple telemetry export is disabled")
		warnedNoKey = true
		return env
	}
	return {
		...env,
		CLAUDE_CODE_ENABLE_TELEMETRY: "1",
		CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
		OTEL_TRACES_EXPORTER: "otlp",
		OTEL_LOGS_EXPORTER: "otlp",
		OTEL_METRICS_EXPORTER: "otlp",
		OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
		OTEL_EXPORTER_OTLP_ENDPOINT: "https://ingest.maple.dev",
		OTEL_EXPORTER_OTLP_HEADERS: `Authorization=Bearer ${key}`,
		OTEL_SERVICE_NAME: "support-agent",
		OTEL_RESOURCE_ATTRIBUTES: "deployment.environment.name=production",
		OTEL_TRACES_EXPORT_INTERVAL: "1000",
		OTEL_LOGS_EXPORT_INTERVAL: "1000",
		OTEL_LOG_USER_PROMPTS: "1",
		OTEL_LOG_TOOL_DETAILS: "1",
		OTEL_LOG_TOOL_CONTENT: "1",
	}
}
``
Read more
Ships withmaple

OpenTelemetry observability platform

Get the whole plugin

Other skills on maple.