Skip to main content
Glama

CI License: MIT Node MCP stars

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 --stats

The 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/main

No 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

shell, fs_read, fs_write, fs_append, fs_exists, fs_stat, fs_list, fs_glob, fs_grep, fs_remove, fs_mkdir

network

http, http_json

MCP

mcp_list, mcp_tools, mcp_call — any pooled server

Cloudflare

cf_get, cf_post, cf_patch, cf_put, cf_del, cf_request — only when CLOUDFLARE_API_TOKEN is set

Moli

moli_fetch — only when MOLI_BIN is set

globals

sleep(ms), env, ROOT, log()

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:

  1. --config <path> or CODEMODE_CONFIG

  2. ./.mcp.json

  3. ~/.config/opencode/opencode.jsonc (and the Windows path)

  4. 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

CODEMODE_ROOT

base dir for relative fs paths (default: home)

CODEMODE_TIMEOUT_MS

sandbox wall-clock limit (default 120000)

CODEMODE_DEBUG=1

forward pooled-server stderr

CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID

enables cf_*

MOLI_BIN

enables moli_fetch

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:vm with no require, import, process, or filesystem. Its only capabilities are the proxies listed above.

  • node:vm is not a security boundary. A determined escape exists, and the host functions really do things: shell is a shell, fs_write writes, http reaches 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.log is 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 code call

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.

License

MIT © Arman Saeedi

Available Tools

1 tool
codeA

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; }

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesJavaScript async arrow function to execute

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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. 1 tool updatev0.1.0
    • First observedcode

TDQS

A4.4/5.0

Scored across 1 tool

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count4/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Universal 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 npm
    129
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    An 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 npm
    28
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A 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.
    11
    BSD 3-Clause
  • A
    license
    A
    quality
    A
    maintenance
    Code-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.
    2
    6
    Apache 2.0