Skip to content

Claude Code Skill

Autotel includes a Claude Code skill that gives AI coding agents deep knowledge of the instrumentation API. When the skill is active, Claude Code can:

  • Wrap functions with trace() and set semantic attributes
  • Convert console.log calls to getRequestLogger() wide events
  • Add createStructuredError() with why, fix, and link fields
  • Set up init() with the correct configuration
  • Review code for observability anti-patterns

The skill lives at .claude/skills/autotel/ in the repository root. Claude Code discovers it automatically when you work inside the repo. No extra configuration needed.

For other projects that use Autotel, copy the .claude/skills/autotel/ directory into your repo's .claude/skills/ folder.

.claude/skills/autotel/
├── SKILL.md # Main skill entry point
└── references/
├── wide-events.md # Canonical log lines guide
├── structured-errors.md # Error handling patterns
├── request-logger.md # Request-scoped logging
└── code-review.md # Anti-pattern detection checklist

File: .claude/skills/autotel/references/wide-events.md

A wide event is a single structured record per request containing all context accumulated during processing. Instead of 10 scattered log lines, you get one queryable event with 25+ fields.

Covers:

  • Anatomy of a canonical log line (core fields, trace identifiers, duration)
  • Context grouping with dot-prefixed keys
  • Configuration options (keep, shouldEmit, pretty, drain, attributeRedactor)
  • Drain pipeline for production fan-out with batching and retry
  • SQL query examples for analyzing wide events
init({
service: 'my-app',
canonicalLogLines: {
enabled: true,
rootSpansOnly: true,
keep: [
{ status: 500 },
{ durationMs: 1000 },
],
pretty: true,
drain: (event) => sendToAnalytics(event),
},
});

File: .claude/skills/autotel/references/structured-errors.md

Covers three APIs:

| API | Purpose | |-----|---------| | createStructuredError() | Create errors with message, why, fix, link, code, status, details | | recordStructuredError() | Record a structured error onto a trace context (sets status + exception + attributes) | | parseError() | Normalize any thrown value to a consistent shape for API responses |

Common patterns:

  • API endpoint error responses
  • Wrapping third-party errors (Stripe, etc.)
  • Domain validation errors
  • Span attribute mapping (error.type, error.why, error.fix, etc.)
throw createStructuredError({
message: 'Payment declined',
why: 'Card has insufficient funds for the requested amount',
fix: 'Prompt the user to update their payment method',
code: 'CARD_DECLINED',
status: 402,
link: 'https://docs.stripe.com/declines/codes#insufficient-funds',
});

File: .claude/skills/autotel/references/request-logger.md

Covers the getRequestLogger() API and framework adapters:

  • log.set(fields) — merge fields onto the span (nested objects flatten automatically)
  • log.info(message, fields?) — span event at info level
  • log.warn(message, fields?) — span event at warn level, promotes canonical log line
  • log.error(error, fields?) — record structured error on span
  • log.getContext() — snapshot of all accumulated fields
  • log.emitNow(overrides?) — emit snapshot immediately with onEmit fan-out

Framework adapters:

  • useLogger() with withAutotel() (Next.js, Nitro, Cloudflare Workers)
  • useLogger() alone when middleware already creates the span (Hono)
  • Custom adapters via createUseLogger()
const log = getRequestLogger(ctx);
log.set({ feature: 'checkout', tier: req.user.tier });
const cart = await loadCart(req.cartId);
log.set({ cart_items: cart.items.length, cart_total: cart.total });

File: .claude/skills/autotel/references/code-review.md

Anti-pattern detection table with severity levels and fixes:

| Anti-Pattern | Severity | Fix | |---|---|---| | console.log for request context | High | getRequestLogger(ctx) with log.set() | | console.error in catch blocks | High | log.error(err) or recordStructuredError() | | throw new Error('...') without context | Medium | createStructuredError({ message, why, fix }) | | catch (e) { throw e } without recording | Medium | Add log.error(e) before rethrowing | | JSON.stringify for log output | High | log.set() — attributes flatten automatically | | Manual request ID generation | Low | ctx.correlationId from trace context |

Also includes a 4-step migration path from console.log to fully instrumented Autotel code.

When you ask Claude Code to instrument code, review for observability, or work with errors/logging, the skill activates and loads the relevant reference guides. Claude Code then applies the patterns directly — wrapping functions with trace(), replacing console.log with request loggers, and adding structured error context.

  • "Add tracing to this handler"
  • "Convert the console.logs in this file to use request logger"
  • "Add structured errors to the catch blocks"
  • "Review this file for observability anti-patterns"
  • "Set up canonical log lines for this service"

