# Capability matrix

What Claude Code, Codex, the Python/TS SDK, and generic OTLP agents each support in TokenJam.

---

TokenJam takes telemetry from four kinds of source: **Claude Code**, **OpenAI Codex**, the **Python / TypeScript SDK**, and **any OTLP-compliant agent** wired straight to the ingest endpoint. They are not equivalent. Each source exposes a different set of hooks upstream (or none), so each one lights up a different slice of the product.

This page is the honest per-capability breakdown. Rows are capabilities, columns are sources, cells say what you actually get.

## Onboarding entry points

| Source | Command |
|---|---|
| Claude Code | `tj init --claude-code` |
| Codex CLI | `tj init --codex` |
| Python SDK | `tj init`, then `@watch()` + `patch_*()` in your code |
| Generic OTLP | None. Point your exporter at `tj serve`'s `/api/v1/spans` |

## What each source supports

| Capability | Claude Code | Codex CLI | Python / TS SDK | Generic OTLP |
|---|---|---|---|---|
| **Live capture** | Yes. OTLP log events convert to spans | Yes. Same converter, dedicated Codex event parsers | Yes. In-process `@watch()` + `patch_*()` spans | Yes. `POST /api/v1/spans` with any GenAI-semconv payload |
| **Historical backfill** | Yes. `tj backfill claude-code` reads `~/.claude/projects/*.jsonl` | Yes. `tj backfill codex` reads `~/.codex/sessions/` | N/A. SDK telemetry is live-only | Partial. `tj backfill otlp --source-file` replays an OTLP dump |
| **Zero-token statusline** | Yes. `tj statusline` wired into `~/.claude/settings.json` | No. Codex has no custom-command status line hook | N/A. No TUI to wire | N/A. Depends on the host agent's own UI |
| **Resume-brief hook** | Yes. `SessionStart` hook re-injects prior context | No. Codex has no hook system tj integrates with | N/A. Use `record_llm_call()` / `record_tool_call()` manually | N/A |
| **Per-terminal identity** | Yes. `claude` shell wrapper sets a distinct instance ID per terminal | No. Codex hardcodes one service name, so every terminal collapses into one tile | Yes. Caller sets `agent_id` per `@watch()` | Partial. Only if the agent sets its own resource attributes |
| **Dashboard (Lens web UI)** | Yes | Yes | Yes | Yes |
| **Analyzers** | Yes | Yes | Yes | Yes |
| **In-loop MCP server** | No, by design. An in-loop MCP measured +36% quota overhead on CC subscription users | No, by design. Same reasoning | Yes. This is the primary intended use | Yes, if the host agent supports MCP tool-calling |

## Reading the matrix

**The dashboard and analyzers are source-agnostic.** They read from the local DuckDB store, not from whichever path put the spans there. Once telemetry lands, `tj optimize`, `tj cost`, and the Lens web UI treat a backfilled Claude Code session and a live SDK span the same way.

**The MCP server is deliberately off for coding agents.** An in-request-path MCP server pays tool-definition overhead on every turn. A measured A/B put that at +36% model-weighted quota for Claude Code subscription users, so the coding-agent paths ship a zero-token statusline instead. The MCP still fits SDK and API integrations, where tj already sits in the request path. See [MCP server](/docs/mcp-server).

**The remaining Codex gap is the statusline, and it is upstream.** Codex's status line accepts a fixed list of built-in items and has no command-backed slot to inject an external line into, so tj stays fully out of band for Codex through OTel plus the `tj` CLI. Historical backfill is no longer a gap: `tj backfill codex` reads the session transcripts Codex writes to `~/.codex/sessions/`.