# 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
[](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);
```