mcpField
R
rentctl

rentctl

ローカル
github.com/Michael-Drake開発ツール04 週間前に更新

rentctl について

rentctl は、コーディングエージェントのセッション向けに、コマンドラインと MCP サーバーでリース制の開発環境を提供します。各環境には期限があり、停止要求、期限切れ、設定済みのセッション終了フックに応じて監視プロセスが後片付けを行います。

主な機能

  1. 01開発環境の起動とリース更新
  2. 02プロジェクトごとのポート範囲の割り当て
  3. 03プロセスセッションの監視と停止
  4. 04期限、フック、スイープによる後片付け
  5. 05終了イベントとクリーンアップ状況の記録

活用シーン

  1. 01コーディングセッションでの開発サーバー実行
  2. 02対応するセッション終了時のサーバー後片付け
  3. 03有効なリースと終了イベントの確認

MCP 対応

MCP サーバーにより、対応するエージェントはツール経由でリース環境を起動・停止できます。セッション終了フックを設定していないクライアントでは、期限切れとスイープが後片付けを担います。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの 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 コーディングセッションに開発環境を 貸し出し ます。それぞれの環境はリースです。セッションが終了するか、リースの期限が切れるか、明示的に停止するかのうち、最初に起きた時点で終了します。クリーンアップは、エージェントが忘れずに行うのではなく、コードが担います。

CLI(rent)と MCP サーバーの両方として提供されるため、エージェントは忘れやすい npm run dev をそのままシェルから実行する代わりに、ツールを使って環境を起動・停止できます。

Why

AI コーディングセッションが開発サーバーを起動します。その後、セッションが終了すると(クラッシュした場合や閉じた場合、あるいは単に次の作業に移った場合でも)、サーバーは動き続けます。金曜日までには6つも起動していて、そのうち3つはポート番号を忘れてしまい、1つは古いコードをひっそり配信していて、バグが再現できないように見えます。

エージェントにクリーンアップを指示しても解決にはなりません。なぜなら、問題はエージェントが指示どおりに動かないことだからです。rentctl はクリーンアップを仕組みとして保証します。環境には有効期限があり、エージェント以外の何かがそれを強制します。

Install

uv tool install rentctl        # or: pipx install rentctl, or: pip install rentctl

Python 3.12 以降が必要です。macOS と Linux に対応しています。 Windows はサポートしておらず、対応をうたってもいません。ここでは POSIX セッションが安全性を担う重要な仕組みですが、Windows で同等に動作することはまだテストされていません。

1.0.x からアップグレードする場合は、まず環境を停止してください。 アップグレード前に、各プロジェクトで rent down --all を実行し、エージェントセッションを再起動して MCP サーバーに新しいバージョンを読み込ませてください。これを省略しても安全ですが、機能は制限されます。1.0.x の環境は 1.1 でも引き続き動作し、rent up で更新され、rent down と rent sweep ではセッション全体を対象に停止できます。ただし、アップグレード前から動作している 1.0.x のウォッチドッグは、古いコードで有効期限を強制するため、シェル終了後も残った子プロセスを見落とすことがあります。

First run

フレームワークを使わないサーバーなので、必要なのは 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 を実行すると、終了処理と、どのクリーンアップ層が実行したかを確認できます。

正しく動作しない場合は、トラブルシューティングを参照してください。

Enroll a project

プロジェクトのルートにある 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 プラグインがすでにフックを提供している場合を除きます)。また、エージェントがサーバーを手動ではなく rentctl 経由で起動するよう指示する短いルールを、CLAUDE.md / GEMINI.md に追加します。他の人が何かを編集する必要はありません。

ポートの設定項目はありません。 ポートはサーバーの起動時に割り当てられ、バージョン管理対象のファイルには記録されません。そのため、同じリポジトリのチェックアウトが2つあっても異なるポートになり、互いに競合しません。また、設定ファイルによって、割り当て機構が把握していない番号が割り当てられることもありません。各プロジェクトには、連続した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 を実行すると、2 台目のサーバーを起動するのではなく、リースが更新されます。

自動クリーンアップに対応しているクライアント

