#
tokens: 5767/50000 24/24 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .github
│   ├── scripts
│   │   └── is_release.sh
│   └── workflows
│       ├── publish_packages.yml
│       └── release.yml
├── .gitignore
├── .npmrc
├── CODEOWNERS
├── Dockerfile
├── LICENSE
├── package.json
├── packages
│   ├── js
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   └── python
│       ├── .gitignore
│       ├── .python-version
│       ├── e2b_mcp_server
│       │   ├── __init__.py
│       │   └── server.py
│       ├── package.json
│       ├── poetry.lock
│       ├── pyproject.toml
│       └── README.md
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── readme-assets
│   ├── mcp-server-dark.png
│   └── mcp-server-light.png
├── README.md
└── smithery.yaml
```

# Files

--------------------------------------------------------------------------------
/packages/python/.python-version:
--------------------------------------------------------------------------------

```
3.12.3

```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
node_modules

```

--------------------------------------------------------------------------------
/packages/js/.gitignore:
--------------------------------------------------------------------------------

```
node_modules/
build/
*.log
.env*
```

--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------

```
enable-pre-post-scripts=true
auto-install-peers=true
exclude-links-from-lockfile=true
prefer-workspace-packages=false
link-workspace-packages=false
engine-strict=true

```

--------------------------------------------------------------------------------
/packages/python/.gitignore:
--------------------------------------------------------------------------------

```
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

```

--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------

```markdown
# Changesets

To add changeset run:

```bash
npx changeset
```

in the root of the project. This will create a new changeset in the `.changeset` folder.

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
![E2B MCP Server Preview Light](/readme-assets/mcp-server-light.png#gh-light-mode-only)
![E2B MCP Server Preview Dark](/readme-assets/mcp-server-dark.png#gh-dark-mode-only)

# E2B MCP Server

[![smithery badge](https://smithery.ai/badge/e2b)](https://smithery.ai/server/e2b)

This repository contains the source code for the [E2B](https://e2b.dev) MCP server.

The E2B MCP server allows you to add [code interpreting capabilities](https://github.com/e2b-dev/code-interpreter) to your Claude Desktop app via the E2B Sandbox. See demo [here](https://x.com/mishushakov/status/1863286108433317958).


Available in two editions:

- [JavaScript](packages/js/README.md)

- [Python](packages/python/README.md)


### Installing via Smithery

You can also install E2B for Claude Desktop automatically via [Smithery](https://smithery.ai/server/e2b):

```bash
npx @smithery/cli install e2b --client claude
```

```

--------------------------------------------------------------------------------
/packages/python/README.md:
--------------------------------------------------------------------------------

```markdown
# E2B MCP Server (Python)

A Model Context Protocol server for running code in a secure sandbox by [E2B](https://e2b.dev).

## Development

Install dependencies:
```
uv install
```

## Installation

To use with Claude Desktop, add the server config:

On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "e2b-mcp-server": {
      "command": "uvx",
      "args": ["e2b-mcp-server"],
      "env": { "E2B_API_KEY": "${e2bApiKey}" }
    }
  }
}
```

### Debugging

Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:

```
npx @modelcontextprotocol/inspector \
  uv \
  --directory . \
  run \
  e2b-mcp-server \
```

The Inspector will provide a URL to access debugging tools in your browser.

```

--------------------------------------------------------------------------------
/packages/js/README.md:
--------------------------------------------------------------------------------

```markdown
# E2B MCP Server (JavaScript)

A Model Context Protocol server for running code in a secure sandbox by [E2B](https://e2b.dev).

## Development

Install dependencies:
```
pnpm install
```

Build the server:
```
pnpm build
```

For development with auto-rebuild:
```
pnpm watch
```

## Installation

To use with Claude Desktop, add the server config:

On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "e2b-server": {
      "command": "npx",
      "args": ["-y", "@e2b/mcp-server"],
      "env": { "E2B_API_KEY": "${e2bApiKey}" }
    }
  }
}
```

### Debugging

Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:

```
pnpm inspector
```

The Inspector will provide a URL to access debugging tools in your browser.

```

