关于 rentctl
rentctl 通过命令行和 MCP 服务,为 AI 编码会话提供租约式开发环境。每个环境都有到期时间并由监督进程负责清理,可在收到停止请求、租约到期或配置的会话结束钩子触发时终止进程。
主要功能
- 01启动并续租开发环境
- 02为项目分配专属端口段
- 03监督并停止进程会话
- 04通过到期、钩子和扫描清理
- 05记录拆除事件和清理状态
适用场景
- 01为编码会话运行开发服务器
- 02在支持的会话结束时清理服务器
- 03查看有效租约和拆除事件
MCP 相关
MCP 服务允许兼容的 Agent 通过工具启动和停止租约式环境。未配置会话结束钩子的客户端依靠租约到期和扫描来清理。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
4收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| profile | string | 否 | |
| project | any | 否 | |
| lease_minutes | integer | 否 |
详细介绍
rentctl
开发服务器不应在所需工作结束后继续运行。
rentctl 为 AI 编码会话租用开发环境。每个环境都是一份租约:会话结束、租约到期或你主动停止时,环境就会终止——以先发生者为准。清理由代码负责,而不是靠 Agent 记得去清理。
它既以 CLI(rent)形式提供,也可作为 MCP 服务,因此 Agent 可以通过工具启动和停止环境,而不必调用原始的 npm run dev 命令后又忘记停止。
为什么需要它
AI 编码会话启动开发服务器。然后会话结束了——可能崩溃、关闭,或只是转而处理其他事——但服务器仍在运行。到了星期五,已经有六个服务器在运行,其中三个占用了你早已忘记的端口,还有一个正悄悄提供过时的代码,让一个 Bug 看起来无法复现。
告诉 Agent 去清理并不能解决问题,因为故障模式本身就是 Agent 没有照做。rentctl 让清理成为结构的一部分:环境设有到期时间,并由 Agent 之外的机制强制执行。
安装
uv tool install rentctl # or: pipx install rentctl, or: pip install rentctl
需要 Python 3.12+。适用于 macOS 和 Linux。 不支持 Windows,也不声称支持 Windows——这里对安全至关重要的是 POSIX 会话,目前尚无经过测试的 Windows 等效机制。
从 1.0.x 升级?请先清空现有环境。 在升级前,先在每个项目中运行 rent down --all,然后重启 Agent 会话,让其中的 MCP 服务加载新版本。跳过这一步是安全的,但功能会受限:1.0.x 环境在 1.1 下仍能正常工作——rent up 会续租,rent down 和 rent sweep 会按整个会话停止环境——但升级前仍在运行的 1.0.x 看门狗会用旧代码强制执行到期时间,可能无法发现脱离其 shell 后仍在运行的子进程。
首次运行
这是一个无需框架的服务器,因此只需要 Python。在一个临时仓库中:
mkdir hello && cd hello && git init -q
cat > rentctl.toml <<'EOF'
[project]
name = "hello"
runner = "process"
[profiles.default]
cmd = 'python3 -m http.server --bind 127.0.0.1 "$PORT"'
cwd = "."
port_env = "PORT"
EOF
rent init
init 会准确显示它将运行的命令,并等待你明确确认:
Enroll project 'hello' (runner: process)
source: /home/you/hello
port block: 6200-6209
rentctl will run these commands on your behalf:
[default]
command: python3 -m http.server --bind 127.0.0.1 "$PORT"
in: /home/you/hello
port via $PORT
Approve and enroll? [y/N] y
你的代码块会有所不同——代码块从 5100 开始分配,优先使用最低的空闲端口。接下来:
$ rent up hello
{
"ok": true,
"project": "hello",
…
"url": "http://localhost:6200",
"port": 6200,
…
"lease_expires": "2026-09-28T15:50:21.327166-05:00",
"already_running": false,
"state": "running",
"supervisor_pid": 24202,
…
"readiness": "answered"
}
$ rent ls # one row: hello, port 6200, "state": "running", "healthy": true
$ curl -sI http://localhost:6200/
HTTP/1.0 200 OK
$ rent down --all --cwd . --reason session-end # what the SessionEnd hook runs
{
"ok": true,
…
"downed": [ { "project": "hello", …, "port": 6200, "was_running": true,
"stopped": null, "pending": true, "state": "stopping",
"detail": "supervisor 24202 is completing cleanup; rent ls shows the outcome" } ]
}
$ curl -sI http://localhost:6200/ || echo gone
gone
(标有 … 的部分已省略。)最后这一步就是产品的核心:在已接入的 Claude Code 或 Gemini CLI 会话中,你永远不必手动输入 rent down——会话结束时会自动执行。钩子不会等待服务器停止;它会发出请求,由环境的监督进程完成后续工作(参见清理实际如何进行)。手动输入 rent down hello 会等待完成,并返回 "stopped": true。
之后运行 rent events --summary,即可查看拆除过程以及执行清理的层级。
如果行为异常,请参阅故障排查。
接入项目
项目在仓库根目录的 rentctl.toml 中描述自身(更名之前遗留的 devctl.toml 仍会被读取,因此无需更改文件名即可继续使用):
[project]
name = "myapp" # lowercase, no separators — it is used as a filename
runner = "process"
[profiles.default]
cmd = 'npm run dev -- --port "$PORT" --strictPort'
cwd = "frontend" # repo-relative, never absolute
port_env = "PORT" # rentctl sets this in the child's environment
然后在仓库中运行:
rent init
init 会准确显示将要运行的命令并征求你的批准,为项目分配一段端口区块,注册 MCP 服务,安装清理钩子(除非 Claude Code 插件已提供这些钩子),并在 CLAUDE.md / GEMINI.md 中添加一条简短规则,告知 Agent 通过 rentctl 而不是手动启动服务器。其他人无需编辑任何内容。
没有端口字段。 端口在服务器启动时分配,而不是写入受版本控制的文件中——因此,同一仓库的两个检出目录会获得不同的端口,而不是互相争用;配置文件也无法分配端口分配器从未登记过的号码。每个项目拥有一个连续的 10 个端口的区块。
使用
rent up myapp # start (or renew) — prints the URL and the port
rent down myapp # stop it; waits up to 15 s (--wait SECONDS, --wait 0 = don't wait)
rent down --all --cwd . # stop everything leased to this directory
rent ls # every environment on this machine, with its state
rent sweep # reconcile: stop what's expired or dead
rent events --summary # what happened, and which cleanup layer did it
rent sync # re-approve a changed rentctl.toml
rent doctor # are the shims, registry and hooks actually working?
rent report-kill myapp --note "…" # "you killed something I was using"
之所以有 report-kill,是因为 rentctl 无法自行判断某次拆除是否不受欢迎——你要求的终止和你事后后悔的终止,从内部看起来一模一样。报告会写入与其他所有内容相同的只追加日志,并与它所质疑的拆除操作匹配,因此,不受欢迎的终止会成为记录中的事实,而不只是一个轶事。
租约默认时长为 120 分钟,上限为 480 分钟。对有效租约执行 rent up 会续租,而不是启动第二台服务。
哪些客户端会自动清理
| 客户端 | 接入方式 | 会话结束时清理 |
|---|---|---|
| Claude Code | 插件(指南),或 rent init(.mcp.json + .claude/settings.local.json) | 会 — SessionEnd 会释放该会话的声明(rent down --all --cwd … --reason session-end);SessionStart 会运行 rent sweep |
| Codex | 插件(指南) | 信任插件的钩子后会(/hooks)— 使用相同的钩子;在此之前,仅依靠过期和 sweep |
| Gemini CLI | rent init(.gemini/settings.json),在项目中使用 Gemini 或已安装 Gemini 时 | 会 — 使用相同的钩子,并由 $GEMINI_PROJECT_DIR 限定范围 |
| 任何其他 MCP 客户端 | 自行注册 rent-mcp | 不会 — 仅依靠租约过期和 sweep |
| 普通 shell | rent | 不会 — 仅依靠租约过期和 sweep |
“仅依靠过期和 sweep”仍能确保服务终止——只是在租约结束时(默认 120 分钟),而不是关闭窗口时。
仅安装插件仍需安装 CLI。 插件(Claude Code 或 Codex)会调用你通过 uv tool install rentctl 安装的 rent 和 rent-mcp;它不会捆绑这些程序,也不会主动获取自己的副本——由第二个不同的 rentctl 管理同一批租约,反而不如没有。若 PATH 中没有 rent,会话结束时清理功能将关闭:之后每个会话启动时都会显示警告,说明这一点并给出安装命令。先安装软件包,然后运行 rent doctor,它会报告每个项目的钩子来自插件、settings.local.json,还是两者兼有。
从 MCP Registry 安装时,客户端会运行 uvx rentctl@<version>:可执行文件 rentctl 就是 MCP 服务,与 rent-mcp 相同(在终端中输入时,它会等待从 stdin 接收客户端)。这样你可以使用工具,但清理仅依靠过期和 sweep——钩子由插件或 rent init 提供。rent --version 会显示你正在运行的版本。
清理实际上如何进行
每个环境都有自己的监督进程:一个小型 rentctl 进程,它会在自己拥有的 POSIX 会话中启动你的命令,并持续运行,直到确认整个会话——命令、它的子进程以及它们派生的所有进程——都已清空。租约会在命令启动前写入,因此不会出现有进程在运行、但 rentctl 没有记录的情况。
无论是谁发起停止,流程都一样:向会话中的每个进程发送 SIGTERM,向关闭期间派生的任何进程发送 SIGTERM,10 s 后向仍在运行的每个进程发送 SIGKILL,然后进行最终扫描。忽略 SIGTERM 的子进程,或在启动它的 shell 退出后仍存活的子进程,都会一并停止。
四个独立层级决定何时清理,因此任何单点故障都不会留下孤儿进程:
- 你发起请求 —
rent down。 - 会话结束 —
SessionEnd钩子会释放该会话对租给该目录的所有资源的占用。如果没有其他会话仍占用资源,服务就会停止。 - 租约到期 — 即使会话在未运行钩子的情况下退出,监督进程也会在租约到期后约一秒内启动停止流程。
- 下一次扫描 —
rent sweep(钩子也会在会话启动时运行它)和rent ls会清理前三层遗漏的内容,包括监督进程已被终止的环境。
rent down 告诉你的内容取决于租约记录,可能有以下三种形式:
"stopped": true— 会话扫描确认已清空,且租约已移除。"stopped": false, "state": "cleanup_incomplete", "survivors": [...]— 有进程无法停止(例如切换了用户的子进程,如使用了sudo)。租约会保留,因此仍会记录这些存活进程的名称,端口也会继续排除在分配范围之外;监督进程会继续重试(5 s、15 s、60 s,然后每分钟一次),rent down会立即重试,而在清理完成前,rent up会以CLEANUP_INCOMPLETE拒绝运行。"stopped": null, "pending": true— 停止请求已接受,流程仍在运行;rent ls会显示最终结果。这表示"ok": true:监督进程无论如何都会完成清理。
rent down 最多等待 15 s 以获得前两种结果(--wait SECONDS;--wait 0 会立即返回)。MCP 工具 env_down 使用相同的等待时间参数 wait_s——默认为 15 s,最多 60 s。
会话结束钩子会快速返回;清理在后台完成。 Claude Code 给插件的 SessionEnd 钩子约 1.5 s 时间。因此,钩子运行的 rent down --all --reason session-end 不会等待:它会为没有其他会话占用的每个租约记录停止请求,唤醒各监督进程,然后返回 pending。监督进程无需等待会话结束。没有存活监督进程的租约会移交给独立的恢复进程,而不会停在清理到一半的状态。
会话崩溃后的清理由第 3 和第 4 层负责,而不是第 2 层。 如果会话在未触发钩子的情况下退出,其服务会继续运行,直到租约到期——最长可达整个租约期限。较短的租约(rent up myapp --lease-minutes 30)可以缩短这段时间。扫描不会停止仍在有效期内的存活租约;它无法判断会话是已崩溃还是仍在工作。
如果监督进程被终止,恢复要等到下一条命令执行时才会开始。 服务会继续提供服务。下一次运行 rent ls、sweep、up 或 down 时,会发现该情况,将租约标记为 unsupervised,并记录 supervisor_lost;不会提前终止服务。到期时,这些命令中的下一条会停止它,而 rent down 会立即停止它。监督进程死亡的那一刻不会有任何东西察觉,因为没有其他进程在运行以便发现这一情况。
没有守护进程。 每个运行中的环境都有一个监督进程;一旦确认环境已消失,该进程就会退出。状态保存在磁盘上,操作系统进程表是真实状态的来源,因此无需后台服务看护,也无需在重启后恢复任何东西。
各平台的监督范围
macOS。 所有仍处于环境会话中的进程都会被发现并停止:
已退出的启动 Shell 遗留的孤儿进程、忽略 SIGTERM 的进程、
移入自己进程组的进程,以及关停期间派生的子进程。调用
setsid() 的进程会离开会话,因此不受监督;守护化(双重 fork 再调用
setsid)就是这样做的。rentctl 永远不会向它发送信号;如果它占用了租约的
端口,拆除时会报告这一情况。如果命令在启动时进行守护化——其自身会话中的
进程已全部退出,但会话外的进程仍在响应端口——rent up 会以
UNSUPERVISABLE 失败,而不会报告一个它无法管理的启动成功结果。此类服务器应在前台运行。
Linux。 每个监督器还会将自身设为 子进程收割器
(prctl(PR_SET_CHILD_SUBREAPER),不需要任何权限),这样孤儿后代会被重新托管给它,
而不是 init。命令的所有后代都会被捕获,包括双重 fork 并调用 setsid() 的守护进程。
内核支持时,向单个进程发送信号会通过 pidfd 进行。仍会逃逸的是并非命令后代的工作进程——
例如交给其他管理器(如 systemd-run、容器运行时、已运行的 tmux 服务器或
at/cron)的进程;以及使用 setns(2) 放入另一个 PID 命名空间的进程的孤儿子进程
(这需要对该命名空间拥有 CAP_SYS_ADMIN):它们会被托管给该命名空间的 init。
如果监督器本身被杀死,捕获也会终止。如果无法启用子进程收割器(某些受限容器中会出现这种情况),
Linux 会提供与 macOS 相同的保证。
rent doctor 会报告当前生效的模式:supervision: session 或
supervision: session+subreaper。两者都不是沙箱——蓄意逃逸监督的命令可以做到。
同一检出目录中的多个会话共用一个服务器
租约属于一个 (项目、目录) 对,因此,同一
检出目录中的两个 Agent 会话会共用一个服务器和一个端口。每个运行 rent up 的会话都会为其持有一个有期限的
声明,租约会持续到最近一份声明到期。会话的 up 只会续期自己的声明。
rent ls 和 up 的结果会列出这些声明,而
shared_with 会列出其他会话。
会话结束时,其 SessionEnd 钩子会释放该会话的声明。只有在没有其他会话仍持有声明时,服务器才会停止。
如果某个会话从未租用该服务器,它结束时不会造成任何影响。MCP 工具
env_down 默认也会释放声明:其他会话仍持有服务器时,它会返回
"stopped": false 和 held_by;设置 force: true 则会为所有人停止服务器。
用户手动输入 rent down 时总会停止服务器,并在 overrode_claims 中列出被覆盖的其他会话声明。
这依赖于识别发出请求的会话。Claude Code 钩子会通过 stdin 发送会话 ID,而 rentctl 也会读取
DEVCTL_SESSION_ID、CLAUDE_CODE_SESSION_ID 和 GEMINI_SESSION_ID。
rent doctor 会说明它找到了哪个 ID。没有这些 ID 的调用方会被视为
unknown,所有 unknown 调用方共用一份声明。释放操作绝不会移除其他会话的声明,因此无法识别会话的结束不会停止服务器,服务器会继续运行直到声明到期。它绝不会停止其他会话正在使用的服务器。
不同的 Git worktree 仍会让每个会话拥有各自的服务器、端口和租约。
rentctl 必须运行的位置:能够查看进程的地方
监督通过读取进程表来实现:工作负载会话中的每个进程都会按其会话 ID 查找,只有能够确认会话为空时,才会报告已验证停止。因此,rentctl 必须运行在能够列出进程并读取其会话 ID 的位置。
- **可用:**由 Agent 主机启动的 MCP 服务(例如 Claude Code),以及在普通终端中输入的
rent。 - **不可用:**在会阻止进程检查的 Agent 命令沙箱内部调用
rent。Codex 的 macOS 沙箱就是其中一种(Operation not permitted (originated from sysctl()))。
如果进程检查被拒绝,rent up 会以 UNSUPPORTED_ENVIRONMENT 拒绝执行,不会启动任何内容。如果服务启动后失去进程检查能力,rentctl 会将所有权视为未知:保留租约,不发送信号,也绝不会报告已验证清理。记录会显示 cleanup_incomplete 和 identity_ambiguous,直到后续尝试能够再次看到该会话。进程检查被拒绝绝不会被视为“没有进程”。解决方法是在主机侧运行 rentctl,而不是给沙箱授予更广泛的权限。
它不会终止不属于自己的进程
每个租约都会记录其监督进程的 PID 和启动时间,而监督进程的 PID 就是环境的会话 ID(1.0.x 的租约使用其服务端的会话 ID)。只有当进程位于该会话中(或在 Linux 上是监督进程的后代)时,它才属于该环境,并且在发送信号之前,会重新检查每个进程的身份。如果会话的 PID 已被回收并分配给某个无关进程,就无法证明其中的任何进程属于我们:租约会被保留并标记为 identity_ambiguous,不会发送任何信号——过期租约无法导致你的数据库被终止。
已登记项目的端口范围内,如果有监听进程却没有对应租约,它就是一个占用者。如何处理取决于机器的强制执行模式:
- advisory(默认模式,也是任何命令唯一会设置的模式)——
rent up会避开该端口,rent ls/rent sweep会报告该进程。绝不会向它发送信号。 - strict——
rent sweep会向每个占用者发送SIGTERM,并报告终止了哪些进程。由于会话启动钩子会运行rent sweep,因此在 strict 模式下,每次会话启动时都会执行此操作。strict 由 rentctl 的机器本地注册表中的enforcement字段设置。没有任何操作会替你开启它。操作前请参阅仅限开发机器。
如果它确实无法判断端口是否正在使用(主机上没有可用的探测方式),就会明确说明,而不会报告端口空闲。“未发现占用者”意味着已进行过检查。
**它能看到的范围仅限于你能看到的范围。**端口探测工具(lsof,或 Linux 上的 psutil)以你的用户身份运行,而非特权探测可能看不到其他用户拥有的套接字。因此,“空闲”指的是就当前用户所能看到的情况而言空闲。实际冲突仍会显现:服务自身的 bind() 会失败,rent up 会报告启动失败(并附上日志末尾内容),而不会写入租约。
安全:你需要信任什么
请阅读本节。rentctl 会执行你仓库配置文件中的命令。
这就是任意代码执行,没有任何工具能改变这一点。 如果你能运行
npm run dev,就能运行任何命令。rentctl 能保证的范围更窄,但也更实用:它不会以任何 静默 方式提供执行路径。
- 在
rent init时,rentctl.toml中的命令会展示给你,并由你明确批准一次。 - 获得批准后,该命令会连同执行决定字段
runner、cmd、cwd和port_env的哈希值,一起复制到rentctl自己的注册表中。 - 如果仓库的
rentctl.toml后续修改了其中任意字段,下次启动时会停止(CMD_CHANGED),并向你显示已批准内容与当前内容的差异。它不会运行新命令。你可以通过rent sync重新批准,也可以不批准。
哈希有意只涵盖决定执行行为的字段,而非整个文件——对所有内容都计算哈希,会让你习惯于对注释修改引发的重新批准直接点击通过,这就违背了初衷。
批准锁定的是命令,而不是命令运行的代码。 无论 package.json 中的 dev 表示什么,npm run dev 都是同一个字符串。修改 package.json 中的脚本、Makefile 或应用代码本身,都会改变实际执行的内容,但不会触发 CMD_CHANGED。这项锁定能防止已更改的 rentctl.toml 悄悄运行新命令;它并不代表你审查过仓库。在运行他人分支的开发服务器之前,应像以往那样谨慎检查。
需要明确的后果:
git pull无法更改已批准的命令。 它可以更改文件,也可以更改该命令运行的代码;但无法更改rentctl运行的命令。- 仓库无法让
rentctl跑出它自己的目录。cwd必须相对于仓库,若它是绝对路径、包含..,或通过符号链接指向仓库外部,则会被拒绝。项目名称必须符合文件名安全要求,否则会被拒绝。 - 非交互式注册需明确授权。 CI 会传入
--trust-repo,此授权会记录为首次使用时信任,而不是悄然采用的默认设置。 rentctl只管理你已注册的内容。 测试框架服务器(pytest fixtures、Playwright 的webServer)由其自身负责管理生命周期,并使用临时端口;rentctl绝不会触碰它们。
如需报告漏洞,请参阅 SECURITY.md。
仅限开发机器
请只在开发服务器可以随时终止的机器上安装。 不要安装在运行着任何你不希望丢失的内容的主机上。
默认安装是安全的:强制执行是建议性的,而且 rentctl 只会扫描你已注册项目的端口段。它不会检查机器上的其他内容,也绝不会向未注册的项目发送信号。
但 rentctl 的全部职责就是清理会话结束后仍在运行的进程,而严格强制执行正是为了确保这一点。一个专门清理你忘记关闭的服务器的工具,不适合部署在提供流量的主机上——在那里,服务器在会话结束后继续运行本来就是预期行为。请只在开发机器上使用。
故障排除
Agent 客户端问题(缺少工具、钩子、审批、未执行清理): docs/troubleshooting.md。测试过哪些客户端和界面,以及测试方式: docs/integrations.md。
rent doctor会检查rent垫片能否运行、注册表能否加载,以及每个已登记项目的钩子是否已接入;还会说明此主机获得的监督级别,以及找到本次会话 ID(session-identity)的位置。如果缺少rent本身,同一项检查会通过python3 -m rentctl.doctor运行。- 服务日志位于
~/.local/state/devctl/logs/<project>-<timestamp>.log(如果设置了$XDG_STATE_HOME,则位于其下)。rent up失败时,会在输出中附上日志末尾内容。 - 发生了什么,以及原因:
rent events会列出每次启动和拆除操作,以及执行操作的层级;原始日志位于~/.local/state/devctl/events.jsonl。拆除操作的down事件会说明停止是否已确认,以及是否需要使用SIGKILL("escalated": true)。stop_requested表示有人请求停止(例如会话结束钩子发出的请求),它本身并不代表拆除操作。claim_released也不是拆除操作:它表示某个会话释放了一个其他会话仍在持有的服务。 rent ls会显示每个租约的state,以及其监督程序是否仍在运行:stopping或"pending": true— 正在停止;几秒后再查看。unsupervised— 监督程序已退出。该服务仍归你所有,也仍在提供服务;租约到期时,下次运行ls/sweep/up/down会停止它,也可以立即运行rent down停止。rent up不会续租。(由 1.0.x 启动的租约也会显示为unsupervised,并带有"legacy": true;rent up会为这类租约续租。)cleanup_incomplete并带有survivors— 这些 PID 无法退出。在重新使用端口前,先查明原因(用户不同?进程卡在内核中?);rent down会重试。
rent up返回UNSUPERVISABLE— 该命令以守护进程方式运行,因此其服务脱离了 rentctl 的监督会话。请在前台运行。遗留的服务会保持不变;请自行停止。- 监督程序会在服务日志中记录其操作,日志行以
[rentctl supervisor <pid>]开头。 - 已登记项目和获批命令的注册表位于
~/.config/devctl/registry.json。这是 rentctl 自己的记录——请通过rent init/rent sync修改,不要手动编辑。
(目录名仍为 devctl,这是该项目的旧名称。这是有意为之——重命名会导致所有仍有效的租约失去关联。)
已知局限
坦率说明这些局限,因为一个宣称能保障安全的工具应当诚实地说明自身边界:
- 审批固定的是命令,而不是代码 — 参见“安全”。
- 同一工作区中的会话共用一个服务 — 参见上文。rentctl 无法识别的会话结束时不会释放任何内容,因此其服务会一直运行,直到租约到期。
- 崩溃清理要等到租约到期 — 会话若在未运行钩子的情况下退出,其服务会一直运行,直到租约到期。
- 恢复要等到下一条命令执行 — 如果监督程序被终止,在下次运行
rent ls/sweep/up/down之前,不会有任何操作。 - macOS 上不会监督以守护进程方式运行的命令;在 Linux 上,交由其他进程管理器处理的任务也不会受到监督 — 参见 各平台的监督范围。
- **只有 Claude Code、Codex(信任其钩子时)和 Gemini CLI 支持会话结束钩子。**其他客户端使用租约到期和清扫机制。不支持云端 Agent 会话(claude.ai/code cloud、Codex Cloud):它们无法访问你机器上的进程。
runner = "compose"目前已设计但尚未实现。请求使用它会返回明确的错误,而不会毫无反应。- 除端口外,唯一支持的环境变量是
port_env。目前,服务所需的其他内容都必须通过cmd传入。 - Windows:参见“安装”。不支持,也不声称支持。
贡献
参见 CONTRIBUTING.md。
关于源代码注释的说明
源代码引用了自身的设计决策——ADR-0008、WI-0040、spec §10。这些
引用指向本项目私下维护的设计日志;每处引用旁都写明了相关的推理,因此即使你无法
查看这些引用指向的内容,也不会遗漏任何信息。
许可证
Apache License 2.0——请参阅 LICENSE 和 NOTICE。
版权所有 2026 Michael Drake。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。