codemode-node
Provides tools for interacting with the Cloudflare API (cf_get, cf_post, cf_put, cf_patch, cf_del, cf_request) from inside the sandbox, enabled when CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are set.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@codemode-nodefind every package.json missing a description and summarize"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Code Mode for Node. One MCP tool instead of a hundred.
Cloudflare's @cloudflare/codemode pattern — without a Cloudflare account.
npx -y https://github.com/a-saeedia/codemode-node/tarball/main --statsThe 10-second version
Your agent re-reads every tool schema on every single request. Eight ordinary MCP servers:
tools | schema bytes | tokens / request | |
8 MCP servers | 113 | 174,406 | 43,602 |
codemode-node | 1 | 6,524 | 1,631 |
That's 26.7× less context, paid once per turn, before the model has done anything. It gets worse with every server you install. In one measured setup, github alone was 120 KB.
With Code Mode the model writes one program instead of making one call per step:
const repos = await codemode.fs_glob({ pattern: '*/package.json' });
const stale = [];
for (const f of repos) {
const pkg = JSON.parse(await codemode.fs_read({ path: f }));
if (!pkg.description) stale.push(f);
}
return { total: repos.length, stale };Same job. One round trip. A 145-byte answer instead of 41 KB of raw JSON.
Related MCP server: MCX (Modular Code Execution)
Why this exists
@cloudflare/codemode turns any MCP server into a single code tool. It ships exactly one executor: DynamicWorkerExecutor, which needs a Workers LOADER binding. A pattern that runs fine on a laptop, gated behind a cloud account.
Executor is a public extension point:
interface Executor {
execute(code, providersOrFns, options?): Promise<{ result, error?, logs? }>;
}This package implements it on a child process, and wraps the result in a ready-to-use stdio server. Cloudflare still owns the type generation, the tool description, the unwrapping and the truncation — none of that is reimplemented here.
How it works
The model sees one tool. Everything else lives behind it.
Install
# audit your current setup first — no install, no config
npx -y https://github.com/a-saeedia/codemode-node/tarball/main --stats
# run the server
npx -y https://github.com/a-saeedia/codemode-node/tarball/mainNo publish needed: npm fetches the repo tarball. Once the package is on npm this becomes
npx -y codemode-node.
{
"mcpServers": {
"codemode": { "command": "npx", "args": ["-y", "https://github.com/a-saeedia/codemode-node/tarball/main"] }
}
}{
"mcp": {
"codemode": {
"type": "local",
"command": ["npx", "-y", "https://github.com/a-saeedia/codemode-node/tarball/main"],
"enabled": true
}
}
}Same shape as Claude Desktop, under servers in .vscode/mcp.json or mcpServers in .mcp.json.
Then turn off the servers you no longer want advertised. They stay reachable through codemode.mcp_call({ server, tool, args }) — disabled means "not advertised", not "unavailable".
The audit
--stats reads your real config, boots each server, and measures the schemas you are actually shipping to the model:
server tools bytes tokens
----------------------------------------------------------------------------
· higgsfield 48 182757 45689 ██████████████████████████████
github 44 120572 30143 ████████████████████
director 26 19725 4931 ███
· playwright 24 18502 4626 ███
...
----------------------------------------------------------------------------
TOTAL POOLED 282 441280 110320
· = not advertised (still reachable through codemode.mcp_call)
ADVERTISED: 113 tools, 174,406 bytes ≈ 43,602 tokens
WITH CODEMODE: 1 tool ≈ 1,631 tokens--stats --json for machines.
Sandbox API
Every function takes a single object and returns JSON. Available as one codemode namespace inside the sandbox.
group | functions |
shell / files |
|
network |
|
MCP |
|
Cloudflare |
|
Moli |
|
globals |
|
Return a small summary. Anything you don't return is discarded — that's the entire point. console.log is captured but never sent back, so put progress in the returned value.
Config
Auto-discovers the first of:
--config <path>orCODEMODE_CONFIG./.mcp.json~/.config/opencode/opencode.jsonc(and the Windows path)Claude Desktop config, all three platforms
Understands mcpServers (Claude), mcp (OpenCode), servers (VS Code), including JSONC comments and {env:VAR} interpolation.
npx -y https://github.com/a-saeedia/codemode-node/tarball/main --print-config # what did it find?env | effect |
| base dir for relative fs paths (default: home) |
| sandbox wall-clock limit (default 120000) |
| forward pooled-server stderr |
| enables |
| enables |
Security
Read this before pointing it at anything you don't trust.
Each call runs in a fresh Node child process that exits afterwards. No state carries over.
The sandbox is
node:vmwith norequire,import,process, or filesystem. Its only capabilities are the proxies listed above.node:vmis not a security boundary. A determined escape exists, and the host functions really do things:shellis a shell,fs_writewrites,httpreaches the network.The model can therefore do anything you could do in a terminal. That is the same deal every coding agent makes. If you need a real boundary, run it inside a container.
No secrets are read from disk. Credentials come from the environment only.
Limitations
No streaming. One result, at the end. Long jobs look like a hang.
Plain JavaScript. No TypeScript, no imports.
console.logis dropped by the wrapping package — return your progress.Cold start ~1.5 s, then ~250–500 ms per call (child spawn + IPC).
stdio only on the server side; upstream servers can be stdio or HTTP.
Benchmarks
Same task both ways, 3 runs, median, Windows 11 / Node 22:
round trips | payload to model | tokens | local wall | |
5 discrete tool calls | 5 | 41,812 B | 10,453 | 2,240 ms |
one | 1 | 145 B | 36 | 2,733 ms |
The code path is ~0.5 s slower locally and 288× smaller in what reaches the model. Each avoided round trip is a full model call, so end-to-end it wins by seconds per task.
What this is not
Not a new protocol. It's Cloudflare's package with the missing executor.
Not a sandbox vendor. It's capability isolation, not a jail.
Not for Workers. If you're on Cloudflare, use
DynamicWorkerExecutor— a real isolate, strictly better than this.
Related
@cloudflare/codemode— the pattern, the Workers executor, the type generator.universal-tool-calling-protocol/code-mode— protocol-agnostic Code Mode (MCP + OpenAPI + native tools).
License
MIT © Arman Saeedi
Available Tools
1 toolcodeA
Execute one JavaScript program that achieves the whole goal. This replaces chains of separate tool calls: one call here does the work of N, and intermediate data never enters your context.
Available: type ShellInput = { cmd: string; cwd?: string; timeout_ms?: number; } type ShellOutput = unknown type FsReadInput = { path: string; } type FsReadOutput = unknown type FsWriteInput = { path: string; content: string; } type FsWriteOutput = unknown type FsAppendInput = { path: string; content: string; } type FsAppendOutput = unknown type FsExistsInput = { path: string; } type FsExistsOutput = unknown type FsStatInput = { path: string; } type FsStatOutput = unknown type FsListInput = { dir?: string; } type FsListOutput = unknown type FsGlobInput = { pattern: string; cwd?: string; limit?: number; } type FsGlobOutput = unknown type FsGrepInput = { pattern: string; cwd?: string; include?: string; limit?: number; ignore_case?: boolean; } type FsGrepOutput = unknown type FsRemoveInput = { path: string; } type FsRemoveOutput = unknown type FsMkdirInput = { path: string; } type FsMkdirOutput = unknown type HttpInput = { url: string; method?: string; headers?: { [key: string]: string; }; body?: string; timeout_ms?: number; } type HttpOutput = unknown type HttpJsonInput = { url: string; method?: string; headers?: { [key: string]: string; }; body?: string; timeout_ms?: number; } type HttpJsonOutput = unknown
declare const codemode: { /** * Run a PowerShell (Windows) or POSIX shell command. Returns {code,stdout,stderr,ms}. */ shell: (input: ShellInput) => Promise;
/**
* Read a UTF-8 text file. Relative paths resolve under the sandbox ROOT.
*/
fs_read: (input: FsReadInput) => Promise<FsReadOutput>;
/**
* Write a file (creates parent dirs).
*/
fs_write: (input: FsWriteInput) => Promise<FsWriteOutput>;
/**
* Append to a file.
*/
fs_append: (input: FsAppendInput) => Promise<FsAppendOutput>;
/**
* True if the path exists.
*/
fs_exists: (input: FsExistsInput) => Promise<FsExistsOutput>;
/**
* Size / dir / mtime.
*/
fs_stat: (input: FsStatInput) => Promise<FsStatOutput>;
/**
* List a directory.
*/
fs_list: (input: FsListInput) => Promise<FsListOutput>;
/**
* Glob files under cwd (forward slashes, *, ?). Skips node_modules/.git/dist.
*/
fs_glob: (input: FsGlobInput) => Promise<FsGlobOutput>;
/**
* Regex search under cwd. Returns ["file:line: text"].
*/
fs_grep: (input: FsGrepInput) => Promise<FsGrepOutput>;
/**
* Delete a file or directory tree.
*/
fs_remove: (input: FsRemoveInput) => Promise<FsRemoveOutput>;
/**
* Create a directory tree.
*/
fs_mkdir: (input: FsMkdirInput) => Promise<FsMkdirOutput>;
/**
* HTTP request. Returns {status, ok, headers, text, ms}.
*/
http: (input: HttpInput) => Promise<HttpOutput>;
/**
* HTTP request parsed as JSON. Returns {status, ok, json}.
*/
http_json: (input: HttpJsonInput) => Promise<HttpJsonOutput>;
}
Rules:
Write an async arrow function that returns the final value. Do NOT use TypeScript syntax.
Use forward slashes in paths. A backslash inside a JS string is an escape ("\p" becomes "p") and silently corrupts the path.
Return a small JSON-able summary. Anything you do not return is discarded, which is the point: filter and aggregate inside the code, not in the conversation.
console.log output is captured host-side but is NOT returned — collect progress in an array and return it.
Loop freely: many files, many URLs, many tool calls per execution.
Default timeout 120s (CODEMODE_TIMEOUT_MS). Errors throw; catch them inside the code if you want to continue.
Example: async () => { const r = await codemode.shell({ cmd: "...", cwd: "...", timeout_ms: 0 }); return r; }
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript async arrow function to execute |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: default 120s timeout with the CODEMODE_TIMEOUT_MS override, errors throw rather than return, console.log is captured but not returned, unreturned data is discarded, relative paths resolve under a sandbox ROOT, and glob skips node_modules/.git/dist. The return shapes for each helper (shell → {code,stdout,stderr,ms}, http → {status,ok,headers,text,ms}) are spelled out inline.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The value proposition and execution rules are front-loaded, and the bullet rules each carry a distinct, non-obvious constraint. It is long, and the helper API is presented twice (TypeScript type blocks plus the declare const with JSDoc), which is mild redundancy, though the second listing adds return shapes and path semantics.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description compensates by explaining what is returned versus discarded. Combined with the enumerated helper surface and the timeout/error model, an agent has everything needed to write a correct call on the first attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single 'code' parameter, so the baseline is a 3-4, and the description exceeds it by specifying the required shape: an async arrow function returning the final value, no TypeScript syntax, forward slashes only because backslashes are escape sequences. The concrete example anchors the expected form.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb and resource ('Execute one JavaScript program') and frames the scope as 'achieves the whole goal,' which tells an agent exactly what unit of work this tool covers. The follow-up contrast with 'chains of separate tool calls' pins down the abstraction level unambiguously.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly indicates when to prefer this over sequential tool calls ('one call here does the work of N, and intermediate data never enters your context') and gives operational guidance such as looping freely and catching errors inside the code. It stops short of stating when not to use it, e.g. a single trivial operation where a direct tool would be simpler.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
code
TDQS
Scored across 1 tool
There is only one MCP tool, so an agent cannot misselect between tools. The only ambiguity is inside the exposed runtime API, where `shell` can do everything the `fs_*` methods and even `http` (via curl) can do, so multiple paths exist for the same task.
With a single tool there is no cross-tool pattern to violate, and the embedded API uses predictable snake_case with `fs_`/`http` prefixes for most methods. The outliers are bare names like `shell` and `http`/`http_json`, which break the prefix convention slightly but remain obvious.
A single entry point is the deliberate design of a code-mode server, and it is far from trivial—it encapsulates shell, filesystem, and HTTP capability. The tradeoff is that all functionality is compressed behind one untyped-looking call, so per-capability discovery relies entirely on reading the description.
The surface covers the full file lifecycle (write/read/append/exists/stat/list/glob/grep/mkdir/remove) plus shell and HTTP, with `shell` acting as a universal escape hatch for anything missing. Minor gaps exist for rename/copy, binary file handling, and line-level edits, all of which must be hand-rolled via read+write or shell.
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
The OpenRouter for tools. One MCP connection gives any AI agent 254 hosted tools, pay per call.
- WauldoOAuthcom.wauldo
Stateless agentic tools over MCP: concept extraction, long-context, knowledge graph, planning.
One MCP endpoint for Claude, GPT & Gemini: 100+ tools + no-code connectors + agent workers.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceUniversal MCP server for executing TypeScript and Python code with progressive disclosure, reducing token usage by 98% by enabling on-demand access to all other MCP tools through code execution rather than loading tool definitions directly.37 npm129MIT
- AlicenseNot gradedqualityFmaintenanceAn MCP server that enables AI agents to execute sandboxed JavaScript and TypeScript code instead of calling individual tools directly. It significantly reduces token usage by allowing agents to filter, aggregate, and transform data locally before returning results.3 npm28MIT
- AlicenseNot gradedqualityDmaintenanceA self-hosted MCP server that provides a single execute_code tool, enabling agents to write TypeScript to call multiple REST APIs via fetch() with transparent credential injection, reducing token usage by keeping intermediate results in the sandbox.11BSD 3-Clause
- AlicenseAqualityAmaintenanceCode-mode MCP server (docs_search + execute_code two-tool surface) backed by a unified capability manifest across three sandbox kernels (in-process node:vm, WASM via QuickJS / Pyodide / Wasmtime, and remote microVM via E2B / Cloudflare Sandbox). At N=30 tools the bootstrap-context cost drops to 13.6% of direct tool-use. Apache-2.0.26Apache 2.0