Skip to main content
Glama
README.md
# rex-mcp

Let an AI agent read, type into, and run commands in [Rex](https://www.superlogical.com/rex) terminal blocks on your own machine, through the `rex` CLI.

rex-mcp is an MCP server over stdio. Any MCP client that spawns local servers (pi, Claude Desktop, Cursor desktop, and others) can connect to it. Ask the agent to list your sessions, read a block's screen, type a command into a block, or run a command and wait for it to finish.

It follows the design of herdr-mcp: one passthrough tool that runs a fixed allowlist of CLI verbs, plus a skill tool that serves the CLI's own guide. The differences are the transport (stdio instead of HTTP with a bearer token) and the CLI underneath.

## What you need

- **Rex** installed, with its app or server available on this machine. The CLI finds the local server on its own; like plain `rex`, a call may autostart a missing server.
- **Node.js 22 or later.**
- **`rex` on the PATH** of the process that spawns the MCP server, or `REX_MCP_BINARY` pointing at it. Inside Rex terminals this is automatic; outside, check `which rex`.

## Install

```sh
git clone <this repo> && cd rex-mcp
npm install
npm test
```

## Run it

The server speaks MCP over stdio, so a client spawns it; running it by hand just waits for JSON-RPC on stdin. Point a client's MCP config at it:

```json
{
  "mcpServers": {
    "rex-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/rex-mcp/bin/rex-mcp.mjs"]
    }
  }
}
```

Or install the package and use the binary: `npm install -g rex-mcp` (or `just link` from a checkout), then set `"command": "rex-mcp"`.

Optional environment variables:

| Variable | Default | Meaning |
| --- | --- | --- |
| `REX_MCP_BINARY` | `rex` | Path to the `rex` executable. `REX_BIN_DIR` is used as a fallback parent directory. |
| `REX_MCP_WAIT_MAX` | `90000` | Wait ceiling in milliseconds for `wait` calls. |

## Tasks

`just` drives everything; run `just` with no arguments to list recipes.

| Recipe | What it does |
| --- | --- |
| `just install` | Install dependencies. |
| `just test` | Run the test suite, no Rex server needed. |
| `just run` | Start the server on stdio. |
| `just live` | End-to-end probe against the real Rex server (creates and closes two blocks). |
| `just pack` | Build the npm tarball into `dist/`. |
| `just link` | `npm link` so `rex-mcp` is on PATH. |
| `just publish` | Publish the current version to npm. |
| `just clean` | Remove `dist/`. |

## Package and release

`npm pack` ships only `bin/`, `src/`, README and LICENSE (see the `files` field), and `prepublishOnly` runs the tests first.

CI (`.github/workflows/ci.yml`) runs the suite on push and pull requests across Ubuntu and macOS with Node 22 and 24.

Releases (`.github/workflows/release.yml`) run when you push a `v*` tag: tests, tarball, GitHub release with the tarball attached, then `npm publish`. Setup, once:

1. Push the repo to GitHub and `git init` + commit if you have not already.
2. Create an npm automation token and add it as the repository secret `NPM_TOKEN`.
3. Cut a release: `npm version patch && git push --follow-tags`.

For a local release without CI: `just pack`, then `npm publish` (or `just publish`).

## Tools

**`rex`** runs one `rex` command. Supply exactly one of `args` (an array of strings) or `command` (a quote-only string; no shell operators, substitution, or expansion).

Allowed verbs, with what the bridge does to them:

| Verb | Notes |
| --- | --- |
| `list`, `ls` | List sessions. |
| `session ls/inspect`, `window ls/inspect` | Discovery. |
| `block ls/inspect/types/capture/send/send-key` | Inspect and drive one block. |
| `client ls/inspect` | See connected clients. |
| `api ls/list/describe` | Read the server's control API surface. |
| `capture` | Print a block's screen (`text`, `html` or `vt`). An explicit target is required. |
| `send`, `send-key` | Type into a terminal. An explicit target is required: `-b BLOCK`, `-w WINDOW`, or `-s SESSION`. |
| `run` | Start a command in a new block; add `--wait` for its exit status and `--keep-open` to read its output with `capture` afterwards. |
| `wait` | Block until a target ends. The bridge pins `--for` at the ceiling (default 90 s). |
| `help` | Read command syntax. |

Listing and inspection calls get `--json` appended, so output is stable to parse. Failure envelopes carry a `code`, a `message`, and recovery advice in `next`. Rex's exit statuses map as: 2 invalid arguments, 3 target not found or ambiguous, 4 server unreachable, 130 cancelled.

The bridge runs outside any Rex terminal, so there is no default session or block: scope calls explicitly. Two patterns cover most work:

```sh
# Run a command and read its output
rex run -s SESSION --wait --keep-open -- CMD   # exit status comes back; block stays open
rex capture -b BLOCK --trim                    # the command's output

# Work in an existing terminal
rex send -b BLOCK 'make test'                  # type; delivery is not completion
rex wait BLOCK --for 90s                       # exit status when the command ends
```

**`rex_skill`** returns the installed CLI guide verbatim. Read it once before the first `rex` call.

## What is disabled

`do` (Lua), `events`, `block stream`, `api call`, `attach`, every create, layout, rename, close, and kill command, and `-S` server routing. The bridge drives the local Rex server only. This keeps an agent from deleting sessions, rearranging layout, or reaching other hosts through the MCP surface.

## Wait semantics

Rex blocks are terminals, not agents. `wait TARGET` returns when the target's child process exits, with that exit status; `wait --for` bounds the wait and returns non-zero on timeout. There is no idle/working/done state to poll. To check on a running command, `capture` the block and read the screen; to wait for a command to finish, call `wait` again with the same target. On `wait_timeout`, capture first: the reply may already be on screen.

## Risks

The `rex` tool types into your real terminals and starts real processes with the permissions of the MCP client. A malicious prompt or document can steer an agent into using that access. Terminal output may contain passwords, keys, or private work, and a `capture` can surface them into agent context. The allowlist reduces accidents, not intent; treat a connected agent as someone typing at your keyboard.

## Development

```sh
just test
just live
```

The tests cover argument splitting and allowlist rules, wait budgeting, exit-status mapping, the spawn runner, and a full MCP handshake over stdio against a fake `rex` binary, so they run without a Rex server. `scripts/live.mjs` is the same handshake against your real Rex server; it creates two short-lived blocks and closes them at the end.

## Credits

rex-mcp is inspired by [herdr-mcp](https://github.com/IgorWarzocha/herdr-mcp) by Igor Warzocha, the MCP bridge for [Herdr](https://github.com/herdrdev/herdr). The tool design (a passthrough CLI tool with an allowlist plus a skill tool) and parts of the bridge code are adapted from it, under the MIT licence. rex-mcp targets Rex instead of Herdr, runs over stdio instead of an authenticated HTTP tunnel, and waits on terminal process exits instead of agent state.

## Licence

[MIT](LICENSE) © Edgardo Yoliani. Contains code adapted from [herdr-mcp](https://github.com/IgorWarzocha/herdr-mcp), MIT © Igor Warzocha; see the notice in [LICENSE](LICENSE).

TDQS

A4.4/5.0

Scored across 2 tools

Disambiguation4/5

rex_skill only retrieves the installed Rex CLI guide, while rex executes scoped CLI commands against the local server; the documentation-vs-execution split is understandable. There is minor overlap because rex also exposes help subcommands and refers back to rex_skill, but the primary purposes are distinct.

Naming Consistency3/5

Both tools share the rex namespace, but the set does not follow a predictable verb_noun pattern: rex is a bare command name and rex_skill is a noun-like guide tool. The names are readable but not self-explanatory without descriptions.

Tool Count3/5

Two tools is thin for a CLI bridge, but one is an omnibus executor and the other provides necessary version-specific documentation. Each earns its place, though the surface is intentionally narrow rather than comfortably scoped.

Completeness3/5

The set covers discovery, inspection, capture, send/send-key, run, wait, and help, so core terminal interaction is available. However, lifecycle operations such as create, close, kill, rename, and layout are explicitly disabled, along with events/streaming, API calls, and server routing, leaving notable gaps.

Maintenance

ActivityMaintained
ResponsivenessNo issues