npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mcp-b/codemode

v1.0.2

Published

Zero-dependency browser-native code sandbox for LLM-generated tool orchestration

Downloads

410

Readme

@mcp-b/codemode

Codemode lets an LLM write and execute code that orchestrates your tools instead of calling them one at a time. LLMs have seen millions of lines of real TypeScript but only contrived tool-calling examples; they write better programs than tool-call chains.

@mcp-b/codemode is a zero-dependency browser port of Cloudflare's codemode. It replaces Cloudflare Workers with iframe sandboxing and adds a WebMCP bridge that turns page tools into codemode tools in one call.

New to codemode? Read Cloudflare's docs first — they cover the core idea, the CodeAct paper, and when codemode beats standard tool calling.

Experimental — expect breaking changes.

When to use codemode

Use codemode when the model needs to:

  • Chain multiple tool calls with logic between them
  • Combine results from several tools before returning
  • Work with many small WebMCP tools on the client
  • Run multi-step workflows that would be awkward with plain tool calling

For single tool calls, standard AI SDK tool calling is simpler.

How it differs from Cloudflare's

| | @cloudflare/codemode | @mcp-b/codemode | | ------------------- | --------------------------------------- | ------------------------------------------------------------------------------ | | Runtime | Cloudflare Workers | Any browser | | Sandbox | DynamicWorkerExecutor (Worker Loader) | IframeSandboxExecutor (iframe + CSP) or WorkerSandboxExecutor (Web Worker) | | Dependencies | acorn, zod-to-ts | None required (acorn optional) | | Code normalizer | acorn (built-in) | Regex (built-in), acorn (opt-in plugin) | | WebMCP bridge | — | One call exposes all page tools as codemode tools | | AI SDK | Required | Optional peer dependency |

Installation

npm install @mcp-b/codemode ai zod

With AST normalization:

npm install acorn

All peer dependencies (ai, zod, acorn, @mcp-b/webmcp-types) are optional. Install what you use.

Quick start: WebMCP

Expose every WebMCP tool on the page as a single codemode tool.

import { streamText } from 'ai';
import { IframeSandboxExecutor } from '@mcp-b/codemode';
import { createCodeToolFromModelContextTesting } from '@mcp-b/codemode/webmcp';

const testing = navigator.modelContextTesting;
if (!testing) throw new Error('modelContextTesting unavailable');

const executor = new IframeSandboxExecutor({
  timeout: 30_000,
  csp: "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval';",
});

const codemode = createCodeToolFromModelContextTesting({
  modelContextTesting: testing,
  executor,
});

const result = streamText({
  model,
  system: 'You are a helpful assistant.',
  messages,
  tools: { codemode },
});

createCodeToolFromModelContextTesting reads tools from modelContextTesting.listTools(), converts their JSON Schema inputs into typed codemode descriptors, and wires execution through modelContextTesting.executeTool(). It returns a single AI SDK tool.

The bridge is live rather than a one-time snapshot:

  • it re-reads modelContextTesting.listTools() when the codemode tool executes
  • its generated description reflects the current tool list when read
  • WebMCP ToolResponse wrappers are unwrapped automatically, so sandbox code gets structuredContent directly when available
  • if you have a very large toolset, pass maxDescriptionLength to cap prompt size with a compact fallback signature block

The model writes code like:

async () => {
  const collections = await codemode.listCollections({});
  return collections.data.map((collection) => collection.name);
};

The function runs in an iframe sandbox. Each codemode.* call routes back to the host via postMessage.

Quick start: standalone

Use codemode with AI SDK tools directly, no WebMCP required.

import { tool } from 'ai';
import { z } from 'zod';
import { createCodeTool } from '@mcp-b/codemode/ai';
import { IframeSandboxExecutor } from '@mcp-b/codemode';

const tools = {
  getWeather: tool({
    description: 'Get weather for a location',
    inputSchema: z.object({ location: z.string() }),
    execute: async ({ location }) => `Weather in ${location}: 72F, sunny`,
  }),
};

const codemode = createCodeTool({
  tools,
  executor: new IframeSandboxExecutor(),
});

Same createCodeTool shape as upstream, plus a pluggable normalizeCode option.

Executors

Both implement the Executor interface:

interface Executor {
  execute(code: string, fns: ToolFunctions): Promise<ExecuteResult>;
}

IframeSandboxExecutor (recommended)

Runs code in a hidden sandboxed iframe with CSP. The document boundary makes it the better default for in-page WebMCP.

Provisioned mode — codemode creates and manages the iframe:

import { IframeSandboxExecutor } from '@mcp-b/codemode';

const executor = new IframeSandboxExecutor({
  timeout: 30_000,
  csp: "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval';",
});

Provided mode — you supply the iframe and control its origin, settings, and loaded document:

const executor = new IframeSandboxExecutor({
  timeout: 30_000,
  targetOrigin: 'https://sandbox.example.com',
  iframeFactory: () => {
    const iframe = document.createElement('iframe');
    iframe.src = 'https://sandbox.example.com/codemode-runtime.html';
    return iframe;
  },
});

If you host your own iframe, initialize the runtime inside it:

import { initializeIframeSandboxRuntime } from '@mcp-b/codemode/browser';