--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------

```yaml
packages:
  - packages/*

```

--------------------------------------------------------------------------------
/packages/python/e2b_mcp_server/__init__.py:
--------------------------------------------------------------------------------

```python
from . import server
import asyncio

def main():
   """Main entry point for the package."""
   asyncio.run(server.main())

# Optionally expose other important items at package level
__all__ = ['main', 'server']

```

--------------------------------------------------------------------------------
/.github/scripts/is_release.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/sh

# This script checks if the current commit contains changesets.

set -eu

CHANGES=$(node -e "require('@changesets/read').default(process.cwd()).then(result => console.log(!!result.length))")

echo "${CHANGES}"

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Dockerfile for E2B MCP Server JavaScript Edition

FROM node:22-alpine AS builder

WORKDIR /app

# Copy the application files
COPY packages/js/ .

# Install dependencies
RUN npm install

# Build the application
RUN npm run build

ENV NODE_ENV=production

ENTRYPOINT ["node", "./build/index.js"]

```

--------------------------------------------------------------------------------
/packages/python/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@e2b/python-mcp-server",
  "private": true,
  "version": "0.1.0",
  "scripts": {
    "postVersion": "poetry version $(pnpm pkg get version --workspaces=false | tr -d \\\")",
    "postPublish": "poetry build && poetry config pypi-token.pypi ${PYPI_TOKEN} && poetry publish --skip-existing"
  }
}

```

--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "ignore": [],
  "linked": [],
  "access": "public",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "privatePackages": {
    "version": true,
    "tag": true
  }
}

```

--------------------------------------------------------------------------------
/packages/js/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "e2b-mcp-root",
  "private": true,
  "scripts": {
    "version": "pnpm changeset version && pnpm run -r postVersion",
    "publish": "pnpm changeset publish && pnpm run -r postPublish",
    "changeset": "pnpx @changesets/cli"
  },
  "packageManager": "[email protected]",
  "devDependencies": {
    "@changesets/read": "^0.6.2",
    "changeset": "^0.2.6"
  }
}

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration file: https://smithery.ai/docs/deployments

build:
  dockerBuildPath: .
startCommand:
  type: stdio
  configSchema:
    # JSON Schema defining the configuration options for the MCP.
    type: object
    required:
      - e2bApiKey
    properties:
      e2bApiKey:
        type: string
        description: The API key for the E2B server.
  commandFunction:
    # A function that produces the CLI command to start the MCP on stdio.
    |-
    (config) => ({ command: 'node', args: ['./build/index.js'], env: { E2B_API_KEY: config.e2bApiKey } })

```

--------------------------------------------------------------------------------
/packages/python/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[tool.poetry]
name = "e2b-mcp-server"
version = "0.1.0"
description = "E2B MCP Server"
authors = ["e2b <[email protected]>"]
license = "Apache-2.0"
readme = "README.md"
homepage = "https://e2b.dev/"
repository = "https://github.com/e2b-dev/mcp-server/tree/main/packages/python"
packages = [{ include = "e2b_mcp_server" }]

[tool.poetry.dependencies]
python = ">=3.10,<4.0"

e2b-code-interpreter = "^1.0.2"
mcp = "^1.0.0"
pydantic = "^2.10.2"
python-dotenv = "1.0.1"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.urls]
"Bug Tracker" = "https://github.com/e2b-dev/mcp-server/issues"