クライアント設定方法セッション終了時のクリーンアップ
Claude Codeプラグイン(ガイド)、または rent init(.mcp.json + .claude/settings.local.json)はい — SessionEnd でセッションの要求を解放します(rent down --all --cwd … --reason session-end)。SessionStart で rent sweep を実行します
Codexプラグイン(ガイド)プラグインのフックを信頼すると有効になります(/hooks) — 同じフックを使用します。それまではリースの期限切れとスイープで対応します
Gemini CLIrent init(.gemini/settings.json)。Gemini をプロジェクトで使用するか、インストールした場合に設定されますはい — 同じフックを使用し、$GEMINI_PROJECT_DIR の範囲に限定されます
その他の MCP クライアントrent-mcp を自分で登録しますいいえ — リースの期限切れとスイープのみ
通常のシェルrentいいえ — リースの期限切れとスイープのみ

「リースの期限切れとスイープのみ」でも、サーバーは確実に停止します。ただし、ウィンドウを閉じたときではなく、リースの終了時(デフォルトでは 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 実行ファイルは rent-mcp と同じ MCP サーバーです(ターミナルで実行すると、標準入力でクライアントを待ち受けます)。この方法で利用できるツールのクリーンアップは、リースの期限切れとスイープのみです。フックはプラグインまたは rent init から設定します。実行中のバージョンは rent --version で確認できます。

クリーンアップの実際の仕組み

各環境には専用のスーパーバイザーがあります。これは小さな rentctl プロセスで、所有する POSIX セッション内でコマンドを起動し、そのセッション全体(コマンド、その子プロセス、および子プロセスが起動したすべてのプロセス)が確認され、空になったと判断されるまで稼働します。リースはコマンドの起動前に書き込まれるため、何かが実行中なのに rentctl に記録がない、という瞬間はありません。

誰が停止を要求した場合でも、停止処理は同じです。セッション内のすべてのプロセスに SIGTERM を送信し、シャットダウン中にフォークされたプロセスにも SIGTERM を送信し、10 秒後も残っている各プロセスに SIGKILL を送信してから、最後にスキャンします。SIGTERM を無視する子プロセスや、起動したシェルより長く動き続けた子プロセスも、ほかのプロセスとともに停止します。

停止するタイミングは、独立した 4 つのレイヤーで決まるため、どれか 1 つに障害が起きても孤立したプロセスは残りません。

  1. ユーザーが要求する — rent down。
  2. セッションが終了する — SessionEnd フックが、そのディレクトリにリースされたすべてのものに対する、そのセッションの要求を解放します。ほかのセッションが要求を保持していなければ、サーバーは停止します。
  3. リースの有効期限が切れる — セッションがフックを実行せずに終了した場合でも、スーパーバイザーはリース切れから約 1 秒以内に停止を開始します。
  4. 次回のスイープ — rent sweep(フックはセッション開始時にも実行します)と rent ls が、最初の 3 つで見逃したものをクリーンアップします。スーパーバイザーが強制終了された環境も対象です。

rent down が返す内容は、リースの状態を次の 3 つのいずれかで示します。

  • "stopped": true — セッションが空であることをスキャンで確認し、リースを削除しました。
  • "stopped": false, "state": "cleanup_incomplete", "survivors": [...] — 停止できなかったプロセスがあります(たとえば、sudo でユーザーを切り替えた子プロセスなど)。リースは保持されるため、残存プロセスの名前は記録されたままで、ポートも割り当て対象外になります。スーパーバイザーは再試行を続け(5 秒後、15 秒後、60 秒後、その後は毎分)、rent down はただちに再試行し、解消されるまで rent up は CLEANUP_INCOMPLETE で拒否されます。
  • "stopped": null, "pending": true — 停止要求は受け付けられ、処理中です。終了結果は rent ls で確認できます。これは "ok": true の状態です。どちらの場合もスーパーバイザーが処理を完了します。

rent down は最初の 2 つの状態について、最大 15 秒間待機します(--wait SECONDS。--wait 0 ならただちに返ります)。MCP ツール env_down では同じ待機時間を wait_s として指定できます。デフォルトは 15 秒で、最大 60 秒です。

セッション終了の応答は速く、クリーンアップはバックグラウンドで完了します。 Claude Code がプラグインの SessionEnd フックに与える時間は約 1.5 秒です。そのため、フックの rent down --all --reason session-end は待機しません。ほかのセッションが要求を保持していないすべてのリースに対する停止要求を記録し、各スーパーバイザーを起動して、pending を返します。スーパーバイザーはセッションの完了を待つ必要はありません。稼働中のスーパーバイザーがいないリースは、中途半端に停止されるのではなく、切り離された復旧プロセスに引き継がれます。

クラッシュしたセッションのクリーンアップは、レイヤー 2 ではなくレイヤー 3 と 4 で行われます。 セッションがフックを実行せずに終了した場合、そのサーバーはリースの有効期限が切れるまで稼働し続けます。最長でリース期間いっぱいまでかかります。短いリース(rent up myapp --lease-minutes 30)にすれば、その時間を短縮できます。スイープは、有効期限内の稼働中のリースを停止しません。クラッシュしたセッションと、作業を続けているセッションを見分けられないためです。

スーパーバイザーが強制終了された場合、復旧は次のコマンド実行時に行われます。 サーバーは引き続きサービスを提供します。次に実行する rent ls、sweep、up、または down がこの状態を検出してリースを unsupervised とし、supervisor_lost を記録します。早期に強制終了されることはありません。有効期限が切れた後、これらのコマンドのいずれかが実行されると停止されます。rent down ならその場で停止します。スーパーバイザーの停止を検出するために稼働しているプロセスはほかにないため、スーパーバイザーが停止した瞬間には何も検知されません。

デーモンはありません。 稼働中の環境ごとにスーパーバイザーが 1 つあり、環境が確実に消失したことを確認すると終了します。状態はディスクに保存され、OS のプロセステーブルが信頼できる情報源となるため、監視が必要なバックグラウンドサービスも、再起動後に復旧させるものもありません。

プラットフォームごとの監視範囲

macOS。 環境のセッション内にとどまるプロセスはすべて検出され、停止されます。 終了した起動シェルの孤立プロセス、SIGTERM を無視するプロセス、独自のプロセスグループに 移動するプロセス、シャットダウン中に fork された子プロセスが対象です。setsid() を呼び出す プロセスは、デーモン化(二重 fork と setsid の呼び出し)によってセッションを離れるため、 監視対象の外です。rentctl はそのプロセスにシグナルを送りません。リースのポートを 使用している場合、ティアダウン時にその旨が報告されます。コマンドが起動時にデーモン化し、 そのプロセス自身のセッションが空になった一方で、セッション外のプロセスがポートに応答する 場合、rent up は所有できない起動を報告する代わりに UNSUPERVISABLE で失敗します。 そのようなサーバーはフォアグラウンドで実行してください。

Linux。 各スーパーバイザーは、自身を子サブリーパーにも設定します (prctl(PR_SET_CHILD_SUBREAPER)。特権は不要です)。そのため、孤立した子孫プロセスは init ではなくスーパーバイザーに再親設定されます。二重 fork して setsid() を呼び出す デーモンも含め、コマンドのすべての子孫プロセスが捕捉されます。単一プロセスへのシグナル送信は、 カーネルが対応している場合、pidfd 経由で行われます。それでも監視を逃れるのは、コマンドの 子孫ではないプロセスを別のマネージャー(systemd-run、コンテナーランタイム、すでに 起動している tmux サーバー、at/cron など)に渡した場合と、別の PID 名前空間に setns(2) で配置されたプロセスの孤立した子プロセスです(この操作には、その名前空間に 対する CAP_SYS_ADMIN が必要です)。それらのプロセスは、その名前空間の init に引き取られます。 スーパーバイザー自体が kill された場合も、捕捉は終了します。サブリーパーの設定が拒否される 環境(制限付きコンテナーの一部など)では、Linux の保証は macOS と同じになります。

rent doctor は、どちらが有効かを supervision: session または supervision: session+subreaper として報告します。どちらもサンドボックスではありません。 監視から逃れようとするコマンドは、それを実行できます。

1 つのチェックアウト内の複数セッションで、1 つのサーバーを共有します

リースは**(プロジェクト、ディレクトリ)の組み合わせに属するため、同じ チェックアウト内の 2 つのエージェントセッションは、1 つのサーバーと 1 つのポートを共有します。 rent up を実行した各セッションは、そのサーバーに対する有効期限付きの独自の要求**を保持し、 リースは最新の要求が有効な間存続します。あるセッションの up が更新するのは、そのセッション 自身の要求だけです。rent ls と up の結果には要求が一覧表示され、 shared_with には他のセッションが表示されます。

セッションが終了すると、その SessionEnd フックがそのセッションの要求を解放します。 他のセッションが要求を保持している間は、サーバーは停止しません。サーバーのリースを取得して いないセッションが終了しても、何も変わりません。MCP ツール env_down もデフォルトで 要求を解放します。他のセッションがサーバーを保持している間は "stopped": false と held_by を返し、force: true を指定すると、すべてのセッションに対してサーバーを停止します。 ユーザーが入力した rent down は常に停止し、上書きした他のセッションの要求を overrode_claims に一覧表示します。

これは、どのセッションが要求しているかを把握できることが前提です。Claude Code のフックは 標準入力でセッション ID を送信し、rentctl は DEVCTL_SESSION_ID、 CLAUDE_CODE_SESSION_ID、GEMINI_SESSION_ID も読み取ります。rent doctor は、 どの ID が見つかったかを報告します。いずれもない呼び出し元は unknown となり、 すべての unknown 呼び出し元は 1 つの要求を共有します。要求の解放によって他のセッションの 要求が削除されることはないため、セッション ID を特定できないセッションが終了しても、 期限切れになるまでサーバーは起動したままです。他のセッションが使用しているサーバーが 停止されることはありません。

Git の別々の worktree を使う場合は、各セッションにそれぞれ独自のサーバー、ポート、リースが 割り当てられます。

rentctl の実行場所:プロセスを確認できる場所

監視はプロセステーブルを読み取って行います。ワークロードのセッション内のすべてのプロセスは、そのセッション ID で特定され、停止が検証済みと報告されるのは、そのセッションが確実に空になった場合だけです。そのため、rentctl はプロセスを一覧表示し、セッション ID を読み取れる場所で実行する必要があります。

  • 動作します: エージェントホスト(たとえば Claude Code)が起動した MCP サーバー、および通常のターミナルで入力した rent。
  • 動作しません: プロセスの検査がブロックされるエージェントのコマンドサンドボックス内からの rent の実行。Codex の macOS サンドボックスがその一例です(Operation not permitted (originated from sysctl()))。

検査が拒否される環境では、rent up は UNSUPPORTED_ENVIRONMENT を返し、何も起動しません。サーバーの起動後に検査できなくなった場合、rentctl は所有権を不明として扱います。リースを保持し、何にもシグナルを送りません。また、クリーンアップが検証済みと報告することもありません。後でセッションを再び確認できるようになるまで、記録には identity_ambiguous とともに cleanup_incomplete が表示されます。検査の拒否が「プロセスなし」として扱われることはありません。解決するには、サンドボックスの権限を広げるのではなく、ホスト側から rentctl を実行してください。

所有していないものは終了させません

各リースには、監視プロセスの PID と起動時刻が記録されます。監視プロセスの PID は、その環境のセッション ID です(1.0.x のリースでは、代わりにサーバーの PID が使われます)。プロセスがその環境に属するのは、そのセッション内にある場合(または 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 が保証するのは、より限定的ですが、実用的なことです。つまり、気付かないうちに実行される経路を追加しません。

  • rentctl.toml のコマンドは、rent init のときに一度だけ、明示的に表示され、承認を求められます。
  • 承認すると、実行を決定するフィールド(runner、cmd、cwd、port_env)のハッシュとともに、rentctl 自身のレジストリにコピーされます。
  • その後、リポジトリの rentctl.toml でいずれかの値が変更されると、次回の起動は停止し(CMD_CHANGED)、承認済みの値と現在の値の差分が表示されます。新しいコマンドは実行されません。rent sync で再承認するか、しないかを選べます。

ハッシュの対象を意図的に実行を決定するフィールドだけに限定しているのは、ファイル全体をハッシュすると、コメントの編集に対する再承認を何も考えずにクリックするようになり、本来の目的が損なわれるためです。

承認によって固定されるのはコマンドであり、実行されるコードではありません。 npm run dev という文字列は、package.json で dev が何を意味するかにかかわらず同じです。package.json のスクリプト、Makefile、またはアプリケーションコードを編集すると、CMD_CHANGED を発生させずに実際に実行される内容が変わります。この固定によって、rentctl.toml の変更が気付かれないまま新しいものを実行するのを防ぎますが、リポジトリをレビューするものではありません。他の人のブランチを取り込むときは、開発サーバーを手動で実行する前と同じように扱ってください。

明確にしておくべき点は次のとおりです。

  • git pull で承認済みのコマンドが変更されることはありません。 ファイルや、そのコマンドが実行するコードは変更される可能性がありますが、rentctl が実行するコマンドは変更されません。
  • リポジトリから rentctl を自身のディレクトリの外へ移動させることはできません。 cwd はリポジトリからの相対パスであり、絶対パス、.. を含むパス、リポジトリ外を指すシンボリックリンクは拒否されます。プロジェクト名はファイル名として安全な形式にするか、拒否されます。
  • 非対話型の登録は明示的に行います。 CI では --trust-repo を渡します。これにより、暗黙のデフォルトではなく、初回使用時の信頼として記録されます。
  • rentctl が管理するのは登録したものだけです。 テストフレームワークのサーバー(pytest のフィクスチャ、Playwright の webServer)は独自にライフサイクルを管理し、一時ポートを使用します。rentctl がこれらに触れることはありません。

脆弱性を報告するには、SECURITY.md をご覧ください。

開発マシン専用

開発サーバーを使い捨てにできるマシンにのみインストールしてください。 失うと困るものを実行しているホストにはインストールしないでください。

デフォルトのインストールは安全です。強制適用は助言的であり、rentctl がスキャンするのは登録したプロジェクトのポート範囲だけです。マシン上のほかのものは調査されず、未登録のものにシグナルが送られることもありません。

ただし、rentctl の主な役割はセッション終了後も残るプロセスを終了させることであり、厳格な強制適用はそれを確実に行うためにあります。忘れたサーバーを終了させるためのツールを、トラフィックを処理しているホストで有効にするのは適切ではありません。そうした環境では、セッション終了後もサーバーが動作し続けることが想定されているためです。開発マシンでのみ使用してください。

トラブルシューティング

エージェントとクライアントの問題(ツールの欠落、フック、承認、クリーンアップが実行されなかった場合): docs/troubleshooting.md。テスト済みのクライアントと利用環境、およびテスト方法: docs/integrations.md。

  • rent doctor は、rent shim が動作すること、レジストリが読み込まれること、登録された各プロジェクトのフックが設定されていることを確認し、このホストがどの監視レベルを受けるか、またこのセッションの 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 で更新されます。
    • survivors を伴う cleanup_incomplete — これらの PID は終了しませんでした。ポートを再利用する前に、その理由(別のユーザーか、カーネル内で停止しているかなど)を確認してください。rent down で再試行できます。
  • rent up の UNSUPERVISABLE — コマンドがデーモン化されたため、そのサーバーは rentctl が監視するセッションから離れました。フォアグラウンドで実行してください。残ったサーバーには手を加えません。自分で停止してください。
  • スーパーバイザーは、サーバーのログに実行内容を記録します。行頭には [rentctl supervisor <pid>] が付きます。
  • 登録済みプロジェクトと承認済みコマンドのレジストリは ~/.config/devctl/registry.json です。これは rentctl 自身の記録です。手作業で変更せず、rent init/rent sync で変更してください。

(ディレクトリ名が devctl なのは、このプロジェクトの以前の名前だからです。意図的なものです。名前を変更すると、現在有効なすべてのリースが孤立してしまいます。)

既知の制限

安全性を主張するツールは、その限界について正直であるべきなので、率直に記します。

  • 承認のピン留めで保護されるのはコマンドであり、コードではありません —「セキュリティ」を参照してください。
  • 同じチェックアウト内のセッションは、1 つのサーバーを共有します — 上記を参照してください。rentctl が識別できないセッションは、終了時に何も解放しないため、そのサーバーはリースの期限が切れるまで稼働します。
  • クラッシュ後のクリーンアップは、期限切れまで行われません — フックを実行せずに終了したセッションでは、リースの期限が切れるまでサーバーが稼働したままになります。
  • 復旧は次のコマンドの実行時まで行われません — スーパーバイザーが強制終了された場合、次の rent ls/sweep/up/down まで何も行われません。
  • macOS ではデーモン化するコマンドは監視されず、Linux でも別のプロセスマネージャーに渡された処理は監視されません — プラットフォームごとの監視範囲を参照してください。
  • セッション終了フックが使えるのは Claude Code、Codex(フックが信頼されている場合)、Gemini CLI のみです。 それ以外のクライアントでは、期限切れとスイープで対応します。クラウドエージェントのセッション(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 をご覧ください。

Copyright 2026 Michael Drake.

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。