Skip to content

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.

Terminal window
claude mcp add rexenv -- rex mcp # this project only
claude mcp add --scope user rexenv -- rex mcp # every project, on this machine

The 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.

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.rex needs destroy permission — Agent access at 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…

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_package is what makes an edit visible. Nothing the site does can write back to your source.
  • wp_run refuses the arguments that would re-target it — --path, --url, --ssh, --http and @aliases. rexenv decides which site the command runs against, from site_id. A non-zero exit comes back as an answer, not a tool failure.
  • A login link, never a password. scratch_login_url is 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_user with the login_url action 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_query on your own site is SELECT-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_inbox is 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_read is Changes; delete and clear are 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_run and site_artisan are a shell as you in one of your sites, which is why they are Full. The same re-targeting arguments as wp_run are 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 and wp 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.

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.

  • 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.rex is 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_info and site_inspect_folder read everything and name nothing on disk. site_info also 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_status carries 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.