Skip to content
Monitoring
Skill

/maple-agent-tracing-strands

Trace Strands Agents (AWS, Python or TypeScript) with Maple: export Strands' built-in OpenTelemetry spans with messages on span attributes, a session id per conversation and per-call token counts, so each conversation is one Maple Agent Session with transcript, tool calls,

BOOST
From plugin
maple
1.8k37 skills
Install
$ npx -y skills add mapletechlabs/maple --skill maple-agent-tracing-strands --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-strands

Context preview

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

Trace Strands Agents (AWS, Python or TypeScript) with Maple: export Strands' built-in OpenTelemetry spans with messages on span attributes, a session id per conversation and per-call token counts, so each conversation is one Maple Agent Session with transcript, tool calls,

SKILL.md

maple-agent-tracing-strands.SKILL.md
name: maple-agent-tracing-strands
description: "Trace Strands Agents (AWS, Python or TypeScript) with Maple: export Strands' built-in OpenTelemetry spans with messages on span attributes, a session id per conversation and per-call token counts, so each conversation is one Maple Agent Session with transcript, tool calls, sub-agent lanes and tokens. Triggers on 'trace my strands agent', 'add Maple to strands', 'agent sessions for strands', 'OpenTelemetry for strands agents'."

Maple agent tracing: Strands Agents

Goal: every conversation = one Maple Agent Session. Each `agent(...)` / `invoke_async` / `stream_async` call = one turn (one trace) with transcript, `chat` spans with tokens, `execute_tool` spans with args/results, failed tools marked failed, sub-agents in their own lanes.

Mechanism: Strands' native OTel tracer (scope `strands.telemetry.tracer`, `gen_ai.provider.name=strands-agents`). No extra instrumentation package. Maple reads `session.id` (then `gen_ai.conversation.id`) as the session key, and reads span ATTRIBUTES only (never span events).

Step 0: Detect

1. Language and version.

  • Python: `python -c "from importlib.metadata import version; print(version('strands-agents'))"` or read `pyproject.toml` / `uv.lock` / `requirements*.txt`. Need >= 1.51 (tested 1.57.1): span-attribute content needs 1.48, tool args/results 1.51. Older → upgrade; do not work around it.
  • TypeScript: `@strands-agents/sdk` in `package.json` (tested 1.19.0). Follow Step 2 TS.

2. Existing OTel setup. Search for `StrandsTelemetry(`, `TracerProvider(`, `set_tracer_provider`, `opentelemetry-instrument`, `aws-opentelemetry-distro`, `logfire.configure`, `sentry_sdk.init`, `setupTracer(`, `NodeSDK(`, `NodeTracerProvider(`.

  • `StrandsTelemetry().setup_otlp_exporter()` already present → reuse it; only change env vars.
  • Another global provider exists (web framework, `opentelemetry-instrument`, ADOT on AgentCore) → do NOT call `StrandsTelemetry()`. Add `BatchSpanProcessor(OTLPSpanExporter(endpoint=".../v1/traces", headers={...}))` to that provider. Strands uses the global provider automatically.
  • Nothing → Step 2.

3. Find: every `Agent(` construction (and whether it is module-level/shared), every place the agent is invoked, where the chat/thread/conversation id lives in the request, every `session_manager=`, every `.as_tool(`, `Swarm(`, `GraphBuilder(` / `Graph(`. 4. Other instrumentors on the same model calls (OpenLIT, OpenLLMetry `Traceloop.init`, OpenInference, `opentelemetry-instrumentation-openai*`, botocore/Bedrock GenAI instrumentation) → they double-trace model calls. Keep Strands' spans; ask before removing the others if they serve something else.

Step 1: Key and region

  • US: `https://ingest.maple.dev`. EU: `https://ingest.eu.maple.dev`.
  • Header: `Authorization=Bearer <key>`.
  • Key given in the 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.
  • Never put a private `maple_sk_` key in browser code.
  • Follow the repo's secret/env convention (`.env`, settings module, secret manager, container env) if it has one. Otherwise inline is acceptable: ingest keys are write-only.
  • Key read from a secret env var in code: when it is unset, log one warning (`MAPLE_INGEST_KEY is not set; Maple telemetry export is disabled`) and skip the Maple exporter so the app runs normally. Never raise, throw or exit over the key; no bare `KeyError` on import, no `Bearer undefined` (an opaque 401).

Step 2: Install + init

Python. Add with the repo's package manager, keeping existing extras (`openai`, `anthropic`, `litellm`, ...):

pip install 'strands-agents[otel]>=1.57'

Env vars. Put them where the repo keeps env (shell/.env/container). `OTEL_SEMCONV_STABILITY_OPT_IN` MUST be in the process environment before the first `Agent(` is constructed (Strands reads it once, into a singleton). Setting it via `os.environ` is only acceptable at the very top of the entry point, before any strands import.

OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.maple.dev
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <key>"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=<service name>
OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=<env>
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental,gen_ai_span_attributes_only
  • Endpoint is the base URL; the exporter appends `/v1/traces`.
  • The exporter reads these when it is built. If the app loads `.env` (`load_dotenv()`, `dotenv`, `--env-file`), load it at the top of the tracing module, before `StrandsTelemetry()` / `setupTracer()`; otherwise the exporter silently targets `localhost:4318` with no key.
  • `gen_ai_latest_experimental`: `{role, parts}` messages, `gen_ai.system_instructions`, tool args/results.
  • `gen_ai_span_attributes_only`: messages as span attributes. Without it the Maple transcript is EMPTY.
  • If the repo already has an `OTEL_SEMCONV_STABILITY_OPT_IN` value, merge tokens (comma-separated), don't replace.

Init once, imported from the entry point before any agent runs (skip if Step 0 found an existing provider):

# telemetry.py
from strands.telemetry import StrandsTelemetry

telemetry = StrandsTelemetry().setup_otlp_exporter()

TypeScript. OTel packages are optional peers; install them:

npm install @strands-agents/sdk @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-metrics @opentelemetry/exporter-metrics-otlp-http

Same env vars.

import { setupTracer } from "@strands-agents/sdk/telemetry"

export const provider = setupTracer({ exporters: { otlp: true } }) // before the first Agent; reads OTEL_EXPORTER_OTLP_*

Step 3: Session id

Python: pass the conversation id as `session.id` in `trace_attributes` on the agent that handles the turn.

agent =
Read more
Ships withmaple

OpenTelemetry observability platform

Get the whole plugin

Other skills on maple.