e2b-code-mode

e2b-code-mode

A Code Mode MCP server for the e2b API, giving agents three tools (search, execute_read, execute_write) to write and run JavaScript functions inside sandboxed environments for API discovery and calls.

Category
Visit Server

README

e2b MCP Code Mode

A Model Context Protocol server that gives an agent three tools instead of dozens for driving the e2b REST API. The agent writes a short JavaScript function that runs inside an e2b sandbox and calls the API through a thin api.request() client — looping, filtering, and chaining calls in one round-trip.

This applies Cloudflare's Code Mode pattern to e2b:

  • LLMs write API-calling code more reliably than long chains of individual tool calls.
  • Intermediate data stays in the sandbox, out of the model's context.
  • The agent discovers endpoints with search (the OpenAPI spec), then calls them with execute_read/execute_write.

Quickstart

Prerequisites: Node.js ≥ 20.12, pnpm (corepack enable provides it), and an e2b API key (dashboard).

pnpm install
pnpm build
pnpm start          # or: pnpm dev  (no build step)

The server starts a stateless streamable-HTTP MCP endpoint on http://127.0.0.1:3000/mcp. It has no API key of its own — each request carries the caller's e2b key as a bearer token.

Connect an MCP client (the key travels as Authorization: Bearer <e2b-key>; this repo ships a .mcp.json):

{
  "mcpServers": {
    "e2b-code-mode": {
      "type": "http",
      "url": "http://127.0.0.1:3000/mcp",
      "headers": { "Authorization": "Bearer ${E2B_API_KEY}" }
    }
  }
}

Copy .env.example to .env and set E2B_API_KEY so ${E2B_API_KEY} expands and the live tests can run.

Try it — call execute_read with an async arrow function:

async () => {
  const res = await api.request({ method: 'GET', path: '/sandboxes' })
  return { status: res.status, running: res.data.length }
}

You get back e.g. { "status": 200, "running": 2 } — produced by code the agent wrote, run in a sandbox that can reach only the e2b API. See TEST-PROMPT.md for a richer, dashboard-visible demo task.


The three tools

Each takes a single code string — an async arrow function that returns a value. console.log output comes back separately as stdout.

Tool What it does In scope Annotation
search Query the e2b OpenAPI spec (all $refs resolved) to find endpoints and their shapes. Network-isolated. spec read-only
execute_read Call the e2b API via api.request() — GET only. api read-only
execute_write Call the e2b API via api.request() — any method (POST/PUT/PATCH/DELETE). api destructive

The workflow is: search to find an endpoint's path and request shape, then execute_read/execute_write to call it. The client is api.request({ method, path, query?, body?, contentType?, rawBody? }), which returns { status, ok, data }.


How it works

MCP client ──HTTP (stateless JSON)──▶ express /mcp ──▶ McpServer (3 tools)
   Bearer: e2b key                         │
                          ┌────────────────┼────────────────┐
                        search        execute_read       execute_write
                          │                └───────┬───────┘
                          ▼                        ▼
                 fresh sandbox            fresh sandbox
                 (egress DENIED)          (egress → api.e2b.dev only;
                 embeds `spec`             E2B_API_KEY injected;
                                           `api.request()` client)
  • Transport — stateless JSON: a fresh McpServer + transport per POST /mcp (sessionIdGenerator: undefined, enableJsonResponse: true — no sessions, no SSE), so the server holds no protocol state and scales horizontally; GET/DELETE /mcp return 405.
  • Per call — a fresh e2b sandbox, killed in finally. No SDK is installed inside it, so there's nothing to amortize with a warm pool.
  • search sandbox — all egress denied; the slimmed OpenAPI spec is embedded as spec for the agent's read-only code to query.
  • execute_* sandbox — egress limited to api.e2b.dev; the caller's key is injected as E2B_API_KEY and used by api.request(). The read/write split is enforced in the client: execute_read throws on any non-GET before the fetch (annotated read-only), while execute_write allows every method (destructive).
  • Runner — agent code is written to a file and run with node (never eval); output returns via a result file. The code sits on its own lines in the invoke scaffold, so a trailing // comment can't swallow the closing tokens.

The source is small and layered by responsibility:

