cc-codemod
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Claude Code codemode
Code mode for Claude Code, after Cloudflare's Code Mode. The plugin is called cc-codemod. It adds one tool, run, that takes a JavaScript function body. Inside it, every tool Claude has is an async function: tools.Bash, tools.Read, tools.Edit, and every MCP tool. The script fans out calls, filters what comes back, and returns only the answer. The raw output of the nested calls never enters the context.
const files = ['api.ts', 'db.ts', 'auth.ts']
const reads = await Promise.all(files.map(f => tools.Read({ file_path: `/repo/src/${f}` })))
return files.map((f, i) => `${f}: ${reads[i].text.split('\n').filter(l => l.includes('TODO')).length} TODOs`)The script runs on opencode's code mode interpreter, a pure TypeScript interpreter for a JavaScript subset. There's no eval, no WASM and no Node, so it works inside Claude Code's mod runtime. Nested calls go through $.tool.call, which means the usual hooks and permission prompts apply to each one. A Read inside a script counts as a read for Edit's read-before-edit check.
Why
Every turn re-reads the whole context, and every output Claude reads stays in it until the session ends. In two weeks of my own Claude Code sessions (2,051 tool calls), two patterns cost the most:
177 turns went to chains of single reads, where each read depended on the last:
rg, thensed -naround the hit, thencatthe file it named. Each of those turns re-read about 95K tokens of context.152 outputs over 10K characters went straight into context, about 750K tokens. 48 of them were
t3_thread_read, mostly from subagents.
A script runs the whole chain in one turn and returns only the slice that answers the question. Forcing everything through it is worse, though. That cost 3.45M tokens on a research task that took 2.62M with direct tools (see When it helps). So the plugin aims Claude at those two cases and leaves the rest to direct calls.
Related MCP server: code2mcp
What else it does
Early starts. Reads with literal arguments start before the script reaches them, so
awaitafterawaitstill runs in parallel.A notebook. Top-level values persist for the session, so a later run can filter the same data again without fetching it twice.
Your permissions. Nested calls go through Claude Code's own hooks and permission checks, one call at a time.
Images. Screenshots and other images from nested calls, such as
html_preview, reach the model.Real errors. A script that throws marks the call as an error.
A guard for T3 Code.
html_renderis refused inside scripts, because T3 Code shows a page only beside a direct call.Hints. After a direct call where a script would have cost less, the next turn gets a one-line hint.
Install
At the Claude Code prompt:
/plugin install cc-codemod --marketplace darjss/claude-code-codemodeAnswer y to add the marketplace and pick a scope. From a shell, claude plugin marketplace add darjss/claude-code-codemode then claude plugin install cc-codemod@cc-codemod does the same. The tool shows up as mcp__cc-codemod__run.
What a call returns
The script's return value, as a string or indented JSON.
console.loglines, in a<console>block.Interpreter warnings, and a note if output hit the 100 KB cap.
Images the nested calls returned, such as
html_previewscreenshots, as image blocks after the text, up to 20 per run. Inside the script,resultshows each one as{ type: "image", attached: true }, so a saved value doesn't carry the base64.A footer like
[4 tool calls · 1 failed · 3 started early · saved pkg, log]. If the script returned a large nested output unchanged, the footer says so and asks the model to slice it next time.
Each nested call resolves to { text, isError, result }. text is what the direct call would have shown, and result is the tool's structured output. Where Claude Code would cut a big output to a notice naming the file it saved it to, text is that file's whole content instead, up to 4 MiB, so the script can slice it. A failed or denied call resolves with isError: true and does not throw, and the footer counts it. A script that throws, a syntax error or an abort marks the whole run as an error.
A script that calls html_render is refused before it runs. T3 Code shows a rendered page only beside a direct call, so from a script the call succeeds and the page never shows.
Limits are 200 nested calls and 100 KB of output per run. The language has no classes, imports, timers or fetch.
Calls that start early
Models write await after await even when the calls don't depend on each other. Before a run, the mod parses the script with acorn and lists the tool calls of its top-level statements whose arguments are plain literals. It asks core about each one with $.tool.check, and every call that core holds read-only and doesn't deny starts at once. When the script reaches the call, it gets the result that's already running.
const pkg = await tools.Read({ file_path: '/repo/package.json' }) // starts at once
const log = await tools.Bash({ command: 'git log --oneline -5' }) // starts at once
await tools.Edit({ file_path: '/repo/a.ts', old_string: 'x', new_string: 'y' }) // a write: the plan ends here
const after = await tools.Bash({ command: 'git diff --stat' }) // runs in its place, after the EditThe plan ends at the first call that isn't a read, at any command that waits (sleep, until, while, wait, watch, timeout), and at any call of a function the script defined. A call inside a branch, loop or callback never starts early. Core answers ask for most Bash reads. Auto mode's classifier settles those, and in other modes the prompt shows up a little sooner than it otherwise would. The idea comes from OC++'s speculation.
Core judges a shell line as a whole, and it won't vouch for a few kinds of reads. When it declines, the mod splits the line on &&, ||, ; and | with shell-quote. The line still counts as a read if every simple command in it is one of these:
a command core holds read-only on its own.
$HOMEis filled in first, since core rejects variablesgh pr|run|issue|repo view/list/checks/diff/status,gh search, orgh apiwithout-X,-f,-For--inputnpm view|info|ls|outdated,pnpm ls|why,bun pm lsgit in another directory (
git -C X,cd X && git ...) when X is under~/devor~/.t3/worktrees. Core refuses git elsewhere because a repo's own config can run commands. Repos under those roots are ones you cloned or made, so they're treated like the working directory, and a clone anywhere else still stops the plan.
Any other variable, a substitution, a redirect other than 2>&1 or 2>/dev/null, or a subshell stops the plan.
Values that carry over
When a run succeeds, its top-level const and let values that are JSON go into the session's $.state. A later run that names one gets it back, so the model can fetch once and filter again without re-reading. A value over 100 KB isn't saved, and the notebook keeps the newest values up to 1 MB. A run that fails saves nothing. This is a small version of OC++'s durable notebook: plain data only, and it's gone when the session ends.
When it helps
The tool is optional. I tried forcing every Bash and Read through it, and it made things worse. It pays off in two places, and the description names both. The first is a chain where one call's output decides the next, such as rg and then reading around the hits. Each step there is a turn that re-reads the whole context. The second is a big output when you need only a slice of it. That output otherwise stays in context for the rest of the session. Plain independent reads are already covered by Claude's own parallel calls.
In two weeks of my sessions, codemode got 4% of tool calls. It missed 177 chained read turns and 152 outputs over 10K characters, 48 of them t3_thread_read. So the mod now adds a one-line hint after a direct call where a script would have cost less. The hint fires after an output over 10K characters or one Claude Code saved to a file, and on the third turn in a row that made a single read. The big-output hint fires once per agent loop. The chain hint fires on each new streak, up to 3 times per loop, because a 400-call session that heard it once kept chaining rg and sed -n for 19 turns afterwards. After a Bash streak it points at the description's rg then sed -n recipe. A subagent gets its own count. Calls made from a script, Agent, Skill and ToolSearch never get one.
Run | Tokens | Tool calls |
Research task, direct tools | 2.62M | 21 |
Research task, all calls forced through | 3.45M | 33 |
Small coding task, direct tools (2 runs) | 0.69M, 0.66M | |
Small coding task, | 0.80M, 0.89M |
Both setups wrote the same code for the coding task. On a single-file edit, the script adds overhead and saves nothing. The win shows up on fan-out reads across many files or repos.
Develop
The toolchain is Vite+. vp installs packages, formats with Oxfmt, lints with Oxlint and bundles with tsdown. tsrs typechecks.
vp install
vp run build # vp pack: bundles build/runtime.ts into hooks/runtime.js and its chunks
vp run check # vp check (format, lint), tsrs typecheck, claude plugin validatehooks/register.ts is the mod. build/runtime.ts wraps CodeMode.execute and gets bundled, because the mod loader can't resolve npm packages. The pack block in vite.config.ts escapes one import( in acorn's error strings, since the loader refuses any module text containing it. It also minifies the bundle, splits it into effect.js, acorn.js and vendor.js so each file stays under the plugin directory's 256 KiB limit, and writes every non-ASCII character as an escape. The types under .claude-plugin/types/ are written by Claude Code and aren't committed.
What the mod accesses
The mod is hooks/register.ts. The other files under hooks/ are the bundled interpreter.
Its hooks:
session.startregisters the toolmcp__cc-codemod__run.tool.callonmcp__cc-codemod__runis that tool's own server, so it answers the call itself instead of callingnext. It stands in for no other tool. If the script nameshtml_render, it refuses the call before anything runs.tool.callon every other tool passes the call tonextunchanged and returns whatnextgives back. It may append one line ofcontextsuggesting a script. That happens once per agent loop after an output over 10,000 characters, and on the third turn in a row with a single read-only call, up to 3 times per loop. It never changes, blocks or rewrites a call. Calls the mod makes itself pass straight through.tool.checkpasses every check through unchanged. For checks the mod asks itself, it records whether core held the input read-only.
Tools it runs: only the ones the model's script calls, through $.tool.call. Each nested call goes through Claude Code's hooks and permission prompts like a direct call. A script can call any tool the session has, including Bash, Agent and MCP tools, and every command or input comes from that script. The mod runs no fixed commands of its own. Before a run, it asks $.tool.check about each top-level call with literal arguments, which runs nothing. It starts a call early only if core holds it read-only and doesn't deny it. That covers Read, Grep, Glob, WebFetch and WebSearch, plus Bash lines that core or the rules in Calls that start early hold read-only.
What it sends, and where:
The mod makes no network calls and sends nothing off the machine.
Nested tools send what they always send when the script calls them. WebFetch fetches its URL, and an MCP tool reaches its server.
Top-level JSON values a script declares are kept in the session's
$.state: up to 100 KB per value and 1 MB in all, gone when the session ends.While a run is going, the status line shows its progress.
What it reads from the machine:
HOME, through$.env.get. It uses it to expand~and$HOMEin the Bash lines it checks for an early start, and to recognize repos under~/devand~/.t3/worktrees. The value is never sent anywhere or shown to the model.Files Claude Code saved for a nested call whose output was too big to show, through
$.fs.read. It reads only the path core's own notice names, underHOMEand in atool-resultsfolder, and hands the text to the script that made the call. Only what the script returns reaches the model.
Reflective code: register.ts uses none. The bundled interpreter does. Effect, @opencode/codemode and acorn use getters, Proxy and then methods for their own APIs, such as Effect's pipeable and promise interop. The scripts the model writes run inside that interpreter, with no eval and no access to Node.
plugin.json names types/index.d.ts, the contract for the mod's $.state value that claude plugin validate checks. Claude Code ignores the field at load time. package.json and bun.lock are only for building hooks/*.js. The plugin starts no MCP server and runs no npx.
Other code mode plugins for Claude Code
juhas96/claude-codemode also routes nested calls through Claude Code's permission system.
jliocsar/claude-codemode runs TypeScript in a Node VM and replaces Read, Write, Edit and Bash with its tool.
ejklock/claude-code-mode bridges to pi's code mode in a Node child process.
brianjmeier/claude-codemode is a mod with a codemode skill and an agent-spawning helper.
codelitt/codemode-x wraps APIs and databases into a typed SDK behind two MCP tools.
ryx2/claude-codemode adds code mode to the Claude Agent SDK in Python.
Thanks
Kenton Varda and Sunil Pai at Cloudflare came up with code mode in Code Mode: the better way to use MCP, September 2025. They turned MCP tools into a TypeScript API and had the model write code against it in a sandbox. This mod is that idea pointed at Claude Code's own tools.
opencode wrote
@opencode/codemode, the interpreter this mod runs on. It made code mode possible here, since Claude Code's mod runtime has no eval and no WASM. Its split between what a script returns and what the model sees shapedrun's output.pi by Mario Zechner showed code mode working over a coding agent's built-in tools (bash, read, edit), not only MCP servers. That's the part cc-codemod copies.
License
MIT. The bundled runtime includes MIT-licensed code from opencode, Effect and acorn. See THIRD_PARTY_NOTICES.md.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Read and write NetSuite and run SuiteQL from Claude or ChatGPT. Write tools flagged for approval.
Code mode (preview): the AI writes a script against a typed foundry.* API and runs it in a sandbox.
Live SEO workflow tools for Claude Code, Codex, and AI agents.
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables AI models to dynamically create and execute their own custom tools through a meta-function architecture, supporting JavaScript, Python, and Shell runtimes with sandboxed security and human approval flows.510MIT
- FlicenseAqualityNot gradedmaintenanceEnables execution of TypeScript code to call MCP tools instead of direct tool calls, reducing token usage by up to 98% while orchestrating complex multi-tool workflows through secure sandboxed code execution.1-
- AlicenseAqualityCmaintenanceEnables Claude Code to perform programmatic tool calling by executing Python scripts that interact with multiple MCP servers in a single round-trip. This reduces latency and token consumption by keeping intermediate tool results within the local Python runtime instead of the conversation context.1MIT
- AlicenseAqualityBmaintenanceEnables Claude Code to delegate tasks to OpenCode subagents asynchronously, with tools for starting tasks, polling status, and fetching results.7309 npm6MIT