# Code-exec-mcp **Repository Path**: Leorizon/code-exec-mcp ## Basic Information - **Project Name**: Code-exec-mcp - **Description**: 一个用 Go 编写的 MCP (Model Context Protocol) 工具服务,支持执行 **Go / Python / Node.js / Shell** 代码,通过 **Streamable HTTP** 协议对外提供服务(基于 **gin** Web 框架),并使用 **Alpine** Docker 镜像封装程序与多语言运行环境。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-24 - **Last Updated**: 2026-08-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # code-exec-mcp [![version](https://img.shields.io/badge/version-1.1.0-blue)]() [![Go](https://img.shields.io/badge/Go-1.25+-00ADD8)]() 一个用 Go 编写的 MCP (Model Context Protocol) 工具服务,支持执行 **Go / Python / Node.js / Shell** 代码,通过 **Streamable HTTP** 协议对外提供服务(基于 **gin** Web 框架),并使用 **Alpine** Docker 镜像封装程序与多语言运行环境。 ## 架构 ``` ┌─────────────────┐ HTTP POST (JSON-RPC / Streamable HTTP) │ MCP 客户端 │ ─────────────────────────────────────────▶ /mcp └─────────────────┘ ┌────────────────────────────────────┐ │ gin Web 框架 (Release 模式) │ │ ├─ GET /healthz 健康检查 │ │ └─ Any /mcp MCP 端点 │ ├────────────────────────────────────┤ │ code-exec-mcp (Go, mcp-go) │ │ └─ Tool: execute_code │ └───────────────┬────────────────────┘ │ 临时目录隔离执行 ┌───────────────▼────────────────────┐ │ Runner (runner.go) │ │ ├─ shell → sh script.sh │ │ ├─ python → python3 main.py │ │ ├─ nodejs → node main.js │ │ └─ go → go run main.go │ │ 超时控制 / 输出截断(256KB) │ └────────────────────────────────────┘ ``` **组件** - `main.go` — 服务入口:gin Web 框架(`/healthz` 健康检查、`/mcp` MCP 端点),挂载 mcp-go 的 Streamable HTTP handler(**有状态会话模式**:`initialize` 返回 `Mcp-Session-Id`,后续请求需携带该头路由到同一会话,无效会话返回 404;gin 中间件处理 CORS:允许任意来源 `*` 并反射客户端 `Access-Control-Request-Headers`,端口由 `PORT` 环境变量决定,默认 `8080`) - `runner.go` — 多语言执行器:代码写入临时目录(执行后清理)、子进程以**独立进程组**运行并整体超时强杀(默认 30s,上限 120s,杀负进程组,后台子进程无残留)、stdout/stderr 分区返回、输出截断保护 - `files.go` — workspace 文件管理工具(`list_files`/`read_file`/`write_file`),词法校验(防穿越)+ 打开层 `openat` + `O_NOFOLLOW` 内核级拒绝符号链接,无 TOCTOU 竞态窗口 `GET /healthz` 返回运行状态:`status`、`version`、`uptime`、`running`(当前并发执行数)、`maxConc`(并发上限)、`workspaces`(工作区数量)。 ## 工具 | 工具 | 说明 | |---|---| | `execute_code` | 执行代码(shell/python/nodejs/go),返回结构化结果;支持 stdin、workspace、timeout_ms | | `list_files` | 列出指定工作区内的文件(非递归) | | `read_file` | 读取指定工作区内文件内容(上限 1MB) | | `write_file` | 向指定工作区写入文件(自动创建父目录) | | `runtime_info` | 返回执行环境信息:各语言版本与常用工具(git/curl/pip/npm/make/gcc 等)可用性 | ### 文件传输与管理 REST API(`/api`,与 MCP 端点共享鉴权) 大文件/二进制不经过 MCP JSON-RPC 通道,走专用 HTTP 文件通道(上传上限 100MB,与 MCP 4MB 请求体限制解耦): | 端点 | 说明 | |---|---| | `POST /api/workspaces/:ws/files/*path` | 上传:multipart 多文件(path 尾斜杠=目录)或原始 body 二进制 | | `GET /api/workspaces/:ws/files/*path` | 下载;`?preview=1` 文本/图片白名单在线预览(强制 nosniff/CSP 安全头,SVG 强制下载) | | `DELETE /api/workspaces/:ws/files/*path` | 删除文件或空目录 | | `GET /api/workspaces/:ws/tree` | 单层目录树(`?path=` 下钻,带大小/类型/修改时间) | | `GET /api/workspaces` | workspace 列表 + 递归容量统计 | | `POST /api/workspaces/:ws/rename` | 重命名/移动(`{"from":"a.txt","to":"b/c.txt"}`,目标父目录自动创建) | | `GET /api/admin/stats` | 管理统计:执行总数/失败/超时/语言分布 + 运行水位 + 资源限制 | | `GET /api/auth/verify` | 校验 Bearer API Key(登录页使用,错误返回 401) | | `GET /admin` | Web 管理后台(Vue 3 SPA,`go:embed` 嵌入):API Key 登录页、统计卡片、workspace 容量、目录树浏览、预览/下载/删除 | ```bash # 上传数据集 → 执行分析 → 下载结果 curl -X POST "http://localhost:8080/api/workspaces/demo/files/data.csv" \ -H "Authorization: Bearer $KEY" --data-binary @data.csv # ... execute_code 分析 ... curl "http://localhost:8080/api/workspaces/demo/files/result.png" \ -H "Authorization: Bearer $KEY" -o result.png ``` > 文件 API 完整复用安全链:鉴权中间件 + 词法校验 + `openat`/`O_NOFOLLOW` 原子打开,上传/下载/删除均无符号链接逃逸与 TOCTOU 窗口。 ### Resources(资源,`resources/list` + `resources/read`) | 资源 URI | 说明 | |---|---| | `code-exec://info` | 静态资源:服务版本、支持语言、预装依赖等简介 | | `ws://{workspace}/{+path}` | 动态资源模板:读取持久化工作区中的文件(如 `ws://demo/src/app.py`,`{+path}` 支持子目录),MIME 按扩展名推断,路径校验防穿越 | ### Prompts(提示词模板,`prompts/list` + `prompts/get`) | 提示词 | 参数 | 说明 | |---|---|---| | `review-code` | `workspace`、`path` | 读取工作区文件并渲染"代码审查"提示词(正确性/可读性/健壮性/安全性四个维度) | | `write-and-run-tests` | `workspace`、`path`、`language`(可选) | 渲染"编写单元测试并用 execute_code 运行验证"的多步工作流提示词 | ### execute_code 参数 | 参数 | 类型 | 必填 | 说明 | |---|---|---|---| | `language` | string | ✅ | `shell` / `python` / `nodejs` / `go` | | `code` | string | ✅ | 要执行的代码 | | `stdin` | string | ❌ | 可选:向被执行程序注入的标准输入 | | `workspace` | string | ❌ | 可选:持久化工作区名(字母数字/下划线/短横线,≤64 字符)。代码在该目录执行且目录保留,可跨多次调用共享文件 | | `timeout_ms` | number | ❌ | 超时毫秒数,默认 30000,上限 120000 | > 提示:`execute_code` 的源文件使用唯一随机文件名,不会覆盖工作区内用户创建的同名文件(如 `main.py`)。可配合 `write_file`/`read_file` 形成"写文件 → 执行 → 检查输出"的工作流。 工具结果包含两部分: 1. `content`(人类可读文本摘要):包含 `exit code`、`duration`、`timedOut`、`truncated` 及 `--- stdout ---` / `--- stderr ---`。 2. `structuredContent`(结构化 JSON,供程序解析): ```json { "language": "python", "exitCode": 0, "stdout": "hi 5\n", "stderr": "", "durationMs": 11, "timedOut": false, "truncated": false } ``` ## 快速开始(Docker) ```bash # 方式一:docker run docker build -t code-exec-mcp:1.1.0 . docker run -d --name code-exec-mcp -e API_KEY=your-key -p 8080:8080 code-exec-mcp:1.1.0 # 方式二:docker compose(推荐,含环境变量集中配置 + workspace 卷持久化) cp .env.example .env # 修改 .env 中的 API_KEY 等配置 docker compose up -d --build ``` MCP 端点:`http://localhost:8080/mcp` > Compose 部署会挂载命名卷 `code-exec-workspaces` 到 `/tmp/workspaces`,容器重建后工作区文件不丢失。 ### 客户端配置示例 Claude / 其他 MCP 客户端(Streamable HTTP): ```json { "mcpServers": { "code-exec": { "type": "http", "url": "http://localhost:8080/mcp" } } } ``` ## 快速开始(本地运行) 依赖本机已安装 Go 1.24+、python3、node、sh: ```bash go run . # 或 go build -o code-exec-mcp . && PORT=8080 ./code-exec-mcp ``` ### 运行测试 ```bash go test ./... ``` 包含执行器单元测试(超时/内存/CPU 限制、进程组清理、环境隔离、workspace、文件工具防穿越与符号链接逃逸)、文件工具权限控制测试、符号链接攻击端到端集成测试、文件 API 测试(上传/下载/预览/穿越拒绝/鉴权/大小上限)与 gin 路由集成测试(CORS/鉴权/请求体限制/healthz/tools),以及 Resources/Prompts 完整交互流程测试,共 38 个测试用例。 ## 手动调用示例 > 若设置了 `API_KEY`,需在请求头携带 `Authorization: Bearer `。 ```bash # 初始化 curl -s -X POST http://localhost:8080/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'Authorization: Bearer ' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"0"}}}' # 执行 Python 代码 curl -s -X POST http://localhost:8080/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'Authorization: Bearer ' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"execute_code","arguments":{"language":"python","code":"print(6*7)"}}}' # 查询运行时环境(语言版本与工具可用性) curl -s -X POST http://localhost:8080/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'Authorization: Bearer ' \ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"runtime_info","arguments":{}}}' # 向工作区写入文件并执行 curl -s -X POST http://localhost:8080/mcp \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"write_file","arguments":{"workspace":"demo","path":"main.py","content":"print(\"hi\")"}}}' ``` ## Docker 镜像内容 两阶段构建: 1. **builder**(`golang:1.25-alpine`):静态编译二进制(`GOPROXY=goproxy.cn`) 2. **runtime**(`alpine:latest`):内置 `go` / `python3` / `nodejs` / `bash` 运行时及常用工具(`git`、`curl`、`wget`、`make`、`gcc`、`jq`、`openssl`、`sqlite3`),预装依赖 **Python:requests / numpy / pandas,Node:typescript(tsc)**,非 root 用户 `app` 运行 > Compose 部署默认挂载卷:`code-exec-workspaces`(持久化工作区)、`code-exec-gopath`(Go 模块缓存,加速依赖下载)。 ### 国内镜像源配置 | 环境 | 镜像源 | |---|---| | apk(系统包) | 清华 `mirrors.tuna.tsinghua.edu.cn` | | Go 模块(构建 + 运行时 `go run`) | `goproxy.cn` | | pip | 清华 `pypi.tuna.tsinghua.edu.cn`(含 `break-system-packages`) | | npm | `registry.npmmirror.com`(清华不提供 npm registry 镜像) | 运行时安装依赖(`pip install`、`npm install`、`go get`)均自动走国内源。Compose 部署默认挂载 Go 模块缓存(`/home/app/go`),pip 用户包目录为 `/home/app/.local`。 ## 环境变量 | 变量 | 默认值 | 说明 | |---|---|---| | `PORT` | `8080` | HTTP 监听端口 | | `CORS_ORIGINS` | `*` | 允许的跨域来源,逗号分隔,如 `http://10.0.2.55:1234,http://localhost:3000` | | `MAX_CONCURRENT` | `4` | 并发执行子进程数上限(信号量),防止并发过高耗尽 CPU/内存 | | `MEMORY_LIMIT_MB` | `2048` | 单个子进程的虚拟内存上限(`ulimit -v`),设 `0` 关闭 | | `CPU_LIMIT_SECONDS` | `30` | 单个子进程的 CPU 时间上限(`ulimit -t`),防死循环占满核,设 `0` 关闭 | | `API_KEY` | 空(关闭鉴权) | 设置后启用 Bearer Token 鉴权,客户端需携带 `Authorization: Bearer `(`/healthz` 除外) | | `MAX_BODY_MB` | `4` | 请求体大小上限(MB),超限返回 413 | | `WORKSPACE_MAX_AGE_HOURS` | `24` | 持久化工作区最长保留时长(小时),超时启动时清理,`0` 不清理 | | `TRANSPORT` | `http` | 传输模式:`http`(Streamable HTTP)或 `stdio`(本地单客户端) | | `LOG_FORMAT` | `text` | 日志格式:`text` 或 `json`(便于日志采集) | | `GIN_MODE` | `release` | gin 运行模式(可设为 `debug`) | ## 其他特性 - **优雅关闭**:收到 `SIGTERM`/`SIGINT` 后停止接收新请求,等待进行中的执行完成(最多 10s)后退出。 - **并发限制**:`execute_code` 通过信号量限制同时执行的子进程数(含 go 编译阶段),超出的请求排队等待。 - **内存限制**:每个子进程通过 `ulimit -v` 限制虚拟内存(默认 2GB),防 OOM 拖垮容器。 - **CPU 时间限制**:每个子进程通过 `ulimit -t` 限制 CPU 时间(默认 30s),防死循环/CPU 炸弹占满核;不耗 CPU 的等待(如 `sleep`)不受影响。被资源限制杀死的进程会在 stderr 中附加原因(如 `[terminated] ...`),便于诊断。 - **超时进程组清理**:子进程以独立进程组(`Setpgid`)运行,超时杀整个进程组(`Kill(-pgid)`),shell 脚本派生的后台子进程(如 `sleep 999 &`)不会残留绕过超时。 - **workspace 持久化**:传 `workspace` 参数后代码在 `/tmp/workspaces/` 中执行且目录保留,可跨多次调用共享文件(如"写文件 → 编译 → 运行"工作流);名称白名单校验,防路径穿越;启动时自动清理超过 `WORKSPACE_MAX_AGE_HOURS` 的过期目录。 - **符号链接逃逸防护**:所有文件操作(工具层 + 资源层)基于 `openat` + `O_NOFOLLOW` 逐级打开,内核级拒绝符号链接组件,无 TOCTOU 竞态窗口;错误路径 fd 由 defer 链保证关闭,无泄漏。 - **请求体大小限制**:超过 `MAX_BODY_MB`(默认 4MB)的请求返回 413,防超大 payload 耗尽内存。 - **环境隔离**:子进程只继承白名单内的环境变量(`HOME`/`PATH`/`GOPROXY` 等),`API_KEY` 等服务密钥不会泄露给被执行的代码。 - **鉴权**:设置 `API_KEY` 后,所有 `/mcp` 请求必须携带 `Authorization: Bearer `(常量时间比较,防时序侧信道)。 - **审计日志**:每次执行输出结构化日志(slog):语言、退出码、耗时、是否超时/截断、代码长度、工作区及代码摘要(前 200 字符);失败/超时以 `WARN` 级别记录;`LOG_FORMAT=json` 可输出 JSON 格式。 - **stdio 模式**:`TRANSPORT=stdio code-exec-mcp` 通过标准输入/输出提供 JSON-RPC 服务,可直接接入本地 MCP 客户端(如 Claude Desktop): ```json { "mcpServers": { "code-exec": { "command": "docker", "args": ["run", "-i", "--rm", "code-exec-mcp:1.1.0"], "env": { "TRANSPORT": "stdio" } } } } ``` ## 安全提示 ⚠️ 本服务会执行任意代码,仅适合在受信任、隔离的环境中使用(如本地开发、沙箱容器)。生产使用前建议: - 限制容器资源(`--memory` / `--cpus`) - 网络隔离(`--network none` 或自定义网络策略) - 必须设置 `API_KEY` 开启鉴权,并配合 HTTPS 传输 ## Roadmap - [x] 并发执行队列与资源限制(信号量,`MAX_CONCURRENT`) - [x] 优雅关闭(SIGTERM/SIGINT) - [x] 执行审计日志与鉴权(slog + `API_KEY` 常量时间比较) - [x] stdio 传输模式支持(`TRANSPORT=stdio`) - [x] 会话状态管理(有状态 Streamable HTTP 会话,`Mcp-Session-Id`) - [x] 常用依赖预装(Python:requests / numpy / pandas;Node:typescript) - [x] MCP 三原语(Tools / Resources / Prompts) - [x] 文件工具符号链接逃逸防护(`openat` + `O_NOFOLLOW` 原子打开,无 TOCTOU) - [x] 超时进程组清理(`Setpgid` + 杀负进程组,无孤儿进程) - [x] 文件上传/下载 REST API(二进制通道,100MB 配额,预览安全头) - [x] workspace 文件管理(目录树/重命名/删除/容量统计) - [x] Web 管理后台(`/admin`,执行统计 + 文件浏览) - [ ] per-workspace 存储配额 - [ ] 打包下载(zip) - [ ] 多 API_KEY 租户隔离 ## 版本 当前版本 **1.1.0**(2026-08-25),完整变更历史见 [CHANGELOG.md](CHANGELOG.md)。