AI agents (MCP)
rexenv can expose its tools to an AI agent over MCP, so a coding agent can create a
throwaway WordPress site, install the plugin you are working on, run WP-CLI against it and read
the mail it sent — and, if you turn the dial up, work on the sites you made yourself. Every rex command has an agent tool, and one setting decides what an agent may do with
your own sites.
The paragraph above the toggle is the one a person reads before handing an agent the inbox, and the app holds it to its sentences with a guard. The part that matters before you flip it:
With the endpoint on, an agent can look at your sites — status, logs, users and content, read-only (password hashes and API keys are in there) — and it can create disposable “scratch” sites of its own, put code into them and run it. It cannot change or delete the sites you made yourself unless you turn Agent access up below, for this session, 7 days or always: that refusal lives in rexenv, not in the agent’s good behaviour.
Connecting a client
Section titled “Connecting a client”claude mcp add rexenv -- rex mcp # this project onlyclaude mcp add --scope user rexenv -- rex mcp # every project, on this machineThe card offers both, with the difference stated, because the thing people are surprised by is
rexenv set up in one repo and missing in the next. It also has the JSON form for editors that
read an MCP config file (Cursor, VS Code). rex mcp is a stdio bridge meant for a client’s
config — run by hand it sits waiting for JSON-RPC. If rex is not found, install it from
Settings → General → Command-line tool and reconnect.
The endpoint needs the app running, not merely installed. rexenv lives in the menu bar and closing its window does not quit it, so an agent’s connection survives the window. If the app does quit mid-call, the bridge answers that call in-band rather than hanging up: the agent is told that rexenv stopped while this call was in progress, that whether the operation finished is unknown from there, and to reconnect and check before retrying anything that creates or changes something — because a bare disconnect is exactly where a model starts guessing.
The Agent access dial
Section titled “The Agent access dial”Settings → AI agents (MCP) → Agent access is one dial, global — never per site, never per client — with three levels. The sentence under each level is served from the app, so the prompt, the refusal and the card cannot drift:
| Level | In the app’s words, an agent can… |
|---|---|
| Read | look at any of your sites — status, content, users, logs, every site’s mail (password-reset links included) and their databases, read-only (password hashes and API keys are in there) — and create disposable sites of its own; it cannot change anything you made |
| Changes | change how any of your sites is served and what is installed in it — PHP version, web server, Xdebug, plugins and themes on or off, options, restarts, dry runs, blueprints — and start or stop rexenv’s stack (macOS still asks for your password) |
| Full | do everything Changes allows, and also delete or reset a site and its database, run a live search-replace or a database import, publish a site to the internet for up to an hour (anyone with the link reaches it until rexenv stops the share), and run commands and code of its choosing in any of your sites, as you |
Read is on whenever the endpoint is. Reads need no door: the socket is private to your user, and what a read returns is what the app’s own screens show. A level above Read carries a duration — this session (gone at the next launch), 7 days (an expiry stamp), or always. A 7-day setting that has expired reads as Read, and the card says so instead of silently showing Read. A level above Read survives the endpoint being turned off, so the card states it while off — “Agent access is set to Full · Always and applies as soon as you turn this on” — rather than re-activating it silently, with a Set it back to Read button beside the sentence.
The dial answers at a glance: the Agent access heading ends in a chip with the level standing
and its duration — Changes · 7 days — and Back to Read now sits under the duration, so
turning it down is one click from wherever you are looking.
run sits at Full and not at Changes on purpose: what it hands over is not data but a shell as
you over your real projects. There is no per-call prompt anywhere any more — not for the
database, not for mail, not for publishing. Where an operation needs an administrator password,
macOS asks, and rexenv does not ask a second time.
The refusal an agent reads when the dial is below what a call needs names the dial, the level needed, the level standing, and where it is set — never a site the agent could not otherwise see:
deleting
shop.rexneedsdestroypermission —Agent accessat Full or above in rexenv — and it is at Read. The person you’re working with can change it under Settings → “AI agents (MCP)” → “Agent access” — for this session, for 7 days, or always. That is their decision, not something an agent can change; with it at Full, an agent can do everything Changes allows, and also delete or reset a site…
Scratch sites — what an agent owns
Section titled “Scratch sites — what an agent owns”A scratch site is a disposable WordPress site the agent creates, at
<name>.scratch.<tld>. It is the agent’s, so the tools below work on it with no permission at
all. Five is the default cap, and a scratch site expires on its own after 24 hours
unused. Both are settings — scratch_cap from 1 to 20 and scratch_ttl_hours
from 1 to 168, writable with rex config set. A site of yours that happens to be called
mine.scratch.rex is not one of these — see below.
| Tool | What it does |
|---|---|
scratch_create_site |
Create a scratch WordPress site (optional PHP minor) |
scratch_delete_site |
Delete one — refused on any site you made yourself |
scratch_add_package |
Copy a plugin/theme directory in, detected from its own header |
scratch_sync_package |
Re-copy it, so the site runs your latest code |
scratch_login_url |
A one-time wp-admin login link — single use, two minutes, this machine |
wp_run |
One WP-CLI command inside the scratch site |
set_php_version |
Switch that site’s PHP minor — the compatibility matrix |
db_query |
One SQL statement, and on a scratch site it may write |
mail_list, mail_get |
Read mail that scratch site sent |
Three limits worth knowing before you plan around them:
- A package is copied, not symlinked. The site runs a snapshot of your code as of the
moment it was added;
scratch_sync_packageis what makes an edit visible. Nothing the site does can write back to your source. wp_runrefuses the arguments that would re-target it —--path,--url,--ssh,--httpand@aliases. rexenv decides which site the command runs against, fromsite_id. A non-zero exit comes back as an answer, not a tool failure.- A login link, never a password.
scratch_login_urlis the same magic login the app’s Log in as button uses: single-use, expires in two minutes, only works from this machine, never recorded, and changes nothing about the account. For one of your own sites,wp_userwith thelogin_urlaction does the same — at Read, because a link is not a credential.
Your own sites — every rex surface, as a tool
Section titled “Your own sites — every rex surface, as a tool”Each thing the CLI can do has a tool, and each tool decides its level per
action: wp_plugin with list is Read, activate is Changes, delete is Full. The tools,
by area:
| Area | Tools |
|---|---|
| Sites | list_sites, site_info, site_status, site_inspect_folder, site_create, site_configure, site_restart, site_retry, site_delete, blueprints, blueprints_list |
| WordPress | wp_info, wp_plugin, wp_theme, wp_user, wp_option, wp_maintain, wp_data, wp_network, wp_org_search, site_wp_run |
| Laravel | site_artisan, composer_link |
| Logs, mail, database | site_logs, tail_log, mail_inbox, db_query, db_import |
| Stack | stack_status, stack, php, php_settings, settings, settings_get, tld, open |
| Git assets | repo — the whole git/asset group as one tool: reads at Read, writes at Full |
| Sharing | share — status at Read, stop at Changes, start at Full |
| Migration | valet_import, connection_rewrite, db_import — gated by consequence |
| The feed | agent_activity — the agent can read its own record |
Three of them deserve their own sentence:
db_queryon your own site isSELECT-only — a read-only account on that one database, allowed at Read, one statement per call, at most 500 rows, and the reply says when there were more. On a scratch site the same tool may write.mail_inboxis the whole Mailpit inbox — every message every site on this machine sent, password-reset links for your own sites included. Listing and reading are Read, because the inbox is shared rather than a site’s;mark_readis Changes;deleteandclearare Full. The 0.4.0 rule that an agent saw only scratch-site mail is gone, and the dial’s Read sentence says so in as many words.site_wp_runandsite_artisanare a shell as you in one of your sites, which is why they are Full. The same re-targeting arguments aswp_runare refused, and rexenv screens the shape before the gate, so a refused shape never records an ask. Prefer the vetted tools when one fits; these are for a plugin’s own commands andwp eval.
Every app command is reachable by an agent, or ruled out by name. That is a build-time test rather than a table in a plan: a new command in the app fails the build until someone writes down which tool reaches it or why none ever will. The rulings that closed the last gaps, as actions on the tools above:
| Tool | New actions, and the level each needs |
|---|---|
stack |
Read: adminer, downloads, edge_conflict. Changes: catch_mail, adminer_update_check, adminer_theme, prefetch, retry_download, and the system actions stop_catching_mail, set_engine_version, adminer_update_apply, autostart |
tld |
Read: status (whose resolver file the TLD has, and how many sites use it) and policy (whether rexenv accepts the TLD at all, and why not) |
open |
Changes: terminal — the site’s folder in your own terminal app, a plain login shell rather than rexenv’s |
wp_info |
Read: core_versions (WordPress.org releases) and cli_packages (the WP-CLI packages directory, as ~/.wp-cli/packages) |
site_logs |
Full: clear — the web server, PHP and database logs are shared, so clearing one clears every site’s lines in it |
repo |
Read: watch_log, only for a watch running on that site |
set_engine_version restarts a running engine, and every site on it is offline meanwhile.
stop_catching_mail lets every site on this machine send real mail to real addresses. Both
are system actions, which sit at Changes on the dial like every system action — the dial has
three levels, not four.
stack_status now also carries the running build, start-at-login, the TLDs this Mac cannot
resolve, Firefox trust (counts only, no profile paths), FrankenPHP’s embedded PHP, the mail
catch-all switch, and each engine’s offered and selected versions.
Two answers got more honest. A site_create or scratch_create_site that half-builds names
the site that now exists — so the agent does not make another — and carries the provisioning
job’s own reason with the log path removed; it used to point at tail_log, which reads only a
WordPress debug log, so no tool held the reason at all. And site_retry answers with the
settled outcome, where it used to return the job’s first snapshot: running, every phase
pending.
Where a person’s click still has to happen, it is macOS’s. stack, php, settings, tld
and valet_import carry the system operations — tld’s status and policy and
stack’s reads are plain Read. Starting the stack binds port 443 and writing a resolver file
touches /etc/resolver, and the administrator dialog those raise is the consent. rexenv
does not ask twice.
The record
Section titled “The record”Every call lands in the activity feed under the Settings card — folded until you open it,
its header counting the calls (Recent activity · 12 calls, or · nothing yet) — and in a
file: mcp.log, which has its own AI agents (MCP) tab in Logs. The tab appears only once
the file exists, so a machine that never enabled MCP has no empty tab. A site’s own Logs view
has Only this site, which filters the feed to lines naming that site by id, so a rename
after the fact does not lose them. The feed’s summaries fold identifiers — a command and its
subcommand, never the arguments — so a password an agent was handed does not end up in a log
because the agent was logged.
What is refused, structurally
Section titled “What is refused, structurally”- An agent cannot change or delete a site you made below the level that allows it. That refusal lives in rexenv, not in the agent’s good behaviour, and the dial cannot be moved from the CLI or by an agent.
- Ownership is the recorded fact, never a guess from the domain: your own site called
looks-like.scratch.rexis a real site here and is refused like any other scratch operation. - The site view an agent receives can carry id, domain, name, type, PHP version, web server,
whether the site is serving, and who owns it. It drops the rest rather than redacting it —
no docroot path, no database name, no certificate or socket paths, and no per-site
environment variables, which is where people park real secrets.
site_infoandsite_inspect_folderread everything and name nothing on disk.site_infoalso carries the site’s resources — CPU and memory for a dedicated backend, requests and bytes in the last minute — but not its database size, because measuring that runs a database client and a read tool runs nothing. A field added to a site later cannot appear in agent output without somebody editing that view. - Every error reply is scrubbed: wp-cli’s own words, paths and credentials do not travel back through a refusal.
- No tool installs a rexenv update.
stack_statuscarries the version an offer names, read from an in-process snapshot — no signature check, no network — and that is the whole surface. Installing one would be asking rexenv to replace the process that enforces the dial bounding the agent, and the relaunch would kill the socket the answer returns on. It is a click in Settings → About and nowhere else — see Updating. - A share an agent starts is stopped by rexenv — after the minutes asked for, 60 at most, and when rexenv quits. An agent’s auto-stop timer ends only the share it started, never one you started yourself.