Skip to main content
ClaudeWave

Leased dev environments for AI coding sessions — every server dies when its session ends, so none are left running rogue. MCP server + CLI.

MCP ServersOfficial Registry0 stars0 forks● PythonApache-2.0Updated today
ClaudeWave Trust Score
95/100
✓ Verified
Passed
  • ✓Open-source license (Apache-2.0)
  • ✓Actively maintained (<30d)
  • ✓Clear description
  • ✓Topics declared
  • ✓Documented (README)
Last scanned: 9/29/2026
Install in Claude Code / Claude Desktop
Method: UVX (Python) · rentctl
Claude Code CLI
claude mcp add rentctl -- uvx rentctl
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "rentctl": {
      "command": "uvx",
      "args": ["rentctl"]
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
Use cases

MCP Servers overview

# rentctl

<!-- mcp-name: io.github.Michael-Drake/rentctl -->

**A dev server should never outlive the work that needed it.**

`rentctl` *rents* dev environments to your AI coding sessions. Every environment is a
**lease**: it dies when the session ends, when the lease expires, or when you say so —
whichever comes first. Cleanup is owned by code, not by an agent remembering to clean up.

Ships as both a CLI (`rent`) and an MCP server, so an agent can start and stop
environments through tools instead of shelling out to a raw `npm run dev` it will forget
about.

## Why

An AI coding session starts a dev server. Then the session ends — crashes, is closed, or
just moves on — and the server keeps running. By Friday there are six of them, three are
on ports you've forgotten, and one is quietly serving stale code that makes a bug look
unreproducible.

Telling the agent to clean up doesn't fix this, because the failure mode *is* the agent
not doing what it was told. `rentctl` makes the cleanup structural: the environment has an
expiry, and something other than the agent enforces it.

## Install

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

Requires Python 3.12+. **macOS and Linux.** There is no Windows support and no Windows
claim — process-group teardown is the safety-critical mechanism here, and it has no
tested Windows equivalent yet.

## First run

A framework-free server, so nothing but Python is involved. In a scratch repo:

```
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` shows you exactly what it will run and waits for an explicit yes:

```
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
```

Your block will differ — blocks are drawn from 5100 upward, lowest free first. Then:

```
$ rent up hello
{
  "ok": true,
  "project": "hello",
  "url": "http://localhost:6200",
  "port": 6200,
  "lease_expires": "2026-09-28T13:32:36.428730-05:00",
  "already_running": false,
  "readiness": "answered",
  …
}

$ rent ls                      # one row: hello, port 6200, "healthy": true
$ curl -sI http://localhost:6200/
HTTP/1.0 200 OK

$ rent down --all --cwd .      # exactly what the SessionEnd hook runs
{
  "ok": true,
  "downed": [ { "project": "hello", "port": 6200, "was_running": true, … } ]
}

$ curl -sI http://localhost:6200/ || echo gone
gone
```

(Output trimmed where marked `…`.) That last step is the whole product: in an enrolled
Claude Code or Gemini CLI session you never type `rent down` — the session ending does.
`rent events --summary` afterwards shows the teardown and which cleanup layer did it.

If something doesn't behave, see [Troubleshooting](#troubleshooting).

## Enroll a project

A project describes itself in a `rentctl.toml` at its repo root (a `devctl.toml`
left over from before the rename is still read, so nothing needs renaming to keep
working):

```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
```

Then, from the repo:

```
rent init
```

`init` shows you the exact command it will run, asks you to approve it, claims a block of
ports for the project, registers the MCP server, installs the cleanup hooks (unless the
Claude Code plugin already supplies them), and adds a short rule to `CLAUDE.md` /
`GEMINI.md` telling agents to start servers through rentctl rather than by hand. Nobody
else has to edit anything.

**There is no port field.** Ports are drawn when a server starts, not written down in a
tracked file — so two checkouts of the same repo get different ports instead of fighting,
and a config file can't hand out a number the allocator never heard of. Each project owns
a contiguous block of 10 ports.

## Use

```
rent up myapp              # start (or renew) — prints the URL and the port
rent down --all --cwd .    # stop everything leased to this directory
rent ls                    # every environment on this machine
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` exists because `rentctl` cannot tell, on its own, whether a teardown
was unwanted — a kill you asked for and a kill you regret look identical from the
inside. The report lands in the same append-only log as everything else and is
matched to the teardown it disputes, so an unwanted kill becomes a fact on the
record rather than an anecdote.

Leases default to **120 minutes** and are capped at **480**. `rent up` on a live lease
renews it rather than starting a second server.

## Which clients get automatic cleanup

| Client | How it is wired | Session-end cleanup |
|---|---|---|
| Claude Code | the plugin, or `rent init` (`.mcp.json` + `.claude/settings.local.json`) | **Yes** — SessionEnd runs `rent down --all --cwd "$CLAUDE_PROJECT_DIR"`; SessionStart runs `rent sweep` |
| Gemini CLI | `rent init` (`.gemini/settings.json`), when Gemini is used in the project or installed | **Yes** — same hooks, scoped by `$GEMINI_PROJECT_DIR` |
| Any other MCP client | register `rent-mcp` yourself | **No** — lease expiry and sweep only |
| A plain shell | `rent` | **No** — lease expiry and sweep only |

"Expiry and sweep only" still guarantees the server dies — just at the end of its lease
(120 minutes by default), not when you close the window.

**Plugin-only installs need the CLI too.** The Claude Code plugin calls the `rent` and
`rent-mcp` you installed with `uv tool install rentctl`; it does not bundle them, and it
deliberately does not fetch its own copy — a second, different rentctl acting on the same
leases is worse than none. Without `rent` on `PATH`, session-end cleanup is **off**: every
session then starts with a warning saying so and naming the install command. Install the
package first, then run `rent doctor`, which reports whether each project's hooks come
from the plugin, from `settings.local.json`, or from both.

**From the MCP Registry**, a client runs `uvx rentctl@<version>`: the `rentctl`
executable is the MCP server, the same as `rent-mcp` (typed in a terminal it waits for a
client on stdin). That gives you the tools with expiry-and-sweep cleanup only — the
hooks come from the plugin or `rent init`. `rent --version` says which version you are
running.

## How cleanup actually happens

Four independent layers, so no single failure leaves an orphan:

1. **You ask** — `rent down`.
2. **The session ends** — a `SessionEnd` hook tears down everything leased to that
   directory.
3. **The lease expires** — a detached watchdog per lease kills it after expiry, even if
   the session died without running its hook. It checks once a minute, so teardown lands
   up to ~60 s after the lease runs out, not at the instant.
4. **The next sweep** — `rent sweep` (which the hooks also run at session start) stops
   anything expired or already dead that the first three missed.

**A crashed session is cleaned up by layers 3 and 4, not 2.** If the session dies
without firing its hook, its server keeps running until the lease expires — up to the
full lease length. Shorter leases (`rent up myapp --lease-minutes 30`) shorten that
window. A sweep does not stop a live, unexpired lease; it cannot tell a crashed session
from one that is still working.

There is **no daemon.** State lives on disk and the OS process table is the source of
truth, so there is no background service to babysit, and nothing to resurrect after a
reboot.

### One active session per checkout

A lease belongs to a **(project, directory)** pair, not to a session. Two agent sessions
open in the *same* checkout share one environment — the second `rent up` renews the
first's server rather than starting its own — and the first session to end runs
`rent down --all --cwd` and tears it down under the other.

Separate git worktrees avoid this entirely: each worktree is its own directory, gets its
own lease and its own port, and its session end touches only its own server. Shared
sessions in one checkout are a known limitation, with no fix promised yet.

### It won't kill things it doesn't own

Every lease records the process's PID **and its start time**. Before killing anything,
`rentctl` re-checks both. If the PID was recycled onto some unrelated process, the start
times disagree and it refuses — a stale lease can't get your database killed.

A listener inside an enrolled project's port block with no lease behind it is a
**squatter**. What happens to it depends on the machine's enforcement mode:

- **advisory** (the default, and the only mode any command sets) — `rent up` routes
  around it, and `rent ls` / `rent sweep` report it. It is never signalled.
- **strict** — `rent sweep` sends each squatter `SIGTERM`, and reports what it killed.
  Since the session-start hook runs `rent sweep`, under strict that happens at every
  session start. Strict is set by the `enforcement` field in rentctl's machine-local
  registry; nothing turns it on for you. See
  [Development machines only](#development-machines-only) before you do.

When it genuinely cannot tell whether a port is in use — no usable probe on the host —
it says so, rather than report
ai-agentsclidev-servermcpprocess-management

What people ask about rentctl

What is Michael-Drake/rentctl?

+

Michael-Drake/rentctl is mcp servers for the Claude AI ecosystem. Leased dev environments for AI coding sessions — every server dies when its session ends, so none are left running rogue. MCP server + CLI. It has 0 GitHub stars and its last recorded update is dated 2026-09-29.

How do I install rentctl?

+

You can install rentctl by cloning the repository (https://github.com/Michael-Drake/rentctl) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.

Is Michael-Drake/rentctl safe to use?

+

Our security agent has analyzed Michael-Drake/rentctl and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.

Who maintains Michael-Drake/rentctl?

+

Michael-Drake/rentctl is maintained by Michael-Drake. The last recorded GitHub activity is dated 2026-09-29, with 0 open issues.

Are there alternatives to rentctl?

+

Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.

Deploy rentctl to your cloud

Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.

Maintain this repo? Add a badge to your README

Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.

Featured on ClaudeWave: Michael-Drake/rentctl
[![Featured on ClaudeWave](https://claudewave.com/api/badge/michael-drake-rentctl)](https://claudewave.com/repo/michael-drake-rentctl)
<a href="https://claudewave.com/repo/michael-drake-rentctl"><img src="https://claudewave.com/api/badge/michael-drake-rentctl" alt="Featured on ClaudeWave: Michael-Drake/rentctl" width="320" height="64" /></a>

More MCP Servers

rentctl alternatives