Mz Query Tracing
MaterializeInc/materialize
Debug SQL execution time via distributed tracing (OpenTelemetry / Tempo).
Answers questions about agent spend, token use, traces, events, errors and tool usage by running read-only SQL against a local TMA1 observability store.
$ npx skills add tma1-ai/tma1 --skill tma1 -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install tma1-ai/tma1 tma1 --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/tma1-ai/tma1.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude-plugin/skills/tma1 .claude/skills/tma1 && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "tma1" agent skill from https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1 into .claude/skills/tma1/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tma1", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1Type this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add tma1-ai/tma1 --skill tma1 -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install tma1-ai/tma1 tma1 --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/tma1-ai/tma1.git skills-src && mkdir -p .agents/skills && cp -r skills-src/claude-plugin/skills/tma1 .agents/skills/tma1 && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "tma1" agent skill from https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1 into .agents/skills/tma1/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tma1", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add tma1-ai/tma1 --skill tma1 -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install tma1-ai/tma1 tma1 --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/tma1-ai/tma1.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/claude-plugin/skills/tma1 .cursor/skills/tma1 && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "tma1" agent skill from https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1 into .cursor/skills/tma1/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tma1", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/tma1-ai/tma1.git --path claude-plugin/skills/tma1--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add tma1-ai/tma1 --skill tma1 -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install tma1-ai/tma1 tma1 --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/tma1-ai/tma1.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/claude-plugin/skills/tma1 .gemini/skills/tma1 && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "tma1" agent skill from https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1 into .gemini/skills/tma1/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tma1", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install tma1-ai/tma1 tma1Installs for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add tma1-ai/tma1 --skill tma1 -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/tma1-ai/tma1.git skills-src && mkdir -p .github/skills && cp -r skills-src/claude-plugin/skills/tma1 .github/skills/tma1 && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "tma1" agent skill from https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1 into .github/skills/tma1/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tma1", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add tma1-ai/tma1 --skill tma1 -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install tma1-ai/tma1 tma1 --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/tma1-ai/tma1.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/claude-plugin/skills/tma1 .opencode/skills/tma1 && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "tma1" agent skill from https://github.com/tma1-ai/tma1/tree/main/claude-plugin/skills/tma1 into .opencode/skills/tma1/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "tma1", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
tma1Answers questions about agent spend, token use, traces, events, errors and tool usage by running read-only SQL against a local TMA1 observability store.
TMA1 collects telemetry from Claude Code, Codex, GitHub Copilot CLI, OpenClaw and any agent that emits standard OpenTelemetry GenAI traces. The skill first checks that the local TMA1 service answers on its health endpoint on port 14318 and, if it does not, points you to /tma1-setup. It then lists the tables in the database to see which agents have data and picks matching queries.
Queries run one read-only SELECT at a time through the exec_query MCP tool, which caps rows at 100 and cell length at 2000 by default and flags truncated output. Without the MCP server, the agent can post SQL to a local HTTP query endpoint instead. For jobs such as keyword session search or reading a transcript, the skill prefers purpose-built tools over hand-written SQL.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit f3dfed8. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
mcp__tma1__exec_queryBashFrom allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
curlFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use curl, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
TMA1 Observability Query loads about 5.1k tokens when it runs. Until then it costs about 70 tokens; SKILL.md has 859 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
allowed-tools: mcp__tma1__exec_query, BashAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from tma1-ai/tma1 at commit f3dfed8, republished under its Apache-2.0 licence (© tma1-ai). 859 words, ~5,127 tokens.
.claude/skills/tma1/SKILL.md (or your agent's skills folder).You are helping the user query their local TMA1 observability data.
TMA1 stores data from five kinds of sources:
~/.codex/sessions/)~/.copilot/session-state/<sessionId>/events.jsonl, no OTel). Data lives in tma1_hook_events (agent_source='copilot_cli') and tma1_messages (session_id LIKE 'cp:%').~/.openclaw/agents/*/sessions/)curl -sf http://localhost:14318/healthIf this fails, tell the user to run /tma1-setup first.
exec_query: SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'Check which tables exist to determine what queries to use:
claude_code_cost_usage_USD_total exists → use Claude Code metrics queriescodex_turn_token_usage_sum or codex_* tables exist → use Codex queriesopenclaw_tokens_total exists → use OpenClaw queriesopentelemetry_traces exists → use traces-based queries (check column names to distinguish OpenClaw vs GenAI)opentelemetry_logs exists → use logs queries for event detailstma1_hook_events has rows with agent_source = 'copilot_cli' → use Copilot CLI queriestma1_hook_events or tma1_messages exists → use session/conversation queriesRun every query below through the mcp__tma1__exec_query MCP tool —
one read-only SELECT per call, returning columns + rows. It caps
rows (row_limit, default 100) and cell length (cell_chars, default
2000), and sets truncated when it had to cut.
If that tool isn't available (an agent without TMA1's MCP server wired up), fall back to the HTTP API:
curl -s -X POST http://localhost:14318/api/query \
-H 'Content-Type: application/json' \
-d '{"sql": "<SQL>"}'Don't reach for SQL when a purpose-built tool answers the question:
search_sessions finds sessions by keyword, get_session_transcript
reads one session's conversation, get_peer_sessions lists recent work
by agent, get_session_state describes the session you're in.
Important: The underlying database (GreptimeDB) uses json_get_string(), json_get_int(), json_get_float() for JSON column access. The -> / ->> operators are NOT supported. Keys containing dots (like session.id) are interpreted as nested paths and cannot be accessed via json_get_*. All timestamps are stored and returned in UTC. Convert in SQL when the user wants local time (ts + INTERVAL '8 hours'), or, on the curl fallback, send -H 'X-Greptime-Timezone: Asia/Shanghai' to shift both WHERE-clause parsing and rendering.
Note: Claude Code resets OTel cumulative counters on each new session. Use logs (opentelemetry_logs WHERE body = 'claude_code.api_request') for accurate cost/token totals. The _total metric tables only reflect the last session's counter value.
SELECT json_get_string(log_attributes, 'model') AS model,
ROUND(SUM(json_get_float(log_attributes, 'cost_usd')), 4) AS cost_usd,
COUNT(*) AS requests
FROM opentelemetry_logs
WHERE body = 'claude_code.api_request'
AND timestamp >= DATE_TRUNC('day', NOW())
GROUP BY model
ORDER BY cost_usd DESCSELECT json_get_string(log_attributes, 'model') AS model,
SUM(json_get_int(log_attributes, 'input_tokens')) AS input_tok,
SUM(json_get_int(log_attributes, 'output_tokens')) AS output_tok,
SUM(json_get_int(log_attributes, 'cache_read_tokens')) AS cache_read,
SUM(json_get_int(log_attributes, 'cache_creation_tokens')) AS cache_write
FROM opentelemetry_logs
WHERE body = 'claude_code.api_request'
AND timestamp >= DATE_TRUNC('day', NOW())
GROUP BY model
ORDER BY input_tok DESCSELECT timestamp,
json_get_string(log_attributes, 'model') AS model,
json_get_int(log_attributes, 'input_tokens') AS input_tok,
json_get_int(log_attributes, 'output_tokens') AS output_tok,
json_get_float(log_attributes, 'cost_usd') AS cost_usd,
json_get_float(log_attributes, 'duration_ms') AS duration_ms
FROM opentelemetry_logs
WHERE body = 'claude_code.api_request'
ORDER BY timestamp DESC
LIMIT 20SELECT timestamp,
json_get_string(log_attributes, 'model') AS model,
json_get_string(log_attributes, 'error') AS error,
json_get_string(log_attributes, 'status_code') AS status_code,
json_get_float(log_attributes, 'duration_ms') AS duration_ms
FROM opentelemetry_logs
WHERE body = 'claude_code.api_error'
ORDER BY timestamp DESC
LIMIT 20SELECT json_get_string(log_attributes, 'tool_name') AS tool,
COUNT(*) AS uses,
ROUND(AVG(json_get_float(log_attributes, 'duration_ms'))) AS avg_ms,
SUM(CASE WHEN json_get_string(log_attributes, 'success') = 'true' THEN 1 ELSE 0 END) AS ok,
SUM(CASE WHEN json_get_string(log_attributes, 'success') = 'false' THEN 1 ELSE 0 END) AS fail
FROM opentelemetry_logs
WHERE body = 'claude_code.tool_result'
GROUP BY tool
ORDER BY uses DESCSELECT timestamp,
json_get_int(log_attributes, 'prompt_length') AS prompt_len
FROM opentelemetry_logs
WHERE body = 'claude_code.user_prompt'
ORDER BY timestamp DESC
LIMIT 20SELECT type,
ROUND(MAX(greptime_value), 1) AS seconds
FROM claude_code_active_time_seconds_total
WHERE greptime_timestamp >= DATE_TRUNC('day', NOW())
GROUP BY typeSELECT type,
MAX(greptime_value) AS lines
FROM claude_code_lines_of_code_count_total
WHERE greptime_timestamp >= DATE_TRUNC('day', NOW())
GROUP BY typeSELECT json_get_string(log_attributes, 'model') AS model,
ROUND(SUM(json_get_float(log_attributes, 'cost_usd')), 4) AS cost_usd,
COUNT(*) AS requests
FROM opentelemetry_logs
WHERE body = 'claude_code.api_request'
GROUP BY model
ORDER BY cost_usd DESCThese queries work when openclaw_tokens_total or opentelemetry_traces with openclaw.* attributes exist.
SELECT timestamp,
"span_attributes.openclaw.model" AS model,
"span_attributes.openclaw.channel" AS channel,
CAST("span_attributes.openclaw.tokens.input" AS BIGINT) AS input_tok,
CAST("span_attributes.openclaw.tokens.output" AS BIGINT) AS output_tok,
CAST("span_attributes.openclaw.tokens.cache_read" AS BIGINT) AS cache_read,
ROUND(duration_nano / 1000000.0, 1) AS duration_ms
FROM opentelemetry_traces
WHERE span_name = 'openclaw.model.usage'
ORDER BY timestamp DESC
LIMIT 20SELECT openclaw_model AS model, openclaw_token AS token_type, SUM(greptime_value) AS tokens
FROM openclaw_tokens_total
WHERE greptime_timestamp > NOW() - INTERVAL '1 day'
GROUP BY openclaw_model, openclaw_token
ORDER BY tokens DESCSELECT "span_attributes.openclaw.channel" AS channel,
"span_attributes.openclaw.outcome" AS outcome,
COUNT(*) AS messages
FROM opentelemetry_traces
WHERE span_name = 'openclaw.message.processed'
AND timestamp > NOW() - INTERVAL '1 day'
GROUP BY channel, outcome
ORDER BY messages DESCSELECT timestamp, span_name,
"span_attributes.openclaw.channel" AS channel,
"span_attributes.openclaw.sessionKey" AS session
FROM opentelemetry_traces
WHERE span_name IN ('openclaw.webhook.error', 'openclaw.session.stuck')
ORDER BY timestamp DESC
LIMIT 20SELECT "span_attributes.openclaw.model" AS model,
COUNT(*) AS requests,
SUM(CAST("span_attributes.openclaw.tokens.input" AS BIGINT)) AS input_tok,
SUM(CAST("span_attributes.openclaw.tokens.output" AS BIGINT)) AS output_tok
FROM opentelemetry_traces
WHERE span_name = 'openclaw.model.usage'
AND timestamp > NOW() - INTERVAL '1 day'
GROUP BY model
ORDER BY input_tok DESCThese queries work when Codex telemetry is flowing into opentelemetry_logs,
opentelemetry_traces, or native codex_* metric tables.
SELECT timestamp,
COALESCE(json_get_string(log_attributes, 'model'), 'unknown') AS model,
COALESCE(json_get_int(log_attributes, 'input_token_count'), 0) AS input_tok,
COALESCE(json_get_int(log_attributes, 'output_token_count'), 0) AS output_tok,
COALESCE(json_get_int(log_attributes, 'cached_token_count'), 0) AS cached_tok,
json_get_float(log_attributes, 'duration_ms') AS duration_ms
FROM opentelemetry_logs
WHERE scope_name LIKE 'codex_%'
AND json_get_int(log_attributes, 'input_token_count') IS NOT NULL
ORDER BY timestamp DESC
LIMIT 20SELECT model,
SUM(greptime_value) AS requests
FROM codex_websocket_request_total
WHERE greptime_timestamp > NOW() - INTERVAL '1 day'
GROUP BY model
ORDER BY requests DESCSELECT tool,
success,
SUM(greptime_value) AS calls
FROM codex_tool_call_total
WHERE greptime_timestamp > NOW() - INTERVAL '1 day'
GROUP BY tool, success
ORDER BY calls DESCSELECT s.model,
ROUND(SUM(s.greptime_value) / NULLIF(SUM(c.greptime_value), 0), 2) AS avg_ttft_ms
FROM codex_turn_ttft_duration_ms_milliseconds_sum s
JOIN codex_turn_ttft_duration_ms_milliseconds_count c
ON s.model = c.model
AND s.service_version = c.service_version
AND s.greptime_timestamp = c.greptime_timestamp
WHERE s.greptime_timestamp > NOW() - INTERVAL '1 day'
GROUP BY s.model
ORDER BY avg_ttft_ms DESCCopilot CLI has no OTel exporter. All data comes from ~/.copilot/session-state/*/events.jsonl and lands in:
tma1_hook_events with agent_source = 'copilot_cli' — session lifecycle, tool calls, subagent lifecycletma1_messages with session_id LIKE 'cp:%' — user / assistant / thinking messagesSELECT session_id,
MIN(ts) AS started,
MAX(ts) AS last_event,
SUM(CASE WHEN event_type = 'PreToolUse' THEN 1 ELSE 0 END) AS tool_calls,
SUM(CASE WHEN event_type = 'PostToolUseFailure' THEN 1 ELSE 0 END) AS tool_failures
FROM tma1_hook_events
WHERE agent_source = 'copilot_cli'
AND ts > NOW() - INTERVAL '1 day'
GROUP BY session_id
ORDER BY last_event DESC
LIMIT 20SELECT model,
SUM(COALESCE(output_tokens, 0)) AS output_tokens,
COUNT(*) AS messages
FROM tma1_messages
WHERE session_id LIKE 'cp:%'
AND model != ''
AND ts > NOW() - INTERVAL '1 day'
GROUP BY model
ORDER BY output_tokens DESCSELECT tool_name,
COUNT(*) AS calls
FROM tma1_hook_events
WHERE agent_source = 'copilot_cli'
AND event_type = 'PreToolUse'
AND tool_name != ''
AND ts > NOW() - INTERVAL '1 day'
GROUP BY tool_name
ORDER BY calls DESC
LIMIT 15SELECT ts, agent_type, metadata
FROM tma1_hook_events
WHERE agent_source = 'copilot_cli'
AND event_type = 'SubagentStop'
ORDER BY ts DESC
LIMIT 20SELECT ts, message_type, "role", model, content
FROM tma1_messages
WHERE session_id = 'cp:<sessionId>'
ORDER BY ts ASCReplace <sessionId> with the on-disk directory name from ~/.copilot/session-state/ (the cp: prefix is the DB namespace).
Requires the OTel SDK to capture conversation content into opentelemetry_logs.
For Python with OpenAI, use opentelemetry-instrumentation-openai-v2 and set
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true.
The openai_v2 instrumentation stores log bodies in these formats:
{"content":"..."}{"message":{"role":"assistant","content":"..."}}{"content":"...","id":"call_..."}{"tool_calls":[...]} (no displayable content)SELECT timestamp, trace_id,
CASE
WHEN json_get_string(parse_json(body), 'message.role') IS NOT NULL
THEN json_get_string(parse_json(body), 'message.role')
WHEN json_get_string(parse_json(body), 'id') IS NOT NULL THEN 'tool'
WHEN json_get_string(parse_json(body), 'content') IS NOT NULL THEN 'user'
ELSE 'unknown'
END AS role,
COALESCE(
json_get_string(parse_json(body), 'message.content'),
json_get_string(parse_json(body), 'content')
) AS content
FROM opentelemetry_logs
WHERE matches_term(body, 'your_keyword')
ORDER BY timestamp DESC LIMIT 50SELECT timestamp,
CASE
WHEN json_get_string(parse_json(body), 'message.role') IS NOT NULL
THEN json_get_string(parse_json(body), 'message.role')
WHEN json_get_string(parse_json(body), 'id') IS NOT NULL THEN 'tool'
WHEN json_get_string(parse_json(body), 'content') IS NOT NULL THEN 'user'
ELSE 'unknown'
END AS role,
COALESCE(
json_get_string(parse_json(body), 'message.content'),
json_get_string(parse_json(body), 'content')
) AS content
FROM opentelemetry_logs
WHERE trace_id = '<trace_id>'
ORDER BY timestamp LIMIT 100SELECT timestamp, trace_id,
json_get_string(parse_json(body), 'content') AS content
FROM opentelemetry_logs
WHERE trace_id != ''
AND json_get_string(parse_json(body), 'content') IS NOT NULL
AND json_get_string(parse_json(body), 'message.role') IS NULL
AND json_get_string(parse_json(body), 'id') IS NULL
AND (
body LIKE '%ignore%previous%instructions%'
OR body LIKE '%ignore%above%'
OR body LIKE '%disregard%previous%'
OR body LIKE '%reveal%system%prompt%'
OR body LIKE '%jailbreak%'
OR body LIKE '%DAN%mode%'
OR body LIKE '%bypass%safety%'
OR body LIKE '%[system]%'
)
AND timestamp > NOW() - INTERVAL '24 hours'
ORDER BY timestamp DESC LIMIT 50These queries only work when opentelemetry_traces exists with gen_ai.* attributes.
SELECT span_name,
"span_attributes.gen_ai.request.model" AS model,
"span_attributes.gen_ai.usage.input_tokens" AS input_tok,
"span_attributes.gen_ai.usage.output_tokens" AS output_tok,
duration_nano / 1000000 AS duration_ms,
timestamp
FROM opentelemetry_traces
WHERE "span_attributes.gen_ai.system" IS NOT NULL
ORDER BY timestamp DESC
LIMIT 20-- Joins against tma1_model_pricing (seeded by tma1-server on first start).
-- To see/edit pricing: SELECT * FROM tma1_model_pricing ORDER BY priority;
SELECT t.model,
ROUND(SUM(
CAST(t.input_tok AS DOUBLE) * p.input_price / 1e6 +
CAST(t.output_tok AS DOUBLE) * p.output_price / 1e6
), 4) AS cost_usd
FROM (
SELECT "span_attributes.gen_ai.request.model" AS model,
"span_attributes.gen_ai.usage.input_tokens" AS input_tok,
"span_attributes.gen_ai.usage.output_tokens" AS output_tok
FROM opentelemetry_traces
WHERE "span_attributes.gen_ai.system" IS NOT NULL
AND timestamp >= DATE_TRUNC('day', NOW())
) t
JOIN tma1_model_pricing p
ON t.model LIKE CONCAT('%', p.model_pattern, '%')
GROUP BY t.model
ORDER BY cost_usd DESCSELECT "span_attributes.gen_ai.request.model" AS model,
SUM(CAST("span_attributes.gen_ai.usage.input_tokens" AS DOUBLE)) AS input_tok,
SUM(CAST("span_attributes.gen_ai.usage.output_tokens" AS DOUBLE)) AS output_tok,
COUNT(*) AS requests
FROM opentelemetry_traces
WHERE "span_attributes.gen_ai.system" IS NOT NULL
AND timestamp >= DATE_TRUNC('day', NOW())
GROUP BY model
ORDER BY input_tok DESCSELECT "span_attributes.gen_ai.request.model" AS model,
COUNT(*) AS requests,
SUM(CASE WHEN span_status_code = 'STATUS_CODE_ERROR' THEN 1 ELSE 0 END) AS errors
FROM opentelemetry_traces
WHERE "span_attributes.gen_ai.system" IS NOT NULL
AND timestamp > NOW() - INTERVAL '24 hours'
GROUP BY model
ORDER BY errors DESCSELECT "span_attributes.gen_ai.request.model" AS model,
ROUND(APPROX_PERCENTILE_CONT(duration_nano, 0.50) / 1000000.0, 0) AS p50_ms,
ROUND(APPROX_PERCENTILE_CONT(duration_nano, 0.95) / 1000000.0, 0) AS p95_ms,
COUNT(*) AS requests
FROM opentelemetry_traces
WHERE "span_attributes.gen_ai.system" IS NOT NULL
AND timestamp > NOW() - INTERVAL '24 hours'
GROUP BY model
ORDER BY p95_ms DESCThe tma1_messages table includes token usage columns: input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, duration_ms (populated for assistant messages from JSONL transcripts). Both tma1_hook_events and tma1_messages include a conversation_id column linking events within the same conversation turn. Agent source is identified by agent_source in tma1_hook_events: 'claude_code', 'codex', or 'openclaw'. OpenClaw session IDs are prefixed oc:<agentId>:<sessionId>.
-- List recent sessions with tool counts
SELECT session_id, conversation_id, agent_source, MIN(ts) AS start_ts, MAX(ts) AS end_ts,
SUM(CASE WHEN event_type = 'PreToolUse' THEN 1 ELSE 0 END) AS tool_calls,
SUM(CASE WHEN event_type = 'SubagentStart' THEN 1 ELSE 0 END) AS subagents
FROM tma1_hook_events WHERE ts > NOW() - INTERVAL '24 hours'
GROUP BY session_id, conversation_id, agent_source ORDER BY MIN(ts) DESC
-- Search conversation content
SELECT session_id, conversation_id, ts, message_type, content FROM tma1_messages
WHERE matches_term(content, 'search keyword')
AND ts > NOW() - INTERVAL '7 days'
ORDER BY ts DESC LIMIT 20
-- Session token usage (from JSONL transcripts)
SELECT session_id, SUM(input_tokens) AS input_tok, SUM(output_tokens) AS output_tok,
SUM(cache_read_tokens) AS cache_read, SUM(cache_creation_tokens) AS cache_write
FROM tma1_messages WHERE message_type = 'assistant' AND ts > NOW() - INTERVAL '24 hours'
GROUP BY session_id ORDER BY input_tok DESC
-- Session tool breakdown
SELECT tool_name, COUNT(*) AS calls FROM tma1_hook_events
WHERE session_id = '<session_id>' AND event_type = 'PreToolUse'
GROUP BY tool_name ORDER BY calls DESCRun the chosen query through exec_query (or curl, as the fallback) and present the result as a readable table or summary.
If a table does not exist (error code 4001), skip that query and try the alternative data source.
If the query returns no rows, explain that there may not be data for the requested time range.
After presenting results, suggest related queries the user might want:
Remind the user that the full dashboard is available at http://localhost:14318.
© tma1-ai, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in claude-plugin/skills/tma1 of tma1-ai/tma1.
Open the folder on GitHubat commit f3dfed8
TMA1 Observability Query next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| TMA1 Observability Query this skilltma1-ai/tma1 | 119 | — | ~5.1k | Automated safety check: Notes | Apache-2.0 | |
| Mz Query TracingMaterializeInc/materialize | 6.4k | — | ~1.8k | Automated safety check: Pass | Custom licence | |
| Sentry Elixir SDKgetsentry/sentry-for-ai | 268 | — | ~3.5k | Automated safety check: Pass | Apache-2.0 | |
| Agentsop Observability Setupagentsope/SkillAlchemy | 436 | — | ~4.4k | Automated safety check: Pass | MIT | |
| Tidewave Integrationoliver-kriska/claude-elixir-phoenix | 565 | — | ~1.3k | Automated safety check: Pass | MIT | |
| Phoenix Client DevelopmentArize-ai/phoenix | 12k | — | ~370 | Automated safety check: Pass | Apache-2.0 |
MaterializeInc/materialize
Debug SQL execution time via distributed tracing (OpenTelemetry / Tempo).
getsentry/sentry-for-ai
Full Sentry SDK setup for Elixir. An agent skill from getsentry/sentry-for-ai.
agentsope/SkillAlchemy
Enhancement-overlay skill — the DECISION + WIRING layer for LM observability that the single-backend skills [[langsmith]], [[phoenix]], [[mlflow]] do NOT cover.
oliver-kriska/claude-elixir-phoenix
Tidewave MCP runtime tools — debugging, smoke testing, live state inspection, SQL queries, hex docs.
Arize-ai/phoenix
Development guide for the @arizeai/phoenix-client TypeScript SDK — run and resume experiments, manage OpenTelemetry tracer providers with stack-based attach/detach, and write vitest unit and…
sickn33/agentic-awesome-skills
Create and manage datasets on Hugging Face Hub. An agent skill from sickn33/agentic-awesome-skills.
tma1-ai/tma1
Lists recent sessions on the current project from other agents such as Claude Code, OpenClaw and Copilot CLI, or from your own earlier work.
tma1-ai/tma1
Lists recent sessions of peer coding agents such as Codex, OpenClaw or Copilot CLI, or your own past sessions, so you can act on their work without copying between terminals.
tma1-ai/tma1
Searches past agent sessions on a project by keyword through the TMA1 MCP server and reads back a chosen session so answers can quote it directly.
tma1-ai/tma1
Searches earlier agent sessions on this project by keyword and reads back a session's conversation, answering with quotes from what was actually said.
Works with
Categories
Answers questions about agent spend, token use, traces, events, errors and tool usage by running read-only SQL against a local TMA1 observability store. TMA1 collects telemetry from Claude Code, Codex, GitHub Copilot CLI, OpenClaw and any agent that emits standard OpenTelemetry GenAI traces. The skill first checks that the local TMA1 service answers on its health endpoint on port 14318 and, if it does not, points you to /tma1-setup.
TMA1 Observability Query fits situations like: finding out how much an agent session or model cost; reviewing token usage across Claude Code and Codex; inspecting traces or events to see what an agent has been doing; comparing models or tool usage and checking recent errors.
Run `npx skills add tma1-ai/tma1 --skill tma1 -a claude-code`. Or copy the skill folder (claude-plugin/skills/tma1 in tma1-ai/tma1) into .claude/skills/tma1 in your project. Claude Code loads it when a task matches its description.
Run `npx skills add tma1-ai/tma1 --skill tma1 -a codex`. Or copy the skill folder (claude-plugin/skills/tma1 in tma1-ai/tma1) into .agents/skills/tma1 in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add tma1-ai/tma1 --skill tma1 -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/tma1, .gemini/skills/tma1, .github/skills/tma1 and .opencode/skills/tma1 in your project.
Going by SKILL.md and its folder, TMA1 Observability Query needs the command-line tools its instructions call (curl). Our summary lists: A running local TMA1 instance; TMA1's MCP server or its local HTTP query API; curl for the health check. Its frontmatter pre-approves these tools: mcp__tma1__exec_query, Bash.
SKILL.md contains no URLs. Its commands use curl, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
TMA1 Observability Query is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.1k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with TMA1 Observability Query: Mz Query Tracing (MaterializeInc/materialize, 6.4k stars), Sentry Elixir SDK (getsentry/sentry-for-ai, 268 stars), Agentsop Observability Setup (agentsope/SkillAlchemy, 436 stars) and Tidewave Integration (oliver-kriska/claude-elixir-phoenix, 565 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
tma1-ai (a GitHub organization) maintains it in tma1-ai/tma1, which has 119 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on October 8, 2026.
Source: tma1-ai/tma1 on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.