Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Description from the maker (original English)
Leased dev environments — every server dies when its AI session ends.
These three are never merged into one score: opinions, measurements and popularity are different things.
We have not written a plain-language summary for this one yet. Below are the facts we read from the registry.
Pick the AI app you use, then copy the settings.
You need
Which app do you use?
Pick your app above. We remember it, so next time the button says “Install in …”.
Open Claude Desktop → Settings → Developer → Edit Config, and paste this into claude_desktop_config.json
{
"mcpServers": {
"rentctl": {
"command": "uvx",
"args": [
"rentctl"
]
}
}
}Run this in your terminal
claude mcp add rentctl -- uvx rentctlPaste into .cursor/mcp.json in your project, or ~/.cursor/mcp.json
{
"mcpServers": {
"rentctl": {
"command": "uvx",
"args": [
"rentctl"
]
}
}
}Your browser asks to open the app, then the app shows the settings and asks you to confirm. Key values are left empty for you to fill in.
Paste into .vscode/mcp.json in your project
{
"servers": {
"rentctl": {
"type": "stdio",
"command": "uvx",
"args": [
"rentctl"
]
}
}
}Your browser asks to open the app, then the app shows the settings and asks you to confirm. Key values are left empty for you to fill in.
This one runs on your computer, and ChatGPT can only connect to servers on the internet. Pick another app.
Your choice stays on this device in a cookie; no account needed.
These settings are generated from the maker’s registry entry. If the README says otherwise, follow the README.
Runs on your computer; the tool list appears in your AI app after you install it.
We check information anyone can see; we have not read the code. Checks that do not apply are left out of the count. · Checked Nov 7, 2026
The registry name was verified with the GitHub account Michael-Drake, which owns the repo
We have not read this repo from GitHub yet
We have not read this repo from GitHub yet
This one runs on your computer; there is no internet endpoint
Runs on your computer and uses local settings instead
Runs on your computer; we can only read tool lists from internet endpoints
Compared with every active server in this category in the registry. Stars count only when the publisher owns the repo.
It passed 1 of 1 applicable basic checks. We have not read the code. Before adding a key or token, see what access the server asks for and use a key with only the permissions you need.
The registry does not list a required key, though some servers ask you to sign in on first use.
It runs on your computer. Your AI app starts it when needed, and data does not pass through the maker’s servers (unless it calls an online service itself).
The GitHub repo last changed –, and the latest version (1.2.0) reached the registry 38 days ago.
Apps that support MCP, such as Claude Desktop, Claude Code, Cursor and VS Code. You also need Python (uv) on your computer.
In the same category: Spec Driven Development, Remote Desktop Commander, Dialyne
Servers in the same category from other makers, sorted by stars.
Reading a card:★ GitHub starsiThree pillars: reviews · checks · usageiUpdated = latest version published to the registryi
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Real phone calls in France for AI agents: dial +33 numbers, menus, hold, AI disclosure.
Copied from the maker’s repo as written. PopNx MCP has not reviewed or endorsed it. HTML, images and scripts are removed. Links lead off this site.
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.
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.
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 — POSIX sessions are the safety-critical mechanism here, and they have no tested Windows equivalent yet.
Upgrading from 1.0.x? Drain first. Run rent down --all in each project before you upgrade, then restart your agent sessions so their MCP servers load the new version. Skipping it is safe but degraded: a 1.0.x environment keeps working under 1.1 — rent up renews it, and rent down and rent sweep stop it by its whole session — but a 1.0.x watchdog still running from before the upgrade enforces its expiry with the old code, which can miss a child that outlived its shell.
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-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
(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. The hook doesn't wait for the server to die; it asks, and the environment's supervisor finishes the job (see How cleanup actually happens). rent down hello, typed by hand, waits and answers "stopped": true. rent events --summary afterwards shows the teardown and which cleanup layer did it.
If something doesn't behave, see Troubleshooting.
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):
[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.
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 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.
| Client | How it is wired | Session-end cleanup |
|---|---|---|
| Claude Code | the plugin (guide), or rent init (.mcp.json + .claude/settings.local.json) | Yes — SessionEnd releases the session's claim (rent down --all --cwd … --reason session-end); SessionStart runs rent sweep |
| Codex | the plugin (guide) | Yes, once you trust the plugin's hooks (/hooks) — the same hooks; until then, expiry and 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 plugin (Claude Code or Codex) 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@ : 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.
Every environment has its own supervisor: a small rentctl process that starts your command inside a POSIX session it owns and stays until that whole session — the command, its children, and anything they spawned — has been checked and found empty. The lease is written before the command starts, so there is no moment when something is running that rentctl has no record of.
Every stop, whatever asked for it, is the same: SIGTERM to every process in the session, SIGTERM to anything forked while it shuts down, SIGKILL to each process still there after 10 s, then a final scan. A child that ignores SIGTERM, or that outlived the shell that launched it, is stopped with the rest.
Four independent layers decide *when*, so no single failure leaves an orphan:
rent down.SessionEnd hook releases that session's claim on everything leased to that directory. The server stops if no other session still holds a claim.rent sweep (which the hooks also run at session start) and rent ls clean up what the first three missed, including an environment whose supervisor was killed.What `rent down` tells you is what the lease says, in one of three shapes:
"stopped": true — the session was scanned empty and the lease removed."stopped": false, "state": "cleanup_incomplete", "survivors": [...] — something could not be stopped (a child that switched user with sudo, say). The lease is kept, so the survivors stay named and the port stays out of the draw; the supervisor keeps retrying (5 s, 15 s, 60 s, then every minute), rent down retries at once, and rent up refuses with CLEANUP_INCOMPLETE until it clears."stopped": null, "pending": true — the stop was accepted and is still running; rent ls shows how it ended. This is "ok": true: the supervisor finishes regardless.rent down waits up to 15 s for the first two (--wait SECONDS; --wait 0 returns at once). The MCP tool env_down takes the same wait as wait_s — 15 s by default, 60 s at most.
Session end returns fast; cleanup finishes in the background. Claude Code gives a plugin's SessionEnd hook about 1.5 s. So the hook's rent down --all --reason session-end doesn't wait: it records a stop for every lease that no other session still holds, wakes each supervisor, and returns pending. The supervisors don't need the session to finish. A lease with no living supervisor is handed to a detached recovery process rather than stopped halfway.
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.
If a supervisor is killed, recovery waits for the next command. The server keeps serving. The next rent ls, sweep, up or down notices, marks the lease unsupervised, and records supervisor_lost; it is not killed early. At its expiry the next of those commands stops it, and rent down stops it now. Nothing notices at the moment the supervisor dies, because nothing else is running to notice.
There is no daemon. Th
Only the beginning is shown; read the rest in the repo