jocay e2640dad57 docs: README 以一键安装为主线重新组织安装说明
- 开头即给出 IPv4 / IPv6 一键安装命令、参数表与安装后的状态检查
- 新增连接 AI 客户端(带 Authorization 头)、更新、卸载、从源码构建的独立小节
- 说明 Release 目前提供的平台附件及其系统要求
- 部署细节集中到末尾:配置文件位置与修改方式、macOS launchd、systemd 手动部署、发布新版本流程

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:01:09 +08:00

bolt — 极速单文件 MCP 命令执行服务器

让 AI(Claude Code / Claude Desktop / Cursor 等 MCP 客户端)通过 MCP 协议连接后,在服务器上执行 shell 命令、读写和传输文件。

  • 单文件:全部代码在 src/main.rs,仅依赖 serde_json(Unix 下加 libc);base64 / SHA-256 / HMAC 均为手写
  • 零框架:手写 JSON-RPC 2.0 + MCP 协议 + Streamable HTTP 服务器,无 async runtime、无重量级依赖
  • 双传输:stdio(本地 AI 客户端默认)与 Streamable HTTP(远程连接)

安装

一键安装(Linux / macOS)

在目标设备上执行(需要 root;已经是 root 时去掉 sudo)。IPv4 网络用 git.jocayn.qzz.io,IPv6 网络用 git.draws.qzz.io:

# IPv4
curl -fsSL https://git.jocayn.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sudo sh -s -- --http 0.0.0.0:8080 --token 你的令牌

# IPv6
curl -fsSL https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sudo sh -s -- --http 0.0.0.0:8080 --token 你的令牌

脚本会:识别平台 → 从 Release 下载最新版二进制并校验 SHA-256 → 装到 /usr/local/bin/bolt → 写入配置 → 注册系统服务(Linux 为 systemd 的 bolt,macOS 为 launchd 的 io.jocay.bolt)→ 设置开机自启并启动。

  • 目前 Release 提供 linux-amd64(glibc ≥ 2.28,Debian 10 / Ubuntu 18.10 及以上)和 darwin-arm64(macOS 11+,Apple Silicon);其他平台请从源码构建后用 --binary 安装。
  • 只在本机使用、或放在反向代理后面时,监听 127.0.0.1:端口 即可;直接对外监听 0.0.0.0 时务必设置 --token。
  • 已安装的设备再次执行会被拒绝,以免按新参数重写配置时丢掉令牌。升级请用更新,确实要重装加 --reinstall。

更多参数写在 sh -s -- 之后,例如限制命令和文件目录:

curl -fsSL https://git.jocayn.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sudo sh -s -- \
    --http 0.0.0.0:8080 --token 你的令牌 --allow "git,docker,ls" --file-root /srv/data
参数 含义
--http <addr> 监听地址,默认 127.0.0.1:8080;对外用 0.0.0.0:8080
--token <t> Bearer 认证令牌(对外监听时必填)
--allow <a,b> / --deny <a,b> 命令白/黑名单
--timeout-ms <n> 命令默认超时,默认 30000
--file-root <d1,d2> 文件功能允许访问的目录(配置 --allow 时必填,否则文件功能禁用)
--max-upload <n> PUT /files 单次上传上限,默认 2G
--version <tag> 安装指定版本,默认最新
--binary <路径或URL> 不从 Release 下载,改用本地二进制(如 scp 过去的)或指定 URL
--reinstall 已安装时按本次参数重写配置
--no-start / --no-enable 只安装不启动 / 不设开机自启

环境变量:BOLT_GIT_HOST 指定下载站点(默认先试 git.jocayn.qzz.io,不通再试 git.draws.qzz.io),BOLT_DL_URL 直接指定二进制地址,BOLT_DL_TOKEN 用于私有仓库下载(与 --token 无关)。

装好后查看状态:

systemctl status bolt                      # Linux
sudo launchctl print system/io.jocay.bolt  # macOS

连接 AI 客户端

# Claude Code 连接远程 bolt(带令牌)
claude mcp add --transport http bolt http://服务器:8080/mcp --header "Authorization: Bearer 你的令牌"

# 放在 HTTPS 反向代理后面时,直接用代理地址
claude mcp add --transport http bolt https://bolt.example.com/mcp --header "Authorization: Bearer 你的令牌"

其他 MCP 客户端按 Streamable HTTP 方式配置 http://服务器:8080/mcp,并加上 Authorization: Bearer 你的令牌 请求头。

更新

只替换二进制并重启服务,保留现有配置:

# IPv4
curl -fsSL https://git.jocayn.qzz.io/jocay/bolt/raw/branch/main/deploy/update.sh | sudo sh

# IPv6
curl -fsSL https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/update.sh | sudo sh
  • 已是最新版本时直接跳过,不会自动降级;新版本服务起不来时自动回滚,旧版本备份在 /usr/local/bin/bolt.bak。
  • 重启由独立进程完成,所以让 AI 通过 bolt 自己执行更新也安全(只是这次调用的连接会断开,稍后重连即可)。
  • 可选参数(写在 sh -s -- 之后):--version v0.2.0 安装指定版本,--binary ./bolt 使用本地二进制,--force 同版本也重装。

卸载

# IPv4
curl -fsSL https://git.jocayn.qzz.io/jocay/bolt/raw/branch/main/deploy/uninstall.sh | sudo sh

# IPv6
curl -fsSL https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/uninstall.sh | sudo sh

删除服务、二进制和备份,默认同时删除配置与日志;要保留配置加 sh -s -- --keep-config。

从源码构建

cargo build --release        # 产物: target/release/bolt (~1MB)

本地模式(stdio):不需要安装服务,由 MCP 客户端直接拉起本进程

claude mcp add --transport stdio bolt -- /abs/path/to/bolt
# 通用 MCP 客户端: {"mcpServers": {"bolt": {"command": "/abs/path/to/bolt"}}}

远程模式(Streamable HTTP):直接前台运行

bolt --http 127.0.0.1:8080                           # 仅本机
bolt --http 0.0.0.0:8080 --token mysecret            # 对外 + Bearer 认证
bolt --http 0.0.0.0:8080 --token mysecret --allow "git,docker,curl" --file-root /srv/data

要注册成系统服务,把构建产物交给安装脚本:sudo sh deploy/install.sh --binary target/release/bolt --http 0.0.0.0:8080 --token 你的令牌。

工具

工具 说明
run_command 执行 shell 命令:完整 shell 语法(管道/重定向/&&)、cwd、env、timeout_ms(超时自动杀进程树)、max_output_bytes(输出截断防膨胀)、shell=false 直接执行二进制;客户端可传 _meta.progressToken 接收进度通知,支持 notifications/cancelled 取消
shell_open 开启持久化交互式 shell 会话(cd、环境变量等状态跨次保留),可指定 shell(sh/bash/zsh/fish)与 cwd;空闲 30 分钟自动回收
shell_write 向会话写入输入(自动补换行)
shell_read 读取会话新输出,可阻塞等待(timeout_ms,0 = 等到会话退出),可被取消
shell_close 关闭并杀掉会话进程树
file_read 读取文件:UTF-8 文本按文本返回,图片按 image 返回,其余按 base64;offset + length 分块读取,不会切断多字节字符
file_write 写入文件:overwrite(默认,临时文件 + rename 原子替换,保留原权限)/ create(已存在则失败)/ append;encoding=base64 写二进制;mkdirs 自动建目录;返回 SHA-256
file_stat 文件信息(是否存在、类型、大小、修改时间、权限),可选计算 SHA-256
file_link 仅 HTTP 模式:生成带 HMAC 签名、限时有效的 /files 直传链接,并给出可在客户端本机执行的 curl 命令

所有工具结果都同时提供 content 与 structuredContent(file_read 为两块 content:元数据 JSON + 文件内容)。

run_command 返回:exit_code / signal / timed_out / cancelled / detached / duration_ms / stdout / stderr / 截断标志等。命令退出后若仍有后台进程占用输出管道(如 python -m http.server &),等待 300ms 后返回 detached=true,后台进程继续运行。

文件传输

MCP 工具的参数和结果都要经过模型上下文,所以分两层:

  • 小文件(配置、脚本、日志片段、截图):直接用 file_read / file_write,stdio 和 HTTP 都可用。
  • 大文件(HTTP 模式):数据走 /files 端点,不经过模型。
    • AI 调用 file_link 拿到签名链接,在客户端本机执行返回的 curl 命令即可:

      curl -fsSL -o ./backup.tar.gz 'http://host:8080/files?path=/srv/data/backup.tar.gz&exp=...&sig=...'   # 下载
      curl -fsS -T ./dataset.zip  'http://host:8080/files?path=/srv/data/dataset.zip&exp=...&sig=...&mode=overwrite&mkdirs=0'  # 上传
      
    • 也可以直接带 Bearer 令牌访问:curl -H "Authorization: Bearer $T" -T f.bin "http://host:8080/files?path=/srv/data/f.bin&mkdirs=1"。

    • 支持 Range 断点续传(206)、HEAD、chunked 上传、Expect: 100-continue;上传同样是原子替换,失败不破坏原文件,响应里带 SHA-256。

    • 签名覆盖方法、路径、写入模式和过期时间,链接只能用于签名时指定的那一个操作;签名密钥在进程启动时随机生成,重启后旧链接失效。