initializeIframeSandboxRuntime({
  targetOrigin: 'https://app.example.com',
});

| Option | Type | Default | Description | | --------------- | ------------------------- | ------------------- | ---------------------------------------------- | | timeout | number | 30000 | Execution timeout in ms | | csp | string | Restrictive default | Content Security Policy for provisioned iframe | | iframeFactory | () => HTMLIFrameElement | — | Supply your own iframe | | targetOrigin | string | — | Required with iframeFactory |

WorkerSandboxExecutor

Runs code in a Web Worker. Network APIs are scrubbed from the global scope before user code runs.

import { WorkerSandboxExecutor } from '@mcp-b/codemode';

const executor = new WorkerSandboxExecutor({ timeout: 30_000 });

Optional: acorn normalizer

The built-in normalizer is regex-based and dependency-free. It handles code fences, export default, named functions, and arrow functions.

For AST-based normalization, install acorn and pass the normalizer to createCodeTool:

import { createCodeTool } from '@mcp-b/codemode/ai';
import { IframeSandboxExecutor } from '@mcp-b/codemode';
import { normalizeCodeWithAcorn } from '@mcp-b/codemode/acorn';

const codemode = createCodeTool({
  tools,
  executor: new IframeSandboxExecutor(),
  normalizeCode: normalizeCodeWithAcorn,
});

The normalizer is pluggable — any (code: string) => string function works.

Testing

@mcp-b/codemode has two browser-facing test layers:

  • pnpm --filter @mcp-b/codemode test runs the package-local Chromium browser suite through Vitest Browser Mode. This covers the iframe executor, worker executor, and WebMCP bridge behavior in a real browser context.
  • pnpm --filter @mcp-b/codemode test:e2e runs the shared Playwright page flow at e2e/tests/codemode-webmcp.spec.ts in Chromium and records whether the page resolved to the native navigator.modelContextTesting surface or the local polyfill/runtime path.
  • pnpm --filter @mcp-b/codemode test:e2e:beta runs that same Playwright codemode flow against Chrome Beta with --enable-experimental-web-platform-features --enable-features=WebMCPTesting and asserts the native navigator.modelContextTesting path.

WebMCP utilities

@mcp-b/codemode/webmcp exports three functions at different abstraction levels:

| Function | What it does | | ------------------------------------------------ | ---------------------------------------------------------------------------- | | createCodeToolFromModelContextTesting(options) | Builds a live AI SDK tool from the current modelContextTesting tool list | | modelContextTestingToCodemodeTools(testing) | Converts modelContextTesting into executable codemode descriptors | | webmcpToolsToCodemode(tools) | Converts ToolListItem[] into JSON Schema descriptors (no execute handlers) |

Use the lower-level functions to filter tools, modify descriptors, or wire execution differently.

Entry points

| Import | Provides | Peer deps | | ------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------- | | @mcp-b/codemode | IframeSandboxExecutor, WorkerSandboxExecutor, normalizeCode, types, utilities | None | | @mcp-b/codemode/ai | createCodeTool, generateTypes | ai, zod | | @mcp-b/codemode/browser | Same as default (explicit zero-dep entry) | None | | @mcp-b/codemode/acorn | normalizeCodeWithAcorn | acorn | | @mcp-b/codemode/webmcp | WebMCP bridge functions | ai, zod, @mcp-b/webmcp-types |

API reference

createCodeTool(options)

Returns an AI SDK Tool. Import from @mcp-b/codemode/ai.

| Option | Type | Default | Description | | --------------- | ------------------------------------------------------------------- | -------------- | -------------------------------------------------------- | | tools | ToolSet \| ToolDescriptors \| JsonSchemaExecutableToolDescriptors | required | Your tools | | executor | Executor | required | Sandbox for generated code | | description | string | auto-generated | Custom description ({{types}} replaced with type defs) | | normalizeCode | (code: string) => string | built-in regex | Code normalizer |

createCodeToolFromModelContextTesting(options)

Returns an AI SDK Tool. Import from @mcp-b/codemode/webmcp.

| Option | Type | Default | Description | | ---------------------- | --------------------------------------------------------- | -------------- | -------------------------- | | modelContextTesting | Pick<ModelContextTesting, 'listTools' \| 'executeTool'> | required | The testing API | | executor | Executor | required | Sandbox for generated code | | description | string | auto-generated | Custom description | | maxDescriptionLength | number | uncapped | Cap generated descriptions | | normalizeCode | (code: string) => string | built-in regex | Code normalizer |

generateTypes(tools)

Returns TypeScript type definitions from your tools. Used internally by createCodeTool; exported for displaying tools in a UI.

sanitizeToolName(name)

Converts tool names into valid JavaScript identifiers: get-weather becomes get_weather, 3d-render becomes _3d_render.

Current limitations

  • modelContextTesting exposes inputSchema but not always outputSchema — output types default to unknown
  • createCodeToolFromModelContextTesting() refreshes tools lazily on description reads and execute() calls rather than subscribing to WebMCP change events
  • Browser sandboxing (iframe sandbox + CSP, or Worker global scrubbing) provides practical isolation, not a hardened VM
  • JavaScript execution only