jupyter_notebook_edit_mcp
服务介绍
Jupyter Notebook Editor MCP Server
一个基于 Rust 的 Jupyter Notebook(.ipynb)文件编辑 MCP(Model Context Protocol)服务器,通过直接操作 JSON AST 安全修改笔记本内容,完整保留文件结构。
🚀 Trae IDE 用户:直接下载编译好的二进制即可使用!详见 Trae IDE 集成 部分。
功能特性
- 安全编辑:通过
serde_json直接操作 JSON 抽象语法树,绝不进行文本替换,保证.ipynb的 JSON 格式不被破坏 - 完整编辑能力:
- 读取笔记本结构和所有单元格内容
- 编辑指定单元格的源代码
- 添加、删除、移动单元格
- 编辑单元格元数据和笔记本级元数据
- 清除代码单元格的输出和 execution_count
- MCP 协议支持:作为 Model Context Protocol 服务器运行,兼容 AI IDE
- 行分隔 JSON-RPC 2.0:一行一个 JSON-RPC 请求/响应,天然避开 Windows CRLF 问题
技术架构
MCP Client (如 Trae IDE)
↓ 行分隔 JSON-RPC 2.0
Jupyter Notebook Editor MCP Server
↓ serde_json AST 操作
.ipynb 文件 (JSON 格式)
快速开始
🔧 构建与运行
# 克隆并进入项目
cd jupyter_notebook_edit_mcp
# 构建 release 版本
cargo build --release
# 运行(交互式 STDIO 模式)
./target/release/jupyter_notebook_edit_mcp
🎯 Trae IDE 集成
- 构建二进制:
cargo build --release - 找到二进制路径:通常位于
./target/release/jupyter_notebook_edit_mcp.exe(Windows)或./target/release/jupyter_notebook_edit_mcp(macOS/Linux) - 打开 Trae IDE,点击左侧边栏的 MCP 图标(锤子+插头)
- 添加 MCP 服务器,点击"新建",粘贴(注意替换为实际路径):
{
"mcpServers": {
"jupyter-notebook-editor": {
"command": "/absolute/path/to/target/release/jupyter_notebook_edit_mcp.exe",
"args": []
}
}
}
Windows 示例:
"command": "D:/projects/jupyter_notebook_edit_mcp/target/release/jupyter_notebook_edit_mcp.exe"
macOS/Linux 示例:"command": "/home/user/projects/jupyter_notebook_edit_mcp/target/release/jupyter_notebook_edit_mcp"
- 保存后重启 Trae,如果 MCP 图标上出现绿色圆点即表示连接成功
- 在聊天界面直接说:
- "帮我读取这个 notebook 的内容"
- "把第一个单元格的内容改成 ..."
- "在最后添加一个 markdown 单元格"
- "删除第二个单元格"
- "清除所有代码单元格的输出"
可用工具
读取
| 工具 | 描述 | 必填参数 | 可选参数 |
|---|---|---|---|
read_notebook |
读取 .ipynb 文件,列出所有单元格及其内容、类型、元数据 |
file_path |
— |
编辑
| 工具 | 描述 | 必填参数 | 可选参数 |
|---|---|---|---|
edit_cell_content |
替换指定单元格的 source 内容,保留类型和元数据 | file_path, index, source |
— |
edit_cell_metadata |
替换指定单元格的 metadata 对象 | file_path, index, metadata |
— |
edit_notebook_metadata |
替换笔记本级 metadata 对象 | file_path, metadata |
— |
结构操作
| 工具 | 描述 | 必填参数 | 可选参数 |
|---|---|---|---|
add_cell |
在指定索引添加新单元格,不指定则追加到末尾 | file_path, cell_type, source |
index |
delete_cell |
按索引删除单元格 | file_path, index |
— |
move_cell |
将一个单元格从一个索引移到另一个 | file_path, from, to |
— |
delete_outputs |
清除所有(或指定)code 单元格的输出和 execution_count | file_path |
index |
参数说明
file_path:文件路径(绝对路径或相对于工作目录的相对路径),如D:/projects/notebook.ipynbindex:单元格从 0 开始计数的索引source:单元格源代码(字符串)cell_type:可选值code、markdown、rawmetadata:JSON 对象,替换原有的元数据from/to:移动操作的原索引和目标索引
示例
读取 notebook
// 请求(一行 JSON)
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_notebook","arguments":{"file_path":"D:/project/analysis.ipynb"}}}
// 响应
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n \"file_path\": \"D:/project/analysis.ipynb\",\n \"nbformat\": 4,\n \"nbformat_minor\": 5,\n \"metadata\": {...},\n \"cell_count\": 5,\n \"cells\": [...]\n}"}],"isError":false}}
编辑单元格内容
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"edit_cell_content","arguments":{"file_path":"D:/project/analysis.ipynb","index":0,"source":"import pandas as pd\nimport numpy as np\n\nprint('done')"}}}
添加 markdown 单元格
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"add_cell","arguments":{"file_path":"D:/project/analysis.ipynb","cell_type":"markdown","source":"## 分析结果\n\n以下是数据集的统计摘要。","index":2}}}
删除单元格
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"delete_cell","arguments":{"file_path":"D:/project/analysis.ipynb","index":3}}}
移动单元格
{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"move_cell","arguments":{"file_path":"D:/project/analysis.ipynb","from":4,"to":1}}}
清除所有输出
{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"delete_outputs","arguments":{"file_path":"D:/project/analysis.ipynb"}}}
清除指定单元格输出
{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"delete_outputs","arguments":{"file_path":"D:/project/analysis.ipynb","index":0}}}
编辑单元格元数据
{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"edit_cell_metadata","arguments":{"file_path":"D:/project/analysis.ipynb","index":0,"metadata":{"tags":["analysis"],"collapsed":false}}}}
在 Trae IDE 中使用自然语言
在 Trae IDE 的聊天界面,可以直接说:
"帮我把 notebook.ipynb 的第一个代码单元格改成 import torch"
"在这个 notebook 最后加一个 markdown 单元格,写一些说明"
"删除第三个单元格"
"把所有代码单元格的输出清掉,文件太大了"
"把这个单元格从位置 2 移到位置 0"
Trae 会自动将这些自然语言转化为对应的 tools/call 请求。
协议说明
本服务器使用 行分隔 JSON-RPC 2.0 协议,而非标准的 Content-Length 帧协议。每一行标准输入读取一个完整的 JSON-RPC 请求(用 read_line()),每一行标准输出写入一个完整的 JSON-RPC 响应(用 println!())。
选择这个协议的原因:
- 天然避开 Windows 下
\n→\r\n自动转换导致的 Content-Length 计算问题 - 无需调用
_setmode等平台特定 FFI - 与
docx-mcp-rust保持一致,经过生产验证
生命周期
- Trae 发送
initialize→ 服务器返回能力声明(必须含"jsonrpc":"2.0") - Trae 发送
notifications/initialized→ 服务器静默忽略(无响应) - Trae 发送
tools/list→ 服务器返回工具列表 - Trae 发送
tools/call→ 服务器调用对应工具并返回结果 - 支持
ping→ 返回{}
项目结构
jupyter_notebook_edit_mcp/
├── src/
│ ├── lib.rs # 核心库:协议类型、工具实现、请求分发
│ └── main.rs # 二进制入口:STDIO 事件循环
├── tests/
│ ├── tests.rs # 单元测试(59 个,覆盖所有工具函数和边界)
│ └── mcp_protocol_test.rs # 集成测试(7 个,使用 mcp-protocol-sdk 连接真实服务器)
├── Cargo.toml # 项目配置
├── AGENTS.md # AI 辅助开发指引
└── README.md # 项目文档
开发
运行测试
# 全部测试(66 个)
cargo test
# 仅单元测试
cargo test --test tests
# 仅 MCP 协议集成测试
cargo test --test mcp_protocol_test
# 手动测试:通过管道发送请求
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | ./target/release/jupyter_notebook_edit_mcp
代码风格
- 工具函数:
tool_<snake_case_name>,接收&Map<String, Value>,返回anyhow::Result<Value> - 辅助函数:
get_*/set_*/validate_*/extract_*,操作 serde_json Value - 核心业务逻辑在
lib.rs中,main.rs仅为 STDIO 事件循环
依赖
serde— JSON 序列化/反序列化serde_json— JSON AST 操作anyhow— 错误处理tokio— 异步运行时(仅在集成测试中使用)mcp-protocol-sdk— MCP 客户端 SDK(仅在集成测试中使用)
许可证
Apache-2.0
贡献
欢迎提交 Issue 和 Pull Request!
贡献指南
- Fork 本仓库 https://gitee.com/awol2010ex/jupyter_notebook_edit_mcp
- 创建功能分支(
git checkout -b feature/amazing-feature) - 提交更改(
git commit -m 'Add some amazing feature') - 推送到分支(
git push origin feature/amazing-feature) - 打开 Pull Request