配置

CLI 参数(环境变量 BOLT_* 为回退):

参数 默认 说明
--http <addr> 无(stdio) 启用 Streamable HTTP 监听地址,如 127.0.0.1:8080(:8080 等价于 127.0.0.1:8080)
--token <t> 无 HTTP 要求 Authorization: Bearer <t>
--allow <a,b> 空 命令白名单(按程序名精确匹配;非空时 shell 模式禁止元字符)
--deny <a,b> 空 命令黑名单(按程序名匹配命令中每一段,尽力而为)
--timeout-ms <n> 30000 命令默认超时(工具参数可覆盖,0 = 无限)
--cwd <dir> 当前 命令与相对文件路径的默认工作目录
--max-output <n> 1M 单流输出捕获上限(支持 K/M/G)
--allow-origin <url> localhost/127.0.0.1 额外允许的 HTTP Origin(防 DNS rebinding)
--file-root <d1,d2> 不限 文件工具与 /files 只能访问这些目录;配置了 --allow 时必须设置,否则文件功能禁用
--max-upload <n> 2G PUT /files 单次上传上限(支持 K/M/G)
export BOLT_TOKEN=xxx BOLT_ALLOW="git,docker" BOLT_TIMEOUT_MS=60000 BOLT_FILE_ROOT=/srv/data
bolt --http 127.0.0.1:8080

协议实现(2025-06-18 规范)

  • initialize(协议版本协商,支持 2024-11-05 / 2025-03-26 / 2025-06-18)、ping、tools/list、tools/call、shutdown
  • notifications/initialized、notifications/cancelled、notifications/exit(仅 stdio 生效)、notifications/progress(进度上报)
  • 工具执行错误以 isError: true 返回;协议错误返回标准 JSON-RPC 错误码(-32601/-32602/-32700 等)
  • Streamable HTTP:
    • initialize 时签发 Mcp-Session-Id;携带该头的请求按会话隔离 shell 会话与取消,未知/过期 ID 返回 404,DELETE /mcp 终止会话并清理其资源;不带该头的客户端共用一个匿名作用域(兼容无状态客户端)
    • POST:通知 → 202;请求 → JSON 或 SSE。Accept 含 text/event-stream 的 tools/call 会在同一个 SSE 响应里先推进度通知、最后推结果
    • GET:长连接 SSE 流(接收本会话的服务器通知 + 10s 心跳)
    • 支持 chunked 请求体、CORS、Origin 校验、Bearer 认证

性能与资源

  • 复用缓冲区,输出计数用原子操作,进度推送不占主循环;stdio 每个工具调用一个工作线程,HTTP 每个连接一个线程
  • 子进程独占进程组(setpgid),超时/取消按进程组 SIGKILL,连带杀掉整棵进程树,无僵尸
  • 上限:并发工具调用 128、HTTP 连接 512、shell 会话 64(空闲 30 分钟回收)、MCP 会话 1024(空闲 24 小时回收)
  • 实测(本机,含 Python 客户端逐字节 I/O 开销):
    • ping 往返吞吐 ≈ 14,700 msg/s
    • 并发 echo 命令平均 4.7 ms/次(进程 spawn + 捕获输出)
  • 构建配置:lto = "fat"、codegen-units = 1、strip = true

测试

cargo test --release          # 单元测试(策略/编码与摘要/HTTP 解析/文件读写与目录限制/会话缓冲/执行引擎等)
cargo test --release test_files   # 只跑某一个

端到端测试脚本在 docs/(需要 Unix 主机;二进制路径默认 target/release/bolt,可用 BOLT_BIN 指定):

python3 docs/bolt_stdio_test.py   # stdio 端到端
python3 docs/bolt_http_test.py    # HTTP 端到端(含 Mcp-Session-Id、/files、签名链接)