The skill described above teaches Claude Code how to write instrumented code. A second class of agent task — investigating telemetry (querying traces, diagnosing errors, ranking expensive LLM calls) — is served by two interchangeable mechanisms:

  1. autotel-mcp — an MCP server the user wires into .mcp.json.
  2. The autotel CLI itself — the same investigation surface as one-shot subcommands that emit JSON on stdout.

Both share the same backends (packages/autotel-mcp/src/backends/) and the same query helpers, so the answers are identical. The CLI route is useful when you'd rather not configure an MCP server, or when you're driving an agent harness that doesn't speak MCP.

| MCP tool group | CLI equivalent | |---|---| | health.backend_health | autotel health | | health.backend_capabilities | autotel capabilities | | discovery.discover_services | autotel discover services | | discovery.discover_trace_fields | autotel discover trace-fields | | discovery.discover_log_fields | autotel discover log-fields | | investigation.search_traces | autotel query traces | | investigation.search_spans | autotel query spans | | investigation.get_trace | autotel trace get <id> | | investigation.summarize_trace | autotel trace summary <id> | | signals.list_metrics | autotel query metrics | | signals.search_logs | autotel query logs | | topology.list_services | autotel topology services | | topology.list_operations | autotel topology operations <name> | | topology.service_map | autotel topology map | | diagnosis.find_anomalies | autotel diagnose anomalies | | diagnosis.find_root_cause | autotel diagnose root-cause <id> | | diagnosis.find_errors | autotel diagnose errors | | diagnosis.check_slos | autotel diagnose slos --service <name> | | correlation.correlate | autotel correlate trace <id> | | correlation.explain_slowdown | autotel correlate explain-slowdown --service <name> | | llm-analytics.get_llm_usage | autotel llm usage | | llm-analytics.list_llm_models | autotel llm models | | llm-analytics.get_llm_model_stats | autotel llm model-stats --model-name <name> | | llm-analytics.get_llm_expensive_traces | autotel llm expensive | | llm-analytics.get_llm_slow_traces | autotel llm slow | | llm-analytics.list_llm_tools | autotel llm tools | | semantic-conventions.* | autotel semconv list \| get <ns> \| refresh | | instrumentation.score_span_instrumentation | autotel score (JSON span on stdin) | | instrumentation.explain_instrumentation_score | autotel score explain | | collector-config.* | autotel collector validate \| suggest \| explain | | collector-schema.* | autotel collector versions \| components \| schema \| readme \| validate-component \| refresh |

Every backend-touching command accepts the same flags as the MCP server's env vars, with flags winning over env when both are set:

Terminal window
autotel query traces \
--backend tempo \
--tempo-base-url http://localhost:3200 \
--service-name checkout \
--error-only \
--limit 20

Available backends: collector (built-in OTLP receiver, in-memory or libsql-persisted) · jaeger · tempo · prometheus · loki · stack (composite) · auto (probe localhost) · fixture (JSON file).

Every investigate command emits one JSON document on stdout. Success:

{ "ok": true, "command": "query traces", "data": { /* … */ } }

Failure:

{
"ok": false,
"error": {
"type": "validation" | "runtime",
"code": "AUTOTEL_E_INVALID_INPUT" | "AUTOTEL_E_UNKNOWN",
"message": "",
"retryable": false
}
}

Exit code is 0 on success, 2 on validation, 1 on runtime. Agents check result.ok and read data (or error).

| | MCP server | CLI subcommands | |---|---|---| | Install step | Edit .mcp.json, restart client | npm i -g autotel | | Transport | stdio / HTTP per client | Shell exec | | Streaming results | Yes | One-shot per command | | Cross-tool state | Server holds backend connection | Each call re-opens | | Works in non-MCP agents | No | Yes | | Discoverability | MCP tool list | autotel <group> --help |

For an agent driving a single investigation prompt the CLI is usually faster — no MCP handshake, no per-tool round-trip. For an interactive incident review with many follow-up queries against a heavy backend, the MCP server's persistent backend connection wins.

A ready-made skill that maps prompts → CLI commands ships at .claude/skills/autotel-investigate/SKILL.md. Claude Code discovers it automatically when you work inside the repo; copy the directory into another project's .claude/skills/ to use it elsewhere.