MCP代码沙盒服务器
一个与模型上下文协议(MCP)兼容的HTTP服务器,在具有持久文件存储的隔离Docker容器中执行Python和TypeScript代码。
概述
此服务器实现 MCP规范 (版本2024-11-05)使用HTTP和服务器发送事件(SSE)传输。它为像Claude这样的人工智能助手提供了安全的沙盒代码执行,允许他们运行代码并生成在执行过程中持续存在的文件。
主要特点:
- 🔒 安全沙盒 -代码在隔离的Docker容器中运行,没有网络访问权限
- 🗂️ 持久存储 -在中创建的文件
/data在每次对话的执行过程中保持一致 - 📦 多语言 -Python和Types/JavaScript支持开箱即用
- 🔐 认证 -具有哈希目录安全性的承载令牌身份验证
- 📤 文件上传 -在代码执行之前上传数据文件进行分析
- ⚡ 快速TypeScript -由Bun提供支持,启动速度比Node.js快4倍
快速开始
选项1:使用启动脚本(推荐)
# Clone and setup
git clone
cd code-runner
# Build and start
./start.sh
# Server will be available at http://localhost:8080这 start.sh 脚本自动执行:
- 从加载配置
.env - 创建沙盒目录
- 检查Docker连接
- 如果需要,构建跑步者形象
- 启动服务器
选项2:Docker编写
# Clone and setup
git clone
cd code-runner
# Copy environment template
cp .env.example .env
# Edit .env with your tokens
# Build runner images
./build.sh
# Start server
docker-compose up -d
# View logs
docker-compose logs -f选项3:直接二进制
# Build
go build -o mcp-code-sandbox ./cmd/server
# Run with environment variables
source .env
./mcp-code-sandbox建筑
系统设计
┌─────────────────────────────────────────┐
│ MCP Client (Claude, n8n, etc.) │
└───────────────┬─────────────────────────┘
│ HTTPS + Bearer Token
│ JSON-RPC 2.0
▼
┌─────────────────────────────────────────┐
│ MCP Server (Go) │
│ - HTTP + SSE Transport │
│ - Tools: upload_file, run_code │
│ - Hashed directory security │
└───────────────┬─────────────────────────┘
│ Docker API
▼
┌─────────────────────────────────────────┐
│ Runner Containers (ephemeral) │
│ - Python 3.12 (numpy, pandas, etc.) │
│ - TypeScript/Bun (postgres, csv, etc.) │
│ - Bind mount: /data → sandbox dir │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Sandbox Filesystem │
│ /sandboxes/ │
│ └── {SHA256(conversationId+secret)}/ │
│ ├── data.csv (uploaded) │
│ └── plot.png (generated) │
└─────────────────────────────────────────┘组件
- HTTP服务器 -处理MCP JSON-RPC请求(POST)和SSE流(GET)
- 跑步者注册表 -通过Docker标签自动发现可用的语言运行程序
- 容器执行器 -使用资源限制管理Docker容器生命周期
- 沙盒管理器 -使用哈希目录处理每个会话的文件系统隔离
- 认证 -API安全的承载令牌中间件
代码执行是如何工作的
- 客户端通过上传数据文件
upload_file工具(可选) - 客户电话
run_code带有语言和代码的工具 - 服务器为对话创建哈希沙箱目录
- 服务器通过以下方式启动临时运行器容器
/data绑定挂载 - 容器以非root用户身份执行代码(UID 1000)
- 服务器列出沙盒中的文件并返回URL
- 客户端可以通过公共URL下载文件(无需身份验证-通过哈希进行安全保护)
配置
环境变量
创建 .env 文件(或副本 .env.example):
# HTTP server
MCP_HTTP_ADDR=:8080
PUBLIC_BASE_URL=http://localhost:8080
# Authentication
MCP_API_TOKEN=your-secret-token-here
# Sandbox filesystem
SANDBOX_ROOT=/var/sandboxes # Path inside server container
SANDBOX_HOST_PATH=/tmp/sandboxes # Actual host path for Docker bind mounts
FILE_SECRET=your-file-signing-secret # Used for hashing conversation IDs
# Optional: Cloudflare Tunnel
TUNNEL_TOKEN= # Leave empty if not using Cloudflare重要配置说明:
SANDBOX_ROOT-从服务器的角度来看的路径(容器或进程)SANDBOX_HOST_PATH-Docker主机上绑定挂载的绝对路径
- 直接运行服务器时:与 SANDBOX_ROOT - 在Docker中运行时:必须指向实际的主机路径 - 示例:容器中的服务器看到 /var/sandboxes,但安装 /home/user/sandboxes 来自主机
FILE_SECRET-用于将对话ID散列到目录名中。必须是:
- 至少32个字符 - 随机生成: openssl rand -base64 32 - 保密-保护文件访问
MCP_API_TOKEN-API身份验证的承载令牌。生成方式:openssl rand -hex 32
双路径架构
服务器使用双路径系统来支持以下两种功能:
- 直接执行(服务器进程访问本地文件系统)
- Docker Compose部署(服务器在容器中,运行器在兄弟容器中)
示例:Docker Compose
# Server container
environment:
SANDBOX_ROOT: /var/sandboxes # Server's view
SANDBOX_HOST_PATH: /host/sandbox-data # Host's actual path
volumes:
- ./sandbox-data:/var/sandboxes # Mount host dir into server
# Server will tell runners to mount: /host/sandbox-data:/data示例:直接执行
# Both paths are the same
SANDBOX_ROOT=/tmp/sandboxes
SANDBOX_HOST_PATH=/tmp/sandboxesMCP协议
运输
服务器实现 HTTP与SSE 传输(单端点):
- 发布
/mcp-发送JSON-RPC请求,接收JSON响应 - 获取
/mcp-为服务器发起的消息建立SSE流
认证
全部 /mcp 请求要求:
Authorization: Bearer 方法
initialize -MCP握手
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"clientInfo": {"name": "client", "version": "1.0"}
}
}响应包括服务器功能(工具)。
tools/list -列出可用工具
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}返回三个工具:
upload_file-将数据文件上传到沙盒run_code-在沙盒容器中执行代码list_runners-列出可用的语言运行程序
tools/call -执行工具
有关详细示例,请参阅下面的“工具”部分。
工具
upload_file
在运行代码之前,将文件上传到对话的沙盒。
论据:
conversationId(string)-唯一对话标识符filename(string)-要创建的文件名(例如。,data.csv)content(string)-Base64编码文件内容
例子:
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "upload_file",
"arguments": {
"conversationId": "session-123",
"filename": "data.csv",
"content": "bmFtZSxhZ2UKQWxpY2UsMzAKQm9iLDI1"
}
}
}'答复:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "{\"success\":true,\"message\":\"File 'data.csv' uploaded successfully (18 bytes)\",\"file\":{\"name\":\"data.csv\",\"url\":\"http://localhost:8080/files/abc123.../data.csv\"}}"
}]
}
}run_code
在沙盒Docker容器中执行代码。
论据:
conversationId(string)-唯一对话标识符language(string)-要执行的语言:python或typescriptcode(string)-要执行的源代码network(布尔值,可选)-启用网络访问(默认值:false)environment(对象,可选)-环境变量(例如,API键)
可用库:
- python:
requests,numpy,pandas,matplotlib,psycopg2 - TypeScript:
postgres,pg,csv-parser,papaparse
环境变量(自动注入):
FILE_BASE_URL-此对话中生成文件的基本URL
- 用于创建带有生成文件链接的markdown - 示例(Python): f"" - 示例(TypeScript): process.env.FILE_BASE_URL + '/output.json'
示例:使用Markdown输出的Python数据分析
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "run_code",
"arguments": {
"conversationId": "session-123",
"language": "python",
"code": "import os\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\ndf = pd.read_csv(\"/data/data.csv\")\nprint(df.describe())\n\nplt.bar(df[\"name\"], df[\"age\"])\nplt.savefig(\"/data/chart.png\")\n\n# Generate markdown with correct URL\nbase_url = os.environ[\"FILE_BASE_URL\"]\nmarkdown = f\"# Analysis Results\\n\\n## Chart\\n\\n\\n\\n## Data\\n\\nSee [data.csv]({base_url}/data.csv)\"\nprint(markdown)"
}
}
}'答复:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{
"type": "text",
"text": "{\"success\":true,\"output\":\" age\\ncount 2.0\\nmean 27.5\\n...\\nChart saved!\\n\",\"files\":[{\"name\":\"data.csv\",\"url\":\"...\"},{\"name\":\"chart.png\",\"url\":\"...\"}]}"
}]
}
}示例:带网络访问的TypeScript
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "run_code",
"arguments": {
"conversationId": "session-123",
"language": "typescript",
"network": true,
"code": "const response = await fetch(\"https://api.example.com/data\");\nconst data = await response.json();\nconsole.log(data);\n\nconst fs = require(\"fs\");\nfs.writeFileSync(\"/data/result.json\", JSON.stringify(data, null, 2));"
}
}
}'list_runners
列出可用的语言运行程序及其Docker镜像。
例子:
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "list_runners"
}
}'安全
集装箱隔离
网络隔离:
- 容器运行
NetworkDisabled: true默认情况下 - 仅在以下情况下启用
network: true明确通过 - 防止意外的外部连接
用户权限:
- 所有跑步者都以非root用户身份执行(UID 1000)
- 预先创建的沙盒目录
1000:1000所有权 - 防止特权升级
资源限制:
- 中央处理器:每个容器0.5个芯
- 记忆:每个容器256MB
- 超时:最多执行30秒
- 自动清理:执行后移除的容器
最小图像:
- Alpine Linux基础,攻击面更小
- 仅安装了基本软件包
- 没有外壳或不必要的工具
哈希目录安全
对话数据存储在使用SHA256哈希命名的目录中:
Directory path: /sandboxes/{SHA256(conversationId + FILE_SECRET)}/
File URL: https://example.com/files/{hash}/{filename}安全属性:
- 不可预测的 -在不知道的情况下无法猜测哈希值
FILE_SECRET - 文件系统安全 -哈希始终是有效的十六进制(64个字符:
[0-9a-f]) - 无路径遍历 -没有
..或/可能在hash中 - 抗蛮力 -2^256个可能值
无需签名 -哈希本身提供了安全性,消除了在URL上使用HMAC签名的需要。
认证
API终点:
- 全部
/mcp请求需要Authorization: Bearer - 处理前通过中间件验证令牌
文件下载:
- 无需身份验证(通过哈希目录进行安全保护)
- 路径遍历预防
- 仅提供沙盒根目录下的文件
生产建议
- 强烈的秘密 -生成方式
openssl rand -base64 32 - 隔离主机 -在专用服务器或虚拟机上运行
- Docker套接字 -在Docker中考虑Docker以获得更好的隔离
- 超文本传输安全协议 -使用Cloudflare隧道或带有TLS的反向代理
- 速率限制 -在代理/网关级别实施
- 监控 -跟踪容器创建、资源使用、错误
- 备份 -沙盒数据量的定期备份
部署
地方发展
# Start with Docker Compose
docker-compose up -d
# Test endpoint
curl http://localhost:8080 \
-H "Authorization: Bearer your-token"使用Cloudflare隧道进行生产
# Set TUNNEL_TOKEN in .env
# Configure tunnel to route to http://mcp-sandbox-server:8080
# Start with Cloudflare compose file
docker-compose -f docker-compose-cloudflare.yml up -d
# Verify tunnel
docker-compose -f docker-compose-cloudflare.yml logs cloudflared文件下载
文件可以通过公共URL访问,无需身份验证:
# Download a generated file
curl "http://localhost:8080/files/abc123.../plot.png" -o plot.png发展
添加新的语言运行程序
- 创建Dockerfile (
Dockerfile-):
FROM
# Labels for discovery
LABEL sandbox.runner=true
LABEL sandbox.language=
# Non-root user (UID 1000)
RUN adduser -D -u 1000 sandbox
# Install language runtime and libraries
RUN apk add --no-cache
# Create runner script
RUN cat > /usr/local/bin/runner.sh /tmp/script.
cd /data
exec /tmp/script.
EOF
RUN chmod +x /usr/local/bin/runner.sh
USER 1000:1000
WORKDIR /data
ENTRYPOINT ["/usr/local/bin/runner.sh"]- 构建图像:
docker build -f Dockerfile- -t mcp-sandbox-runner-:latest .- 重新启动服务器 -自动发现将找到新的跑步者
项目结构
code-runner/
├── cmd/server/ # Main server application
├── internal/
│ ├── auth/ # Bearer token authentication
│ ├── config/ # Environment configuration
│ ├── filesign/ # Base URL management
│ ├── handler/ # HTTP handlers, MCP protocol
│ ├── runner/ # Docker container execution
│ └── sandbox/ # Filesystem management
├── Dockerfile-python # Python runner image
├── Dockerfile-typescript # TypeScript/Bun runner image
├── Dockerfile # Server image
├── build.sh # Build all images
├── start.sh # Start server with env
├── docker-compose.yml # Local deployment
└── docker-compose-cloudflare.yml # Cloudflare deployment监控
查看活动容器
# All containers
docker ps
# Only runners
docker ps --filter "label=sandbox.runner=true"资源使用情况
# Real-time stats
docker stats
# Server only
docker stats mcp-sandbox-server日志
# Server logs
docker-compose logs -f mcp-sandbox-server
# All logs
docker-compose logs -f磁盘使用
# Docker resources
docker system df
# Sandbox data
du -sh ./sandbox-data故障排除
服务器无法连接到Docker
# Check Docker socket
ls -la /var/run/docker.sock
# Test Docker
docker ps
# Check logs
docker-compose logs mcp-sandbox-server未找到跑步者图片
# List runners
docker images | grep mcp-sandbox-runner
# Rebuild
./build.sh
# Restart
docker-compose restart容器中的权限错误
# Check sandbox directory ownership
ls -la sandbox-data/
# Fix ownership (if needed)
sudo chown -R 1000:1000 sandbox-data/端口已在使用中
# Find process
lsof -i :8080
# Change port in .env
MCP_HTTP_ADDR=:8081
PUBLIC_BASE_URL=http://localhost:8081
# Restart
docker-compose down && docker-compose up -d许可证
麻省理工学院
