The MCP server
@mindantic/localrun-mcp exposes LocalRun to MCP clients. Give a folder a .test address, start
it, read its logs, fix its env, from a chat window instead of the tray.
You: Give this folder a .test address and start it
Claude: Added notes at https://notes.test, running on port 3000Requirements
macOS, and the LocalRun app. The app owns the DNS responder, the proxy, and the certificate; the
package is a thin wrapper around the daemon's unix socket. The package declares "os": ["darwin"]
so npm refuses to install it elsewhere; on Windows a socket path means a named pipe, and it could
not connect even in principle.
Install
The MCP tab in LocalRun's menu-bar panel is the shortest path. It writes the client config, and repairs it later if you move or rename the app.
By hand, for Claude Code:
claude mcp add localrun --scope user -- npx -y @mindantic/localrun-mcpFor Claude Desktop, add this to
~/Library/Application Support/Claude/claude_desktop_config.json and restart the app:
{
"mcpServers": {
"localrun": { "command": "npx", "args": ["-y", "@mindantic/localrun-mcp"] }
}
}The app ships the MCP bundle inside itself, so the Connect button registers two absolute paths
into LocalRun.app rather than an npx command. That is faster and works
before the package is on your machine, but it breaks if you move the app, rename it, or reinstall
to ~/Applications. LocalRun checks every path in the entry at launch, rewrites the
broken ones, and turns the row's button into Reconnect when it cannot. An
npx entry has no paths to break.
Tools
Nineteen tools, against the daemon's 38 JSON-RPC methods. The gap is deliberate: the tool list and the set of RPCs the server is allowed to reach are both pinned by a test, so widening either is a conscious edit rather than a side effect.
Looking around
| Tool | What it does |
|---|---|
list_sites | Every site, its address, and whether it is running |
get_site | One site in full: address, folder, command, port |
read_logs | Recent output from a site, to find out why it is failing |
check_setup | Whether .test addresses and HTTPS are working |
find_running_servers | Dev servers already running that LocalRun does not manage |
preview_folder | What LocalRun would do with a folder, without adding it |
Changing things
| Tool | What it does |
|---|---|
add_site | Give a folder an address |
add_site_for_port | Give an address to something already on a port |
adopt_running_server | Take over a server you started yourself |
start_site / stop_site / restart_site | Run control |
rebuild_site | Run the build command, then restart |
update_site | Name, start command, build command, port, https |
set_env | Environment variables, merged, never replaced |
set_keep_running | Manual, with LocalRun, or always |
remove_site | Remove from LocalRun |
run_setup | Fix the system setup (macOS asks for your password) |
import_from_caddy | Move sites out of an existing Caddyfile |
Tools that act on an existing site accept a name, an id, or any path inside the project folder. The
daemon is inconsistent about which it wants (update and remove need the exact id, everything else takes either), so the MCP server resolves it for you.
What it deliberately cannot reach
Some RPCs have no safe agent-facing shape, so the server has no route to them at all:
| Blocked | Why |
|---|---|
projects.upsert | Writes a whole project record; update_site has a field allowlist instead |
projects.openTerminal | Opens a window and types a command into your shell |
projects.startAll | One call, arbitrary blast radius |
settings.set, autostart.* | Machine-level configuration, not project work |
caddy.trust, caddy.provision, caddy.sync | Raw proxy taps; run_setup is the supervised version |
No confirmation prompts
There are none, on purpose.
An earlier build issued a confirmation token the agent had to echo back before a destructive call went through. A real Claude session defeated it in one turn, by echoing it. Anything the agent can read, the agent can return, so that guard was friction that proved nothing, and it is gone.
What replaced it is honesty about consequences plus a few hard limits:
remove_sitenever touches your files. It removes the entry; the folder and everything in it is left alone, andadd_siteputs it straight back. The action is reversible, so it does not need a gate.start_sitewill not take a port from another program. If something else holds the port it reports what, and does nothing, unless you passtakeOver: true.set_envmerges. Variables you do not mention are kept, and values are never read back.update_sitehas a hard field allowlist. It cannot reach the folder, the working directory, env, or the autostart tier.import_from_caddyinverts the daemon's defaults. Hostnames are required rather than inferred, andremoveOriginaldefaults to false, so your Caddyfile is left alone unless you ask.
Destructive tools carry the MCP destructiveHint annotation, so a client that prompts before such
calls still can.
The daemon listens on an unauthenticated unix socket. Any process running as you can drive it directly, with or without this package. Treat the MCP server as a convenience layer, not a sandbox.
Configuration
| Variable | Default | Purpose |
|---|---|---|
LOCALRUN_DIR | ~/.localrun | Where LocalRun keeps its state |
LOCALRUN_DAEMON_SOCK | $LOCALRUN_DIR/daemon.sock | Socket path override |
LOCALRUN_MCP_NO_LAUNCH | unset | 1 stops it opening LocalRun.app automatically |
If LocalRun is not running, the server opens it and waits. If it cannot, it returns instructions rather than failing silently.
When the tools are not there
The client shows the server as connected but no LocalRun tools appear, or every call errors. Check, in order:
claude mcp list # is it registered at all?
npx -y @mindantic/localrun-mcp # does it start standalone? (Ctrl-C to exit)
ls -l ~/.localrun/daemon.sock # is the daemon actually up?A missing socket means LocalRun is not running and could not be launched. A registration pointing at a moved app bundle shows up in the MCP tab as Reconnect.
npm: @mindantic/localrun-mcp.