```

--------------------------------------------------------------------------------
/packages/js/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@e2b/mcp-server",
  "version": "0.2.1",
  "description": "A Model Context Protocol server",
  "repository": {
    "type": "git",
    "url": "https://github.com/e2b-dev/mcp-server",
    "directory": "packages/js"
  },
  "type": "module",
  "bin": {
    "@e2b/mcp-server": "./build/index.js"
  },
  "files": [
    "build"
  ],
  "scripts": {
    "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
    "prepare": "npm run build",
    "watch": "tsc --watch",
    "inspector": "npx @modelcontextprotocol/inspector build/index.js"
  },
  "dependencies": {
    "@e2b/code-interpreter": "^1.0.4",
    "@modelcontextprotocol/sdk": "0.6.0",
    "dotenv": "^16.4.5",
    "zod": "^3.23.8",
    "zod-to-json-schema": "^3.23.5"
  },
  "devDependencies": {
    "@types/node": "^20.11.24",
    "typescript": "^5.3.3"
  }
}

```

--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------

```yaml
name: Release MCP Packages

on:
  push:
    branches:
      - main

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
  id-token: write
  contents: write

jobs:
  is_release:
    name: Is release?
    runs-on: ubuntu-22.04
    outputs:
      release: ${{ steps.version.outputs.release }}
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v3

      - name: Install pnpm
        uses: pnpm/action-setup@v3
        id: pnpm-install
        with:
          version: 9.5

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: "22.x"
          registry-url: "https://registry.npmjs.org"
          cache: pnpm
          cache-dependency-path: pnpm-lock.yaml

      - name: Configure pnpm
        run: |
          pnpm config set auto-install-peers true
          pnpm config set exclude-links-from-lockfile true

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Check if new version
        id: version
        run: |
          IS_RELEASE=$(./.github/scripts/is_release.sh)
          echo "release=$IS_RELEASE" >> "$GITHUB_OUTPUT"

  publish:
    name: Publish
    needs: [is_release]
    if: (!cancelled()) && !contains(needs.*.result, 'failure') && needs.is_release.outputs.release == 'true'
    uses: ./.github/workflows/publish_packages.yml
    secrets: inherit

  report-failure:
    needs: [publish]
    if: failure()
    name: Release Failed - Slack Notification
    runs-on: ubuntu-22.04
    steps:
      - name: Release Failed - Slack Notification
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_COLOR: "#ff0000"
          SLACK_MESSAGE: ":here-we-go-again: :bob-the-destroyer: We need :fix-parrot: ASAP :pray:"
          SLACK_TITLE: Release Failed
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

```

--------------------------------------------------------------------------------
/packages/python/e2b_mcp_server/server.py:
--------------------------------------------------------------------------------

```python
import os
import json
import logging
from collections.abc import Sequence
from typing import Any

from dotenv import load_dotenv
from mcp.server import Server
from mcp.types import (
    Tool,
    TextContent,
    ImageContent,
    EmbeddedResource,
)

from pydantic import BaseModel, ValidationError
from e2b_code_interpreter import Sandbox


# Load environment variables
load_dotenv()

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("e2b-mcp-server")

# Tool schema
class ToolSchema(BaseModel):
    code: str

app = Server("e2b-code-mcp-server")

@app.list_tools()
async def list_tools() -> list[Tool]:
    """List available tools."""
    return [
        Tool(
            name="run_code",
            description="Run python code in a secure sandbox by E2B. Using the Jupyter Notebook syntax.",
            inputSchema=ToolSchema.model_json_schema()
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
    """Handle tool calls."""
    if name != "run_code":
        raise ValueError(f"Unknown tool: {name}")

    try:
        arguments = ToolSchema.model_validate(arguments)
    except ValidationError as e:
        raise ValueError(f"Invalid code arguments: {e}") from e

    sbx = Sandbox()
    execution = sbx.run_code(arguments.code)
    logger.info(f"Execution: {execution}")

    result = {
        "stdout": execution.logs.stdout,
        "stderr": execution.logs.stderr,
    }

    return [
        TextContent(
            type="text",
            text=json.dumps(result, indent=2)
        )
    ]

async def main():
    # Import here to avoid issues with event loops
    from mcp.server.stdio import stdio_server

    async with stdio_server() as (read_stream, write_stream):
        await app.run(
            read_stream,
            write_stream,
            app.create_initialization_options()
        )

```