安全说明

  • 默认只接受本机连接(127.0.0.1),对外公开需显式 0.0.0.0 + --token(未设置时启动日志会警告)
  • 该服务器等于远程 root shell 入口,务必配置认证后再暴露公网
  • 白名单(--allow):按程序名精确匹配,shell 模式下禁止 ; & | $ \ < > ( ) { }和换行,防止git status; rm -rf ~ 这类串联绕过;shell_write 写入的每一行同样受检。注意白名单内的程序如果自身能执行任意命令(sh、env、docker、find -exec、git -c ...`),白名单就约束不了它
  • 黑名单(--deny):检查命令每一段的程序名(去掉路径、引号、转义和前置的 VAR=x),但仍可能被包装命令绕过,只适合防误操作
  • 文件功能等同于任意写文件(可写 authorized_keys、crontab),所以配置白名单时必须用 --file-root 限定目录,否则文件功能自动禁用;路径会解析符号链接并拒绝 .. 逃逸
  • 不同 MCP 会话之间的 shell 会话与取消相互隔离;HTTP 客户端无法通过 notifications/exit 关停服务
  • Unix 下命令在独立进程组运行,取消/超时会连带杀掉子进程;收到 SIGINT/SIGTERM(Windows 为 Ctrl+C)时先清理全部子进程再退出

平台

  • Linux / macOS(完整功能:进程组杀、信号处理)
  • Windows(基础功能:cmd /C、taskkill /T 杀进程树、Ctrl+C 清理退出)

部署细节

配置文件与修改

Linux(systemd) macOS(launchd)
二进制 /usr/local/bin/bolt /usr/local/bin/bolt
配置 /etc/bolt.env(BOLT_* 环境变量);监听地址在 /etc/systemd/system/bolt.service 的 ExecStart /Library/Application Support/bolt/io.jocay.bolt.plist(开机自启时复制到 /Library/LaunchDaemons/)
日志 journalctl -u bolt -f /var/log/bolt/stdout.log、/var/log/bolt/stderr.log
重启 sudo systemctl restart bolt sudo launchctl kickstart -k system/io.jocay.bolt

Linux 上改完 /etc/bolt.env 后重启服务即可生效,可用的变量见 deploy/bolt.env.example。macOS 的两份 plist 需要保持一致,改参数建议用 --reinstall 重新执行安装脚本(记得带上原来的 --token 等参数)。两份配置均仅 root 可读写。

macOS(launchd)

--no-enable 只保留手动启动配置,取消本服务的开机配置,仍可立即启动;--no-start 本次不加载服务,已运行的本服务也会卸载,下次开机是否启动由 --no-enable 决定。两个参数可一起使用。

# 查看服务 / 重启已加载的服务 / 停止并卸载
sudo launchctl print system/io.jocay.bolt
sudo launchctl kickstart -k system/io.jocay.bolt
sudo launchctl bootout system/io.jocay.bolt

# 停止后或使用 --no-start 安装后,手动加载并启动
sudo launchctl bootstrap system '/Library/Application Support/bolt/io.jocay.bolt.plist'

macOS 服务的工作目录为 /var/root,PATH 包含 /opt/homebrew/bin 和 /opt/homebrew/sbin,不会加载个人 shell 配置。

systemd 手动部署

不用安装脚本时,仓库内 deploy/ 提供现成的部署文件:

sudo install -m 0755 target/release/bolt /usr/local/bin/bolt
sudo cp deploy/bolt.service /etc/systemd/system/
sudo cp deploy/bolt.env.example /etc/bolt.env   # 按需修改(监听地址在 service 文件 ExecStart)
sudo systemctl daemon-reload
sudo systemctl enable --now bolt                 # 开机自启 + 立即启动

服务特性:崩溃自动重启(Restart=on-failure,2 秒间隔)、优雅停机(SIGTERM 时杀掉所有子进程树)、日志进 journald、LimitNOFILE=65536。

发布新版本

  1. 修改 Cargo.toml 的 version,提交并推送。

  2. 编译附件(需要 cargo-zigbuild 与 zig):

    cargo zigbuild --release --target x86_64-unknown-linux-gnu.2.28   # bolt-linux-amd64,只依赖 glibc 2.28
    cargo zigbuild --release --target aarch64-apple-darwin            # bolt-darwin-arm64,安装时缺少签名会自动补 ad-hoc 签名
    sha256sum bolt-* > SHA256SUMS
    

    直接 cargo build 的产物依赖构建机的 glibc 版本,拿到较老的系统上可能无法运行。

  3. 在 Gitea 上新建 Release(tag 如 v0.3.0),上传 bolt-linux-amd64、bolt-darwin-arm64 和 SHA256SUMS。脚本按 /api/v1/repos/jocay/bolt/releases/latest 识别最新版本,发布后各设备执行更新命令即可。

S
Description
No description provided
Readme 211 KiB
v0.2.0 Latest
2026-10-18 10:54:30 +08:00
Languages
Rust 85.9%
Shell 14.1%