codetools / MCP server
codetools MCP server
codetools mcp serve is a stdio MCP server. Claude launches it once per session, and it exposes the codetools verbs as tools. It follows ADR-0038 (Accepted, 2026-10-13): "codetools and orchestrata expose MCP tool servers generated from their verb registries - every activity verb is a callable tool with the CLI's approval gate - registered with Claude through the deployed configuration, not by hand".
The facts below come from:
codetools mcp liston 2026-10-23;- the module docstrings and code of
codetools.mcpata5c419f5, read through the AST toolkit; ~/.claude.json;deploy/install-claude-config.shinwt-MAP-PRECOMMIT.
How a tool call runs
- The tool table is generated. At start,
codetools.verbs.collect_verbs()walks the CLI parser, and every leaf verb becomes a tool. "Nothing is hand-listed, so a verb an activity registers later appears here with no change" (mcp/server.py). The input schema is generated from the verb's argparse parameters, never written by hand (REQ-007-005,ToolSpec.from_verb). - A call is a CLI run. "A call runs the same function the CLI runs:
python -m codetools <verb argv>in this release's interpreter, same environment, so the CLI's own audit/ledger rows are written exactly as for a shell invocation" (mcp/server.py). - Writers need a token. A verb that mutates, or a call that sets a writing parameter, takes an extra
approval_tokenargument. The token comes from theapprovetool and is valid for one call of that one tool, for 300 seconds (DEFAULT_TTL_SECONDS). Without a valid token, the call is refused withPERMISSION_DENIED"before any process is started, so the tree is unchanged". - Tokens are secrets. "Tokens are never logged, printed or written to the ledger."
- Every outcome is recorded. Each approval, success and failure appends a row to an ndjson ledger: id, tool, outcome, exit code and timestamp. A refusal carries its ledger row id back to the caller (REQ-007-006).
The tools (codetools mcp list, 2026-10-23)
| Tool | Verb | Approval | Help |
|---|---|---|---|
ast_map |
ast map |
required | write CODEBASE_MAP.md (and the full skeleton) with a delta cache |
ast_query |
ast query |
none | run the structural Python query DSL |
ast_snapshot |
ast snapshot |
none | read source snapshot statistics or JSON |
deploy_baseline_record |
deploy baseline record |
required | append a verified audit entry from an existing verify record |
deploy_install |
deploy install |
required | deploy a pinned git ref into a prefix |
deploy_promote |
deploy promote |
required | promote a built artifact to current |
deploy_render_mcp |
deploy render-mcp |
required | render the release's .mcp.json fragment under the prefix |
deploy_rollback |
deploy rollback |
required | re-point current at the previous release |
deploy_select_affected |
deploy select-affected |
none | bound a changed-file set to a measured selection from a facts document on stdin (RUL-040 fail-closed JSON contract) |
deploy_verify_identity |
deploy verify-identity |
none | prove the running units are the deployed release |
gate_cpuset |
gate cpuset |
required | the container cpuset from capacity |
gate_provision |
gate provision |
required | converge the container |
gate_readiness |
gate readiness |
none | run the gate's readiness probes |
gate_run |
gate run |
required | run one batch of master |
gate_status |
gate status |
none | print the last batch |
graph_blast |
graph blast |
none | compute affected symbols or tests |
graph_extract |
graph extract |
none | extract graph nodes and edges |
realize |
realize |
required | generate (and optionally implement and verify) an OpenSpec change |
rename_module |
rename module |
required | Move one module by dotted path. |
rename_plan |
rename plan |
required | Execute a declarative rename plan. |
rename_report |
rename report |
none | Dry-run a plan and print its residue report only. |
rename_symbol |
rename symbol |
required | Rename one type-resolved symbol. |
render |
render |
on:out | render a Mermaid classDiagram to Python skeleton(s) |
approve |
- | none | issue a single-use token |
That is 23 verb tools plus approve. mcp serve and mcp list are not exposed as tools. Every option of every verb is described on codetools verbs.
In a Claude session the tools appear as mcp__codetools__<tool>. The deferred-tool list of this session also showed mcp__codetools__ast_query, ast_snapshot, graph_extract, graph_blast, the deploy tools and others, but no ast_map, deploy_select_affected or any rename_* tool. The session's server runs an older release that does not have those verbs (see below).
Registration
codetools deploy render-mcp --prefix /opt/codetoolswrites the fragment/opt/codetools/mcp/codetools.mcp.json. On 2026-10-23 it holdscommand: /opt/codetools/bin/codetoolsandargs: ["mcp", "serve"].deploy/install-claude-config.sh(mcp_register_servers) renders that fragment when a codetools release exists. It reads thecodetoolsentry and registers it at user scope under the namecodetools, next toorchestrata. It fails when the fragment is missing.mcp_verify_registrationwrites~/.claude/mcp-registered.jsonfrom whatclaude mcp getreports. It then runscodetools deploy verify-identity --prefix /opt/codetools --expect-commit <VERSION commit> --mcp-json ~/.claude/mcp-registered.json, so a registered command that resolves outside the deployed release fails the config deploy.~/.claude.jsonon 2026-10-23:mcpServers.codetools={"command": "/opt/codetools/bin/codetools", "args": ["mcp", "serve"]}.
/opt/codetools/bin is a symlink to current/.venv/bin/. The registered command therefore follows each promote, but only for servers started after it.
The call ledger
- Path:
$CODETOOLS_MCP_LEDGERif set, otherwise~/.local/state/codetools/mcp-ledger.ndjson(mcp.ledger.ledger_path). - On 2026-10-23 the ledger held 7 rows, all written on 2026-10-14 between 19:42 and 20:27 EDT. All seven were
okcalls ofast_snapshot,ast_query,graph_extractandgraph_blast. No call has been recorded since.
Measured problem: sessions keep the old release
A stdio server keeps the interpreter and code it started with. On 2026-10-23 at 01:56, three codetools mcp serve processes were running:
| PID | Started | Runs from |
|---|---|---|
| 11029 | 2026-10-21 22:14 | /opt/codetools/releases/6b4babfeaac2e008/.venv/bin/python3 |
| 11903 | 2026-10-21 22:14 | same |
| 1279789 | 2026-10-22 15:26 | same |
Release 6b4babfeaac2e008 is commit 55221698, promoted on 2026-10-14 at 23:00 UTC. /opt/codetools/audit.log shows three promotes after it:
a37fbde65c97813aat 2026-10-23 01:35 UTC (commite1ffbc36);6ab0e3036ce3f300at 01:54 UTC (commitb518f88a);58dc55ce7d9ba893at 04:01 UTC (commita5c419f5), the current release.
The three servers do not offer the verbs listed above. They pick up the new release only when their Claude session restarts. Options are on codetools blockers.
Related
- codetools verbs
- codetools deploy
- codetools plugin
- codetools architecture
- codetools decisions
- Host services: the MCP servers Claude launches
- codetools reference