File Responsibility
src/index.ts Entry point: loads .env, starts the HTTP listener, configures loopback DNS-rebinding protection.
src/http.ts Express app: streamable-HTTP transport, bearer auth, Host/Origin validation, /healthz, /debug/endpoints.
src/server.ts The MCP surface: the three tools, response formatting, the untrusted-data boundary.
src/sandbox.ts Sandbox plumbing: runner construction, the two sandbox shapes, the injected api.request() client, output caps, the credential/admin guard.
src/spec.ts Fetches, $ref-resolves, slims, and caches the e2b OpenAPI spec for search.
src/log.ts pino logger + credential redaction.

Security notes

Agent code is untrusted but runs inside an e2b Firecracker microVM and authenticates with the caller's own e2b key — so by design it can use that key against the e2b API. Beyond the sandbox isolation, per-tool egress, and read/write split above:

  • Untrusted-data boundary — all execute output (result/stdout/stderr) may embed API content (logs, metadata), so it's wrapped in a <untrusted-data-{uuid}>…</> envelope (error path too), with the model told not to follow instructions inside. Truncation happens before wrapping, so a size cap can't sever the closing tag.
  • Credential/admin block — paths containing api-keys, access-tokens, teams, or admin (incl. /admin/teams/{id}/api-keys) are refused. The path is first normalized to a fixpoint (percent-decode, re-resolve dot-segments, collapse slashes, lowercase), so encoded spellings (/%61pi-keys, //access-tokens, /x%2F..%2Fteams, /TEAMS) still match; it only over-blocks.
  • HTTP hardening — bearer token required on /mcp (401 + WWW-Authenticate otherwise); loopback binds get DNS-rebinding protection (Host/Origin allowlist, incl. [::1]); 1 MB body cap.
  • Output caps — bounded inside the sandbox before reaching the server: runner result cap (400k chars), head -c reads (500k file / 20k per stream), final server truncate (100k result / 10k logs).
  • Token location (vs Cloudflare) — Cloudflare keeps the token out of the isolate via an outbound-fetch proxy; e2b has no such hook, so E2B_API_KEY lives in the sandbox env (agent code can read it). Safety rests on isolation + egress — the key only reaches api.e2b.dev — not token secrecy.

Configuration

All optional except the caller's bearer token.

Env var Default Meaning
PORT 3000 HTTP listen port
HOST 127.0.0.1 Bind address (loopback enables DNS-rebinding protection)
E2B_TEMPLATE base e2b template the sandboxes boot from (needs Node with global fetch; secure-mode compatible)
E2B_EXEC_TIMEOUT_MS 120000 Per-call budget for the agent's code
E2B_OPENAPI_URL e2b infra spec Override the OpenAPI spec URL the search tool loads
E2B_API_KEY — Only used by local tooling/tests; HTTP callers send it as the bearer token
LOG_LEVEL info trace/debug/info/warn/error/fatal/silent
LOG_FORMAT json json (structured) or pretty (dev)
MCP_DEBUG_ENDPOINT true Set false to disable GET /debug/endpoints

Observability

  • Structured logs via pino to stderr: one line per HTTP request (method, path, status, responseTime, req.id; 4xx→warn, 5xx→error) plus per-call lifecycle lines carrying the reqId, redacted key, duration, and calledEndpoints count. Use LOG_FORMAT=pretty locally. Credentials are never logged.

  • GET /debug/endpoints — a no-auth JSON listing of every route (method, path, auth, description), the tool names, and a small status block (version, pid, uptime, log level). No secrets; disable with MCP_DEBUG_ENDPOINT=false.

    curl -s localhost:3000/debug/endpoints | jq
    

Testing

pnpm test
  • test/server.test.ts — the MCP surface via an in-memory client/server pair (tool list, annotations, error shapes). No key.
  • test/http.test.ts — the express app over a real ephemeral port: stateless JSON shape, the bearer-auth gate, non-Bearer rejection, token→handler plumbing, 405s, DNS-rebinding enforcement, and the /debug/endpoints manifest. No key.
  • test/live.test.ts — end-to-end against real e2b: spec search, execute_read GET plus non-GET rejection, trailing-comment tolerance, error/SyntaxError surfacing inside the boundary, the credential/admin guard (including encoded bypasses), an execute_write create+delete round-trip, egress denial, and the stdout size cap. Runs only when E2B_API_KEY is set.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured