内容
* emacs-jail-mcp
#+BEGIN: om-readme-toc
- [[#概要][概要]]
- [[#安装][安装]]
- [[#配置][配置]]
- [[#使用][使用]]
- [[#开发][开发]]
- [[#致谢][致谢]]
- [[#历史][历史]]
- [[#作者][作者]]
- [[#许可证][许可证]]
#+END:
** 概要
:PROPERTIES:
:CUSTOM_ID: synopsys
:END:
~emacs-jail-mcp~ 是一个 [[https://modelcontextprotocol.io/][MCP 服务器]],它在可丢弃的 [[https://podman.io/][podman]] 容器中运行 Emacs,并具有对主机文件系统的 *写入时复制访问*。
这使 LLM 可以访问 *您的开发环境副本*(Emacs、配置、项目文件),因此它可以高效地调试问题,但不能破坏任何内容。
https://github.com/user-attachments/assets/27440d9e-46e0-4976-96b5-8f97834857de
/emacs-jail-mcp 在左侧运行,opencode 在右侧运行。它被赋予了修复 LSP 符号搜索在特定项目中失败的任务。它启动了沙箱,打开了项目,初始化了 LSP 与 clangd,复制了错误,测试了修复,然后将其应用于 Emacs 配置。/
*** 什么是?
这是这个想法的实现:
1. AI 代理(例如 OpenCode)连接到 ~emacs-jail-mcp~ 并与 Emacs 实例交互。
2. 在 LLM 控制下的 Emacs 实例可以访问您的所有文件,但任何文件修改都保留在可丢弃的 jail 中,不会反映在主机上。
3. 它在虚拟 X 服务器下运行,并加载您的通常 Emacs 配置,以便 LLM 可以重现真实的编辑器行为。它还可以截取屏幕截图。
4. 您可以给它诸如以下的任务:“当我在项目 Y 中打开文件 X 并按 ~C-c C-j~ 时,代码导航不起作用。” LLM 可以使用您的确切配置和项目重现问题,然后在 jail 中测试修复。
5. 您不必担心它会干扰您的活动 Emacs 会话或修改主机上的项目。只需让它在后台运行,直到它找到修复(或乱七八糟的东西)。
*** 如何实现?
Go MCP 服务器接受来自 LLM 客户端的请求,并公开 MCP “工具” - 可调用的函数来启动/停止 jail、评估 Elisp、读取 Emacs 日志等。
当 jail 启动时,它会创建一个 Podman 容器,该容器具有对完整主机文件系统的写入时复制访问权限。该容器以当前用户身份运行,并共享主机的网络、主机名、IPC 等。
在 jail 中,Emacs 在 Xvfb 下运行,加载用户的正常 Emacs 配置,并启动捆绑的 Elisp JSON-RPC 服务器。Go MCP 服务器通过 Unix 套接字与 JSON-RPC 服务器通信。
#+BEGIN_HTML
<img src="./doc/architecture.drawio.png" width="80%"/>
#+END_HTML
*** 功能
命令行工具:
- MCP 服务器
- MCP 客户端(从命令行访问所有提供的 MCP 工具)
MCP 传输:
- TCP/SSE 传输,用于连接到 ~http://127.0.0.1:9421/sse~ 的客户端
- stdio 传输,用于客户端启动 ~emacs-jail-mcp serve --stdio~ 作为子进程
MCP 工具:
- ~emacs_jail_control~:启动、停止、重启或检查 jail 状态
- ~emacs_jail_logs~:读取 Emacs 消息和警告、初始化诊断和 stderr 日志
- ~emacs_jail_bytecomp~:编译 Elisp 文件并返回诊断信息
- ~emacs_jail_eval~:评估 Emacs Lisp 表达式
- ~emacs_jail_shell~:在 jail 容器中运行 shell 命令
- ~emacs_jail_screenshot~:捕获 Xvfb Emacs 显示屏的 PNG 图片
*** 限制
到目前为止,该项目仅支持我的个人设置:
- 仅限 Linux
- 需要 Podman
- 需要 X11(Xvfb)
- 需要 root 来启动容器
应该很容易实现其他容器或 OverlayFS 后端并添加 Wayland 支持。欢迎提交补丁!
** 安装
:PROPERTIES:
:CUSTOM_ID: installation
:END:
*** 前置条件
系统上应已安装:
- golang (>= 1.24)
- emacs (>= 27.1, 仅测试 >= 30.1)
- podman(用于容器)
- xvfb(用于虚拟 X 服务器)
- imagemagick(用于截图)
- sudo(可选)
对于基于 Debian 的发行版:
#+begin_src shell
sudo apt-get install golang podman xvfb imagemagick sudo
#+end_src
*** 构建和安装
这将下载并构建源代码,并将 =emacs-jail-mcp= 二进制文件放入 =$GOPATH/bin=(默认为 ~/go/bin):
#+begin_src shell
go install github.com/gavv/emacs-jail-mcp/cmd/emacs-jail-mcp@latest
#+end_src
添加到 PATH:
#+begin_src shell
export PATH="${GOPATH:-$HOME/go}:$PATH"
#+end_src
安装 shell 补全(可选):
#+begin_src shell
emacs-jail-mcp completion bash | sudo tee /etc/bash_completion.d/emacs-jail-mcp >/dev/null
#+end_src
安装手册页(可选):
#+begin_src shell
sudo mkdir -p /usr/local/share/man/man1
emacs-jail-mcp man | sudo tee /usr/local/share/man/man1/emacs-jail-mcp.1 >/dev/null
man emacs-jail-mcp
#+end_src
** 配置
:PROPERTIES:
:CUSTOM_ID: configuration
:END:
*** OpenCode
要使用已经运行的 TCP/SSE 服务器,请在 ~opencode.jsonc~ 中添加以下内容:
#+begin_src json
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"emacs_jail*": "allow"
},
"mcp": {
"emacs_jail": {
"type": "remote",
"url": "http://127.0.0.1:9421/sse"
}
}
}
#+end_src
单独启动服务器:
#+begin_src shell
emacs-jail-mcp serve
#+end_src
或者,让 OpenCode 通过 stdio 启动 ~emacs-jail-mcp~:
#+begin_src json
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"emacs_jail*": "allow"
},
"mcp": {
"emacs_jail": {
"type": "local",
"command": ["emacs-jail-mcp", "serve", "--stdio"]
}
}
}
#+end_src
*** Systemd
如果您使用 TCP/SSE 服务器,可以添加一个用户 systemd 单元,以自动在后台启动服务器:
#+begin_src systemd
# ~/.config/systemd/user/emacs-jail-mcp.service
[Unit]
Description=Emacs Jail MCP server
[Service]
ExecStart=%h/go/bin/emacs-jail-mcp serve
Restart=on-failure
[Install]
WantedBy=default.target
#+end_src
启用它:
#+begin_src shell
systemctl --user daemon-reload
systemctl --user enable --now emacs-jail-mcp.service
#+end_src
检查日志:
#+begin_src shell
journalctl --user -u emacs-jail-mcp.service -f
#+end_src
*** 提示
在代理指令中添加类似的内容:
#+begin_src markdown
您可以通过名为 `emacs_jail_*` 的 MCP 工具访问 Emacs jail。它运行
一个完全在您控制下的 Emacs 实例。 使用它来重现
错误,测试更改和实验。
使用 MCP 工具与 Emacs 交互,而不是从常规 shell 运行命令
`emacs --batch --eval`。
Jail 在主机文件系统的写入时复制快照中运行。启动后
或重启后,其文件系统与主机匹配。 如果 jail 修改文件,
这些更改在主机上不可见。 当 jail 停止或重启时,所有此类
更改都会丢失。
您可以更新主机上的 Emacs 配置,jail 通常会看到
更新的文件。 避免从 jail 中修改相同的文件,因为
这会触发这些路径的写入时复制。 如果有疑问,请重启 jail。
使用 `emacs_jail_eval` 在 jail 中运行 Elisp,并使用 `emacs_jail_shell`
在 jail 容器中运行 shell 命令。
使用 `emacs_jail_screenshot` 捕获 jail 中 Emacs 实例的屏幕截图
当您需要验证渲染时。
编辑 Elisp 时,使用 `emacs_jail_bytecomp` 验证更改的文件并
修复报告的错误和警告。
为了清理,请停止或重启 jail。它将自动丢失所有状态,
包括在 jail 中创建或修改的文件。
#+end_src
** 使用
:PROPERTIES:
:CUSTOM_ID: usage
:END:
*** 命令行参考
您可以在 [[./MANUAL.md][MANUAL.md]] 中找到子命令及其标志的完整参考。
*** 服务器示例
为 stdio 客户端运行 MCP 服务器:
#+begin_src shell
emacs-jail-mcp serve --stdio
#+end_src
使用默认 TCP/SSE 传输运行 MCP 服务器:
#+begin_src shell
emacs-jail-mcp serve
#+end_src
默认情况下,SSE 服务器监听 ~127.0.0.1:9421~。使用 ~--mcp-host~ 和 ~--mcp-port~ 重写绑定地址:
#+begin_src shell
emacs-jail-mcp serve --mcp-host 127.0.0.1 --mcp-port 9421
#+end_src
检查是否正在监听 SSE 服务器:
#+begin_src shell
emacs-jail-mcp info
#+end_src
*** 客户端示例
~send~ 命令是一个小型 CLI 客户端,用于运行的 SSE 服务器。它为每个可用的 MCP 工具提供一个子命令。
检查、重启或停止 jail:
#+begin_src shell
emacs-jail-mcp send control --status
emacs-jail-mcp send control --restart
emacs-jail-mcp send control --stop
#+end_src
读取日志。默认情况下,此命令读取 ~messages~、~warnings~ 和 ~backtrace~:
#+begin_src shell
emacs-jail-mcp send logs --sources messages,warnings
#+end_src
评估 Elisp:
#+begin_src shell
emacs-jail-mcp send eval '(emacs-version)'
#+end_src
在 jail 容器中运行 shell 命令:
#+begin_src shell
emacs-jail-mcp send shell 'pwd && emacs --version'
#+end_src
捕获 Emacs 显示屏的截图:
#+begin_src shell
emacs-jail-mcp send screenshot --output test.png
#+end_src
** 开发
:PROPERTIES:
:CUSTOM_ID: hacking
:END:
*** 项目结构
| 路径 | 目的 |
|-------------|-------------------------------------------|
| ~bin/~ | 构建输出 |
| ~doc/~ | 文档 |
| ~cmd/~ | 可执行入口点 |
| ~e2e/~ | 针对实时 jail 的端到端测试 |
| ~internal/~ | ~cmd~ 和 ~e2e~ 使用的内部包 |
内部包:
| 路径 | 目的 |
|----------------------------+---------------------------------------------------------|
| ~internal/config/~ | 由 CLI 解析器填充并由其他包使用的配置 |
| ~internal/cli/~ | CLI 解析器 |
| ~internal/tools/~ | MCP 工具处理程序 |
| ~internal/jail/~ | 高级 jail 协调和状态机 |
| ~internal/container/~ | 容器生命周期、入口点和捆绑的 Elisp |
| ~internal/container/elisp~ | Emacs 端的 JSON-RPC 服务器 |
| ~internal/emacsclient/~ | Go 端的 Emacs 端 JSON-RPC 服务器客户端 |
| ~internal/display/~ | 虚拟显示 |
单元测试与包代码一起位于 ~*_test.go~ 文件中。端到端 MCP 和 CLI 覆盖率位于 ~e2e/~ 下。
*** 开发依赖
| 依赖 | 目的 |
|---------------+-------------|
| [[https://taskfile.dev/][go-task]] | 任务运行器 |
| [[https://golangci-lint.run/][golangci-lint]] | 静态代码分析工具 |
*** 开发目标
可用的 ~task~ 目标:
- ~task~:运行所有目标
- ~task tidy~:运行 ~go mod tidy~
- ~task build~:构建 ~bin/emacs-jail-mcp~
- ~task lint~:运行 ~golangci-lint~
- ~task docs~:重新生成手册页
- ~task test~:运行单元测试
- ~task e2e~:使用真实的 Emacs 实例运行端到端测试
** 致谢
:PROPERTIES:
:CUSTOM_ID: acknowledgements
:END:
捆绑的 Emacs JSON-RPC 服务器最初基于 [[https://github.com/rhblind/emacs-mcp-server/][emacs-mcp-server]] 中的代码,由 Rolf Håvard Blindheim 撰写,遵循 GPLv3 许可证。
** 历史
:PROPERTIES:
:CUSTOM_ID: history
:END:
变更日志文件位于此处: [[./CHANGES.md][changelog]]。
** 作者
:PROPERTIES:
:CUSTOM_ID: authors
:END:
参见 [[./AUTHORS.org][here]]。
** 许可证
:PROPERTIES:
:CUSTOM_ID: license
:END:
[[./LICENSE][GPLv3+]]