--------------------------------------------------------------------------------
/.github/workflows/publish_packages.yml:
--------------------------------------------------------------------------------

```yaml
name: Publish MCP Packages

on:
  workflow_call:
    secrets:
      PYPI_TOKEN:
        required: true

permissions:
  id-token: write
  contents: write

jobs:
  test:
    name: Publish MCP Server
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.VERSION_BUMPER_APPID }}
          private-key: ${{ secrets.VERSION_BUMPER_SECRET }}

      - name: Checkout Repo
        uses: actions/checkout@v3
        with:
          token: ${{ steps.app-token.outputs.token }}

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.10"

      - name: Install and configure Poetry
        uses: snok/install-poetry@v1
        with:
          version: 1.5.1
          virtualenvs-create: true
          virtualenvs-in-project: true
          installer-parallel: true

      - uses: pnpm/action-setup@v3
        with:
          version: 9.5

      - name: Setup Node.js 22
        uses: actions/setup-node@v6
        with:
          node-version: '22.x'
          cache: pnpm
          registry-url: 'https://registry.npmjs.org'

      - name: Configure pnpm
        run: |
          pnpm config set auto-install-peers true
          pnpm config set exclude-links-from-lockfile true

      - name: Update npm
        run: |
          npm install -g npm@^11.6
          npm --version

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Create new versions
        run: pnpm run version
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Release new versions
        uses: changesets/action@v1
        with:
          publish: pnpm run publish
          createGithubReleases: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
          NPM_TOKEN: "" # See https://github.com/changesets/changesets/issues/1152#issuecomment-3190884868

      - name: Update lock file
        run: pnpm i --no-link --no-frozen-lockfile

      - name: Commit new versions
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git commit -am "[skip ci] Release new versions" || exit 0
          git push
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

```

--------------------------------------------------------------------------------
/packages/js/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Sandbox } from "@e2b/code-interpreter";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  ListToolsRequestSchema,
  CallToolRequestSchema,
  ErrorCode,
  McpError,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import dotenv from "dotenv";

dotenv.config();

const toolSchema = z.object({
  code: z.string(),
});

class E2BServer {
  private server: Server;

  constructor() {
    this.server = new Server(
      {
        name: "e2b-mcp-server",
        version: "0.1.0",
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    this.setupHandlers();
    this.setupErrorHandling();
  }

  private setupErrorHandling(): void {
    this.server.onerror = (error) => {
      console.error("[MCP Error]", error);
    };

    process.on("SIGINT", async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  private setupHandlers(): void {
    this.setupToolHandlers();
  }

  private setupToolHandlers(): void {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: "run_code",
          description:
            "Run python code in a secure sandbox by E2B. Using the Jupyter Notebook syntax.",
          inputSchema: zodToJsonSchema(toolSchema),
        },
      ],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name !== "run_code") {
        throw new McpError(
          ErrorCode.MethodNotFound,
          `Unknown tool: ${request.params.name}`
        );
      }

      const parsed = toolSchema.safeParse(request.params.arguments);
      if (!parsed.success) {
        throw new McpError(
          ErrorCode.InvalidParams,
          "Invalid code interpreter arguments"
        );
      }

      const { code } = parsed.data;

      const sandbox = await Sandbox.create();
      const { results, logs } = await sandbox.runCode(code);

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify({ results, logs }, null, 2),
          },
        ],
      };
    });
  }

  async run(): Promise<void> {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);

    // Although this is just an informative message, we must log to stderr,
    // to avoid interfering with MCP communication that happens on stdout
    // console.error("E2B MCP server running on stdio");
  }
}

const server = new E2BServer();
server.run().catch(console.error);

```