mcpField
R
rentctl

rentctl

本地服务
github.com/Michael-Drake开发工具04 周前更新

关于 rentctl

rentctl 通过命令行和 MCP 服务,为 AI 编码会话提供租约式开发环境。每个环境都有到期时间并由监督进程负责清理,可在收到停止请求、租约到期或配置的会话结束钩子触发时终止进程。

主要功能

  1. 01启动并续租开发环境
  2. 02为项目分配专属端口段
  3. 03监督并停止进程会话
  4. 04通过到期、钩子和扫描清理
  5. 05记录拆除事件和清理状态

适用场景

  1. 01为编码会话运行开发服务器
  2. 02在支持的会话结束时清理服务器
  3. 03查看有效租约和拆除事件

MCP 相关

MCP 服务允许兼容的 Agent 通过工具启动和停止租约式环境。未配置会话结束钩子的客户端依靠租约到期和扫描来清理。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"michael-drake-rentctl": {
"command": "uvx",
"args": [
"rentctl"
]
}
}
}
启动命令uvx rentctl

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypirentctl1.2.0

工具

4

收录的工具列表 · 更新于 3 周前

参数类型必填说明
profilestring否
projectany否
lease_minutesinteger否

详细介绍

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 CLIrent init(.gemini/settings.json),在项目中使用 Gemini 或已安装 Gemini 时会 — 使用相同的钩子,并由 $GEMINI_PROJECT_DIR 限定范围
任何其他 MCP 客户端自行注册 rent-mcp不会 — 仅依靠租约过期和 sweep
普通 shellrent不会 — 仅依靠租约过期和 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 退出后仍存活的子进程,都会一并停止。

四个独立层级决定何时清理,因此任何单点故障都不会留下孤儿进程:

  1. 你发起请求 — rent down。
  2. 会话结束 — SessionEnd 钩子会释放该会话对租给该目录的所有资源的占用。如果没有其他会话仍占用资源,服务就会停止。
  3. 租约到期 — 即使会话在未运行钩子的情况下退出,监督进程也会在租约到期后约一秒内启动停止流程。
  4. 下一次扫描 — 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。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。