Code editor MCP server with sandboxing, optimistic locking, and path whitelists.
Project description
code-editor
面向多客户端并发、安全沙箱、编码感知的代码编辑 MCP 服务器。
路径访问更新:所有参数必须是绝对路径。除
read_files外,其余文件/目录工具仍校验是否落在允许目录列表内。CODE_EDIT_ROOT只是安全标记,不做访问边界或相对路径解析;访问新目录请用config_ops(action="set_root", path="...")加入允许列表。
安装与运行
# 安装(pip 或 uv 均可,包名 code-editor-mcp)
pip install code-editor-mcp # 或 uv pip install code-editor-mcp
# 更新最新版
pip install -U code-editor-mcp
# 直接启动 CLI 入口
code-editor
# 若从源码运行
uv sync
uv run python server.py
关键环境变量:
CODE_EDIT_ROOT:安全标记(默认启动时的 CWD),不做访问边界,也不参与相对路径解析。CODE_EDIT_ALLOWED_ROOTS_FILE:允许目录持久化 JSON,默认tools/.code_edit_roots.json。CODE_EDIT_ALLOWED_DIRECTORIES:逗号分隔允许目录列表(兼容旧的CODE_EDIT_ALLOWED_ROOTS)。
环境变量一览:
| 变量 | 作用 | 默认值 |
|---|---|---|
CODE_EDIT_ROOT |
安全标记(非访问边界,不做路径解析) | 当前工作目录 |
CODE_EDIT_ALLOWED_ROOTS_FILE |
允许目录持久化文件 | tools/.code_edit_roots.json |
CODE_EDIT_ALLOWED_DIRECTORIES (兼容 CODE_EDIT_ALLOWED_ROOTS) |
额外允许目录(逗号分隔) | 空 |
CODE_EDIT_FILE_READ_LINE_LIMIT |
(legacy)内部 read_file 默认行数上限;read_files v2 使用 page_line_count/max_lines_per_snippet 控制输出 |
1000 |
CODE_EDIT_FILE_WRITE_LINE_LIMIT |
file_ops 写入行数警戒 |
50 |
设计要点
- 允许目录列表:默认允许用户主目录;路径需落在允许目录内,否则拒绝。
config_ops(action="set_root")仅将目录加入允许列表并更新安全标记,不做路径拼接。 - 持久允许目录:
config_ops(action="set_root")成功后写入 JSON,可跨会话复用。 - 乐观锁:写/删类支持秒或纳秒级
expected_mtime,10ms 容忍;写入走原子写避免部分落盘。 - 默认忽略:
.git、__pycache__、node_modules、.DS_Store、.env*、.venv、*.log、*.pem;ignore_patterns传空字符串/空列表可关闭默认忽略。 - 编码感知:
read_filesv2 默认逐文件auto检测/复用编码(基于最新 mtime 刷新的元信息缓存);如自动结果乱码,可在每个 request 里显式传encoding覆盖。 - 安全删除:禁止删除当前根/其祖先/关键系统目录。
MCP 工具(code-editor)
文件系统工具
| 名称 | 工具 | 功能 | 主要参数/说明 | 常见误用 |
|---|---|---|---|---|
config_ops |
config_ops(action, path=None) |
允许目录管理 + 元信息 | action=set_root/list_roots/get_info;path 仅 set_root/get_info 需要 |
拼错 action;误以为 list_roots 需要 path |
read_files |
read_files(requests, default_encoding="auto", page_line_count=400, max_snippets=64, max_total_chars=200000, max_chars_per_snippet=50000, max_lines_per_snippet=2000, parallelism=4) |
LLM 友好读取:多文件/同文件多片段 + 分页 | requests 为 list[dict]:支持 mode=range/tail/all;行号 1-based;可用 cursor 继续分页;返回结构化 results[],每项含 content(纯内容)、start_line/end_line、core_*、encoding、sha256_utf8、可选 next_cursor;encoding 支持 auto/utf-8/gbk/gb2312/gb18030;仅此工具允许读取任意绝对路径,不受允许目录白名单限制 |
继续传旧的 file_paths;把 start_line 当 0-based;一次请求太多片段触发 max_total_chars;对图片用 range/tail |
dir_ops |
`dir_ops(action, dir_path, depth=2, format="tree" | "flat", ignore_patterns=None, max_items=1000, expected_mtime=None, confirm_token=None, allow_nonempty=None, approval_token=None)` | 统一目录操作 | action=create/list/delete;绝对路径;list: tree 返回字符串列表、flat 返回字典列表;ignore_patterns 为 None 用默认忽略,空字符串/空列表关闭默认忽略;flat 下 max_items 限制返回条数;delete: 必须提供 expected_mtime、confirm_token、allow_nonempty,confirm_token=delete:<normalized_abs_path>(Path.resolve + os.path.normcase);高风险删除会返回结构化 needs_approval 并要求 approval_token 二次提交 |
file_ops |
file_ops(action, file_path=None, content=None, source_path=None, destination_path=None, expected_mtime=None, encoding="utf-8", approval_token=None) |
综合文件操作:write/append/copy/move/delete | 所有路径必须绝对且在允许目录内;write 覆盖、append 追加;write/append 需 file_path+content;copy/move 需 source_path+destination_path;delete 需 file_path;encoding 仅写入使用;expected_mtime:写/删校验目标文件,拷贝/移动校验源文件;删除返回结构化结果 | action 不支持或参数缺失;copy 目标已存在;delete 目标是目录 |
convert_file_encoding |
convert_file_encoding(file_paths, source_encoding, target_encoding, error_handling="strict", mismatch_policy="warn-skip") |
批量转码并覆盖写回 | 绝对路径列表;utf-8/gbk/gb2312/gb18030;错误处理 strict/replace/ignore;编码检测( charset-normalizer ),策略 fail-fast / warn-skip(默认) / force;结果返回 detectedEncoding/Confidence/mismatch;内置别名兼容 utf8/utf_8/utf-8-sig/ascii/cp936/gb-2312/gb-18030 | 相对路径;二进制文件;未在白名单 |
dir_ops 参数要点(避免误调用)
create:仅dir_path必须;其余参数会被忽略。list:format仅支持tree/flat;max_items必须为正整数或 None。delete:必须同时提供expected_mtime、confirm_token、allow_nonempty(显式 True/False)。- 删除行为统一移动到回收站,不支持永久删除。
- 命中高风险规则时会返回
status=needs_approval,响应内包含approval_token与llm_instruction。 - 盘符根目录(如
C:\/D:\/E:\)会被永久阻断并返回status=blocked_critical。 confirm_token生成规则(严格匹配):
from pathlib import Path
import os
normalized = os.path.normcase(str(Path(dir_path).resolve()))
confirm_token = f"delete:{normalized}"
代码精准编辑工具
| 名称 | 工具 | 功能 | 主要参数/说明 | 常见误用 |
|---|---|---|---|---|
edit_blocks |
edit_blocks(edits, error_policy="fail-fast", encoding="auto") |
单编辑/批量编辑:搜索替换 | edits 支持 dict(单编辑,支持大文件)或 list[dict](批量,支持同文件多处与多文件);每个 edit 必须有 file_path(注意不是 path);支持 encoding="auto" 自动检测;批量模式支持 error_policy: fail-fast/continue/rollback |
把 read_files 的 path 当成 file_path;传了不支持的编码;批量模式传空列表;大文件用批量模式(应改为单编辑 dict) |
edit_blocks 返回格式(v0.3.0+)
edit_blocks 返回结构化数据,包含修改位置信息便于验证:
# edit_blocks(单编辑)返回示例
{
"status": "success",
"message": "Applied 2 edit(s) to /path/file.py (lines 10-12, 20-25)",
"file_path": "/path/file.py",
"replacements": 2,
"locations": [
{"start_line": 10, "end_line": 12, "start_col": 5, "end_col": 20},
{"start_line": 20, "end_line": 25, "start_col": 1, "end_col": 15}
]
}
# edit_blocks(批量)返回示例
{
"status": "success", # success/partial/error
"message": "Completed 3/3 edits",
"total_edits": 3,
"successful_edits": 3,
"failed_edits": 0,
"results": [
{"status": "success", "file_path": "...", "replacements": 1, "locations": [...], "message": "..."},
{"status": "success", "file_path": "...", "replacements": 1, "locations": [...], "message": "..."},
{"status": "success", "file_path": "...", "replacements": 1, "locations": [...], "message": "..."}
]
}
使用示例
- 查看并新增允许目录:
config_ops(action="list_roots")→ 若未包含目标,调用config_ops(action="set_root", path="/data/project")。 - 带锁写入:
info = config_ops(action="get_info", path="/abs/path/src/app.py")→file_ops(action="write", file_path="/abs/path/src/app.py", content=content, expected_mtime=info["modified"])。 - 精确替换(单编辑):
edit_blocks(edits={"file_path":"/abs/path/src/app.py","old_string":"old","new_string":"new","expected_replacements":1,"expected_mtime":info["modified"]})。 - 单文件片段读取(range,行号 1-based):
read_files([
{"path": "/abs/path/src/app.py", "mode": "range", "start_line": 10, "line_count": 60}
])
- 同文件多片段 + 跨文件(一次拿齐,减少多次调用):
read_files([
{"path": "/abs/path/src/app.py", "mode": "range", "start_line": 1, "line_count": 80, "id": "app_head"},
{"path": "/abs/path/src/app.py", "mode": "range", "start_line": 200, "line_count": 80, "id": "app_mid"},
{"path": "/abs/path/README.md", "mode": "range", "start_line": 1, "line_count": 120, "id": "readme"},
], max_total_chars=200000, parallelism=4)
- 分页读取整文件(all + cursor):
resp1 = read_files([{"path": "/abs/path/src/app.py", "mode": "all"}], page_line_count=400)
cursor = resp1["results"][0].get("next_cursor")
resp2 = read_files([{"cursor": cursor}], page_line_count=400) # 继续下一页
- 批量编辑(同文件多处修改):
edit_blocks(edits=[
{"file_path": "/abs/app.py", "old_string": "foo", "new_string": "bar"},
{"file_path": "/abs/app.py", "old_string": "hello", "new_string": "world"},
], error_policy="rollback")
- 批量编辑(多文件):
edit_blocks(edits=[
{"file_path": "/abs/a.py", "old_string": "v1", "new_string": "v2"},
{"file_path": "/abs/b.py", "old_string": "v1", "new_string": "v2"},
], error_policy="continue")
- 批量转码:
convert_file_encoding(["/abs/a.txt", "/abs/b.txt"], "gb2312", "utf-8", error_handling="replace", mismatch_policy="warn-skip")。 - 列目录(扁平):
dir_ops(action="list", dir_path="/abs/path", format="flat", ignore_patterns=[".git", "node_modules"])。 - 删除目录(显式确认,统一进回收站):
info = config_ops(action="get_info", path="/abs/path")→normalized = os.path.normcase(str(Path("/abs/path").resolve()))→token = f"delete:{normalized}"→dir_ops(action="delete", dir_path="/abs/path", expected_mtime=info["modified"], confirm_token=token, allow_nonempty=True)。 - 高风险删除二次审批:首次调用 delete 可能返回
status=needs_approval+approval.approval_token,拿到用户明确同意后,带上approval_token再次调用同一 delete。
MCP 客户端快速配置示例
{
"mcpServers": {
"code-editor": {
"command": "code-editor",
"env": {
"CODE_EDIT_ROOT": "."
}
}
}
}
[mcp_servers.code-editor]
command = "code-editor"
# 将工作目录指向当前项目,使 CODE_EDIT_ROOT 默认跟随启动时的 CWD
cwd = "."
startup_timeout_sec = 120
安全/行为提示
- 路径验证:除
read_files外,其他操作要求绝对路径且落在允许目录列表内;read_files仅要求绝对路径;CODE_EDIT_ROOT仅为安全标记。 - 删除防护:删除只允许移动到回收站;任何失败都会直接拒绝(不会降级为永久删除)。
- 盘符根目录阻断:
C:\、D:\、E:\(及其他文件系统根目录)会被永久阻断,并返回极高风险警告。 - 二次审批:仅“重要目录 + 大规模删除”触发;返回会强制提示模型先询问用户并等待批准。
- 允许目录管理:如需访问新路径,先
config_ops(action="set_root")加入白名单;不在白名单的绝对路径会被拒绝。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file code_editor_mcp-0.2.5.tar.gz.
File metadata
- Download URL: code_editor_mcp-0.2.5.tar.gz
- Upload date:
- Size: 37.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b6c2c2b80a1da48e29a2b8b5c2eef6c17cf7df17bd5a6f90fd137bcdb1e0133
|
|
| MD5 |
822fed04c21c4f804331a81868784372
|
|
| BLAKE2b-256 |
aca8584176a30eaaa82ff1f4f43f2266de4546a2e306533be93d01964f7f1a3c
|
File details
Details for the file code_editor_mcp-0.2.5-py3-none-any.whl.
File metadata
- Download URL: code_editor_mcp-0.2.5-py3-none-any.whl
- Upload date:
- Size: 41.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed1f8f81bda824f9025bce65a1e8af767d479866e4546f787500e02b46ce8f44
|
|
| MD5 |
fd5a48e2de9f41723ca5bdb10a61e5d8
|
|
| BLAKE2b-256 |
8f66308d9d1e25d491cbe2f533df878e3c33e5e26d6565c6d26400291d8175ed
|