Tool Coverage¶
See application_model.md for the system overview, and implementing-a-tool-renderer.md for the how-to when closing a gap listed here.
Which tools get a specialized renderer, and which fall back to the generic one. The first census covers Claude Code tools and the second covers Codex item/tool adaptation.
The Claude Code table is checked against the upstream Tools reference (42 documented tools, snapshot 2026-07-15).
Three levels of support¶
Coverage is not binary — a tool can be typed on one side and generic on the other:
- Typed input — an entry in
TOOL_INPUT_MODELS(factories/tool_factory.py) parsestool_use.inputinto a Pydantic model, which_dispatch_formatroutes toformat_<Model>/title_<Model>methods inhtml/renderer.pyandmarkdown/renderer.py. - Typed output — an entry in
TOOL_OUTPUT_PARSERSturns the rawtool_result(and, for members ofPARSERS_WITH_TOOL_USE_RESULT, the structuredtoolUseResult) into a dataclass with its own formatters. - Generic fallback — no registry entry. Input renders as a
<table class="params">key/value dump (format_ToolUseContent), output as a raw<pre>block (format_ToolResultContent), and the header shows the bare tool name, HTML-escaped (title_ToolUseMessage, see #245).
The fallback is a genuine feature, not a hole: it means an unknown tool —
a brand-new built-in, an mcp__* tool, a plugin's tool — always renders
something faithful. Typing a tool buys a compact title, structured
formatting, and correct Markdown/JSON export; it is worth doing for tools
that appear often or carry structure worth surfacing.
JSON export needs no per-tool work. json/renderer.py serialises
whatever the factory produced via dataclasses.asdict, so a tool's JSON
representation improves for free the moment it gets typed models.
Documented tools¶
Legend: Full = typed input + typed output · Input only = typed input, generic output · Generic = no registry entry, fallback rendering.
| Tool | Support | Notes |
|---|---|---|
Agent |
Full | Aliased to TaskInput / parse_task_output; see agents.md |
Artifact |
Full | #257 |
AskUserQuestion |
Full | Input card elides when the paired answer re-renders the questions |
Bash |
Full | minted_background_task_id hoisted from output (#158) |
CronCreate |
Full | |
CronDelete |
Full | |
CronList |
Full | |
Edit |
Full | |
EnterPlanMode |
Generic | |
EnterWorktree |
Generic | |
ExitPlanMode |
Full | |
ExitWorktree |
Generic | |
Glob |
Input only | GlobOutput model exists but no parser registers it — see "Dead models" below |
Grep |
Input only | |
ListMcpResourcesTool |
Generic | |
LSP |
Generic | |
Monitor |
Full | |
NotebookEdit |
Generic | |
PowerShell |
Generic | Windows sessions only; shape is close to Bash |
PushNotification |
Generic | |
Read |
Full | |
ReadMcpResourceTool |
Generic | |
RemoteTrigger |
Generic | |
ReportFindings |
Generic | Structured findings list — a good typing candidate |
ScheduleWakeup |
Full | |
SendMessage |
Full | See teammates.md |
SendUserFile |
Generic | |
ShareOnboardingGuide |
Generic | |
Skill |
Input only | Body folded in from the paired isMeta entry (#93) |
TaskCreate |
Full | |
TaskGet |
Generic | Only TaskGet of the Task* family is untyped |
TaskList |
Full | |
TaskOutput |
Full | Back-links to the spawning call (#90, #154) |
TaskStop |
Full | #158 follow-up |
TaskUpdate |
Full | |
TodoWrite |
Input only | Disabled by default upstream since v2.1.142, but ubiquitous in older transcripts |
ToolSearch |
Generic | |
WaitForMcpServers |
Generic | |
WebFetch |
Full | |
WebSearch |
Full | |
Workflow |
Input only | Input script is JS; #174 |
Write |
Full |
Totals: 21 full · 5 input only · 16 generic.
Tools we support that upstream does not document¶
A transcript viewer reads history, and provider adapters also need internal canonical tools. These entries are historical, undocumented, or deliberately internal, but all are load-bearing for supported transcripts; removing one silently degrades rendering.
| Tool | Upstream status | Why we keep it |
|---|---|---|
Delete |
Internal canonical tool; never in the reference table | Codex apply_patch deletions receive a dedicated typed pair instead of masquerading as edits |
Task |
Renamed to Agent |
Every subagent spawn in pre-rename transcripts |
MultiEdit |
Removed; superseded by Edit |
Common in older transcripts; has input model + title, no output parser |
ToolExecution |
Internal canonical tool; never in the reference table | Faithful typed fallback for Codex JavaScript that the bounded analyzer cannot reconstruct safely |
ask_user_question |
Legacy snake_case name of AskUserQuestion |
Aliased to the same input model |
TeamCreate |
Never in the reference table | Agent-teams feature; fully typed both sides |
TeamDelete |
Never in the reference table | Ditto |
TeamCreate / TeamDelete are the inverse case: emitted by the
agent-teams feature but absent from the public table. Undocumented is
not the same as obsolete — the reference table is not a complete census
of what lands in a JSONL file.
Dead models¶
GlobOutput and GrepOutput are declared in models.py and
GlobOutput even has a format_GlobOutput in markdown/renderer.py,
but neither is registered in TOOL_OUTPUT_PARSERS, so nothing ever
constructs them — ToolOutput carries an explicit
# TODO: Add as parsers are implemented for both. Either wire up the
parsers or drop the models; right now they read as coverage that isn't
there.
MCP and plugin tools¶
mcp__<server>__<tool> names never appear in the reference table and are
unbounded by definition, so the default is faithful generic rendering. Plugins
can register their own formatters, and providers may canonicalize a small,
evidence-backed family before plugin dispatch. The Codex provider does this for
OpenAI Developer Docs search/fetch calls; all other MCP names remain open-ended.
See plugins.md.
Keeping this page current¶
The upstream reference changes with most Claude Code releases. When
revisiting: re-fetch the reference, diff its tool names against
TOOL_INPUT_MODELS / TOOL_OUTPUT_PARSERS keys, and move rows between
the two tables above rather than deleting them — a tool leaving the
reference means it moves to "no longer documented", never that support
gets removed.
Codex provider coverage¶
Codex needs a separate census because its persisted rollout format is an
implementation detail, not a closed public list of function names. The public
app-server ThreadItem
union is the semantic reference; concrete function, MCP, app, plugin, and
collaboration tool names remain open-ended.
This snapshot was checked on 2026-07-19 against the official Codex manual and
the JSON schema generated by codex-cli 0.144.3. The generated schema contains
18 ThreadItem variants. Coverage below describes the rollout provider, not a
claim that rollout records use the app-server wire shape directly.
Public item families¶
Legend:
- Direct — the provider normalizes the observed rollout family itself.
- Adapted — an observed rollout call is converted to an existing shared semantic renderer.
- Partial — useful observed shapes are covered, but the public item family is not decoded comprehensively.
- Missing — no semantic normalization exists yet; an unknown persisted record is ignored rather than guessed.
| App-server item | Coverage | Rollout-provider behavior |
|---|---|---|
userMessage |
Direct | Visible event/response copies are locally deduplicated; text, steering, environment context, shell commands, and inline image references are normalized. |
agentMessage |
Direct | Visible assistant text is normalized and ordered with other entries. |
plan |
Partial | Observed update_plan calls become TodoWrite; a native plan item shape is not decoded. |
reasoning |
Direct | Readable summaries render as thinking; encrypted reasoning is deliberately never inspected or emitted. |
commandExecution |
Adapted | exec_command becomes Bash; terminal wait/write_stdin polling and ordered or parallel marker sessions are coalesced when correlation is complete. |
fileChange |
Partial | Static apply_patch Add/Delete operations become Write/Delete; adjacent Update runs become Edit/MultiEdit, preserving patch order. Moves, dynamic patches, and ambiguous programs remain ToolExecution. |
mcpToolCall |
Adapted | Exact MCP names and forwarded result envelopes are preserved, allowing plugins such as ClMail to apply the same transformation as for Claude Code. Codex OpenAI Developer Docs searches and fetches receive built-in renderers. |
dynamicToolCall |
Partial | Direct open-ended calls render generically; statically analyzable exec wrappers expand, while opaque JavaScript remains ToolExecution. |
collabAgentToolCall |
Partial | Observed spawn/message/list function calls reuse Task, SendMessage, and TaskList; the native public item shape is not decoded directly. |
webSearch |
Adapted | Search-only web__run calls become WebSearch; exact open-only batches and find-only calls become WebFetch. Codex citation/source serialization is normalized before Markdown rendering. Mixed actions remain generic. |
imageView |
Partial | User-message image wrappers can inline readable local files. A native image-view item/tool result has no specialized adapter. |
imageGeneration |
Missing | No observed native rollout mapping is normalized. A function/MCP tool with this behavior remains generic unless a plugin transforms it. |
sleep |
Partial | Static Promise/setTimeout wrappers become synthetic wait pairs; a native sleep item shape is not decoded. |
subAgentActivity |
Partial | Thread lineage and spawn calls are retained, but load_session() does not yet splice descendant activity into the parent transcript. |
hookPrompt |
Missing | No observed rollout mapping is normalized. |
enteredReviewMode |
Missing | No observed rollout mapping is normalized. |
exitedReviewMode |
Missing | No observed rollout mapping is normalized. |
contextCompaction |
Missing | Exact persisted compaction shape and interrupted/delta recovery remain evidence gaps. |
Totals: 3 direct · 3 adapted · 7 partial · 5 missing.
Concrete Codex call adapters¶
These mappings run before the shared tool factory, so a mapped name receives the same typed models and HTML/Markdown/JSON rendering as a Claude Code tool. Result transport is normalized in the provider before reaching shared factories.
| Codex call | Canonical rendering | Coverage and fallback |
|---|---|---|
exec_command |
Bash |
Typed input/output; approval justification becomes the description. Completed async polling chains fold into the originating Bash pair. |
apply_patch |
Write / Delete / Edit / MultiEdit |
Lossless Adds and Deletes become individual Write and Delete pairs; adjacent static Updates reuse the edit renderers. One aggregate result is correlated to every derived pair. Otherwise ToolExecution. |
update_plan |
TodoWrite |
Typed input; a successful empty transport becomes Todo list updated. |
spawn_agent |
Task |
Typed input/output; opaque transport payloads are redacted on both specialized and ToolExecution paths. |
send_message, followup_task |
SendMessage |
Typed input/output with target and follow-up semantics retained. |
list_agents |
TaskList |
Typed input/output when agent rows are valid; malformed output stays generic. |
search-only web__run |
WebSearch |
Typed input/output; a multi-query call keeps the first query in a compact title and lists every query in the body. Named turn…search/view… refs become anchors; numeric citation wrappers, word limits, and packed source-line markers are normalized into readable Markdown. |
open-only web__run batch |
WebFetch pairs |
Expanded only when refs and result chunks split exactly; output uses the shared Codex web-result normalizer. |
find-only web__run |
WebFetch |
Static refs and patterns become typed input; a reusable turn… ref links back to the card that introduced it, and output uses the shared Codex web-result normalizer. |
mcp__openaiDeveloperDocs__fetch_openai_doc |
CodexDoc |
Codex-only built-in plugin: URL/anchor parameters stay visible and the returned documentation renders as collapsible Markdown. Aggregated static result objects are projected back into one pair per fetch. |
mcp__openaiDeveloperDocs__search_openai_docs |
CodexDocSearch |
Codex-only built-in plugin: query/limit parameters stay visible; every surviving hit renders its linked hierarchy and Markdown content. Complete hit objects are recovered from otherwise-invalid truncated JSON. Aliased properties in aggregated static result objects are projected back into individual searches; properties removed by Codex output truncation remain explicit omitted results. |
mcp__*, app, and plugin tools |
Original name | Generic built-in rendering, followed by optional plugin transformation. The namespace is intentionally not closed. |
| static Promise delay | wait |
Synthetic generic pair with delay_ms and an explicit completed result. |
wait, write_stdin command polling |
Originating Bash |
Folded only with a matching live handle and terminal result; otherwise preserved as generic calls. |
| unknown direct function | Original name | Faithful generic params/result rendering. |
unsupported exec JavaScript |
ToolExecution |
Original script remains visible without claiming native Workflow semantics. Results retain completion and wall-time status, followed by labelled generic result sections; opaque payloads are scrubbed. There is no legacy recognizer fallback. |
exec JavaScript analysis¶
Codex often persists tool orchestration as JavaScript inside a custom exec
call. providers/codex_quickjs.py executes it in a sandboxed QuickJS
engine (quickjs-ng) with instrumented tools / text() stand-ins that
record what the snippet actually did; the recording is mapped back to a tool
batch. Because arguments are captured after the snippet's own JS evaluated
them, expression-built values (string concat, templates, .join(),
reduce, ternaries, computed numerics, conditional branches) resolve for free
— the fragile static-expansion whitelist of the former Tree-sitter analyzer is
gone.
Safety model: no host callables are registered (attack surface = the
QuickJS interpreter only). Per-snippet bounds — memory, wall-time, stack, a
pending-job cap, a 64 KB source cap, a 128 expanded-call cap, and a per-string
materialization cap — bound hostile inputs (infinite loops, allocation bombs,
deep recursion, string amplification). Provenance rides on an in-band
private-use sentinel (U+E000). Any failure — syntax error, engine exception,
a cap hit, a run that throws or never resolves, or a shape the mapper cannot
correlate — fails closed to None, so the raw-script ToolExecution fallback
stays visible.
The report → batch mapper recognizes:
- direct awaited calls, sequential batches, and heterogeneous
Promise.all()batches with identifier or array destructuring; - result provenance through direct references, property paths,
JSON.stringify(), result-derived templates, and object emissions — both a whole-result bundle (JSON.stringify({first, second}), keyed per field) and a single-call projection ({name, ...r}spread, or explicit{name, output: r.output}), collapsed to the canonicaloutputfield; for...ofexpansion over static or computed arrays, including destructured rows and loop-local calls/emissions, andPromise.all(array.map(async ...));- ordered, reversed, and marker-delimited result correlation;
setTimeout/ Promise delays represented aswaitcalls;- outer
execcell continuations coalesced before call expansion, including informational MCP completion events and collaboration bookkeeping inside the polling interval; - command-session continuation through
waitandwrite_stdin, including ordered and parallel marker sessions; - consolidated output splitting on unique materialized template prefixes and projected JSON objects after a Codex truncation preamble; missing sections are identified only when Codex explicitly reports truncation.
Inter-call data dependencies (an unresolved result flowing into a later call's
argument), forged sentinels, ambiguous emissions, repeated/absent output
separators, ALL_TOOLS-registry pipelines (no static membership yet), and
cap/engine failures remain ToolExecution. False negatives are an acceptable
compatibility cost; false reconstruction is not.
Token accounting¶
Codex records token usage as cumulative token_count events
(payload.info.total_token_usage), one emitted after nearly every agent-loop
step — unlike Claude, which carries per-assistant-message usage. The provider
surfaces these totals in two places: the project-card token summary on the
wholesale index, and the per-session token fields on the session cache
(total_input_tokens etc., which sessions has carried since the initial
schema — so this is Claude-schema parity, not Codex-only state). Per-session
token rows on the index are deliberately NOT shown — the Claude index does
not render them either (the project-summary session dicts carry no
token_summary key, and the drift pin
test_index_summary_dict_shape_matches_claude_path locks the two shapes
together), so adding them is a Claude-path UX change deferred to its own
follow-up (see work/codex-backlog.md). The extraction is:
providers/base.ProviderTokenTotals— the token figures a session-level provider surfaces: billableinput,cache_read,output, and the authoritativetotal.cache_creationis deliberately absent (Codex has no such concept; an omitted column ≠ a zero one), so of the index's four token columns the project card shows three and never a "Cache Creation" one.codex._token_totals_from_records— takes the last cumulative record as the session total (the values are cumulative and monotonic, so the final one subsumes every prior turn; compaction lowers the live context window but does not reset the counter). ReturnsNone— totals omitted, not zeroed — for pre-accounting rollouts with notoken_count.codex._map_cumulative_usage— mapsinput = input_tokens − cached,cache_read = cached_input_tokens,output = output_tokens(already includes reasoning). The subtraction keeps the cached tokens in exactly one column; folding them back intoinputwould double-count.total_tokensis carried through authoritatively (never recomputed — a degenerate record with zero components but a non-zero total keeps its stored total).converter.render_provider_wholesalesums each session's cumulative total into the index project-card totals (_sum_provider_token_totals) and writes the per-session totals onto the session cache, bypassing the per-messageusageaccumulators (compute_session_data/compute_project_aggregates) that a cumulative figure must never flow through.
Why only session/project granularity — an evidenced design limit, not a
TODO. The argument is structural, not statistical. A token_count delta
(last_token_usage) measures everything consumed since the previous
token_count, and one agent-loop step bundles reasoning + assistant text + a
tool call + its (often large, cached) tool result under a single delta. That
window contains more than one rendered thing, so the delta cannot be
attributed to any single message the transcript renders — no matter which
record the step happens to end on. The corpus distribution only confirms that
steps overwhelmingly end on tool work: measured post-inherited-prefix-strip
(the records that actually render), n=4138 events across 34 sessions, ~75.6% of
token_count events follow a tool-execution step (custom_tool_call_output,
mcp_tool_call_end, function_call_output, patch_apply_end,
web_search_end, sub_agent_activity) and ~22.5% follow an assistant/agent
message — but even that 22.5% is not attributable, because the message shares
its delta with the reasoning before it and the next turn's cached context
re-read. Per-message (and even per-turn) attribution is therefore not
recoverable from this stream, so the session cumulative is the finest honest
unit. Account-level fields in the same payload (rate_limits,
model_context_window) are never read into output.
Refreshing the Codex census¶
The Codex manual states that generated app-server schemas match the installed Codex version. To refresh this section:
Read the definitions.ThreadItem.oneOf variants from
codex_app_server_protocol.v2.schemas.json, compare them with the public-item
table, then reconcile the concrete adapter table against
providers/codex_tools.py, providers/codex.py, and the test/test_codex_*
contracts. Keep rollout observations explicitly separate from the public
app-server wire contract.