# Usage

`@testmaze/mcp` is a thin stdio→HTTP bridge. You install it into your coding agent's MCP config,
give it a workspace token, and it connects the agent to your Test Maze instance. There's nothing to
run yourself — your MCP client launches it via `npx` on demand.

## 1. Prerequisites

- A **Test Maze workspace** (a Space).
- A **per-space token** (`tmt_*`) — see below.
- Node.js **20+** on the machine running the agent (for `npx`).

## 2. Get a token

The same steps are printed by `npx -y @testmaze/mcp token`, and whenever the wrapper cannot find a
token.

1. Sign in at <https://testmaze.com/login> (or <https://testmaze.com/register> to create an account
   — a workspace is created for you).
2. Open the workspace the agent should work in. Its id is the first path segment of the URL once
   you are inside, e.g. `https://testmaze.com/aasp-xxxx-…/dashboard`.
3. Go to **Settings → MCP Tokens** — direct link `https://testmaze.com/<workspace-id>/settings/mcp`.
   You need admin rights on the workspace to see this tab.
4. Enter a name that identifies *this* machine or agent (e.g. "Claude Code on my laptop") and click
   **Create token**. One token per machine lets you revoke a single laptop later without disturbing
   anyone else.
5. Copy the token from the dialog. It is a long signed string (`eyJ…`, shown as `tmt_xxx` in
   examples) and is shown **once**. If you lose it, click **Regenerate** on that row for a new
   value (the old one stops working immediately).

It's a signed, space-scoped credential: it tells the server which space to act on and which user to
attribute. Tokens are scoped to the workspace they were issued in — to connect a second workspace,
repeat from step 3 inside that workspace. Self-hosted? The sign-in and token pages are on your host;
pass `--url https://your-host` to `token` and `init`.

## 3. Store the token in the project

MCP client configs (`.mcp.json`, `.cursor/mcp.json`, …) are meant to be committed, so the token
must not live in them. Instead, run once per clone, from the project root:

```sh
npx -y @testmaze/mcp init tmt_xxx
```

`init`:

1. Verifies the token against the server and prints the **workspace** it is scoped to, the
   **user** every action will be attributed to, and the token's label — so you know what you are
   connecting before anything is written.
2. Writes `.env.testmaze` in the current directory (mode `600`) with `TESTMAZE_MCP_TOKEN` and
   `TESTMAZE_MCP_URL`. Existing keys are updated in place; other lines are preserved.
3. Adds `.env.testmaze` to `.gitignore` when one exists. If there is no `.gitignore` it warns
   instead — add the entry yourself before committing.

A rejected token (revoked, regenerated, wrong instance) exits non-zero and writes nothing.

Each teammate runs `init` with their **own** token. The file is per-clone and never shared.

### Where the wrapper looks for credentials

First hit wins, for the token and the URL independently:

1. `--token` / `--url` flags
2. `.env.testmaze` in the working directory or any parent directory — what `init` writes
3. `TESTMAZE_MCP_TOKEN` / `TESTMAZE_MCP_URL` environment variables
4. `.env` in the working directory or any parent directory (fallback — handy if you already keep
   secrets there, but prefer `.env.testmaze` so the token does not leak into your app's own env)

The project file deliberately outranks the environment variable. An env var is normally a
machine-wide default — set in an editor's MCP config or a shell rc — and a stale one would
silently point every repo at the same workspace. `init` is per project and must always take
effect. When two sources hold different tokens the wrapper logs which one it ignored
(`NOTE a different token in … is being ignored`), `whoami` prints it as `Ignored`, and `init`
warns if the file it just wrote would not be the one used.

MCP clients launch the wrapper with the project root as the working directory, so a
`.env.testmaze` at the root is found from any subdirectory too.

**Changes are picked up live.** The wrapper re-checks the files before every frame it forwards, so
re-running `init` with a different token (for example to point the same folder at another
workspace) takes effect on the agent's next tool call — no reconnect or editor restart needed. Only
a token given via `--token` is pinned. The switch is logged to stderr as `credentials changed`.

**Not sure which workspace a folder points at?** Run `npx -y @testmaze/mcp whoami` there. It
resolves the token exactly as the proxy does, verifies it, and prints the workspace, user, token
label and the file it came from. Inside a session, ask the agent to call the `project.whoami` tool
— same answer, from the server's point of view.

**One registration, many projects.** Because the token is resolved from the working directory, a
single user-level registration (`claude mcp add tm -- npx -y @testmaze/mcp` without
`--scope project`) is enough for every repo on the machine: each repo carries its own
`.env.testmaze` and therefore its own workspace. `--scope project` is still the better choice for
teams, since teammates then get the wiring on clone.

## 4. Register the server with your agent

None of these carry a secret — commit them.

### Claude Code

```sh
claude mcp add tm --scope project -- npx -y @testmaze/mcp
```

`--scope project` writes `.mcp.json` next to `.env.testmaze` so the whole team gets the wiring on
clone. Drop the flag to register it in your user profile instead.

**Why `tm`?** The name you register is the prefix Claude Code uses for everything from this server:
tools appear as `mcp__tm__quality.smell_check` in permission prompts, prompts as slash commands
`/mcp__tm__code-smell-review`, and resources as `@tm:testmaze://kb/<space>/pattern/strategy`
mentions. Two letters beat eight every time you type them. Any name works — an existing `testmaze`
registration just uses that longer prefix.

### Cursor / Cline / Gemini CLI / generic MCP client (JSON)

```json
{
  "mcpServers": {
    "tm": {
      "command": "npx",
      "args": ["-y", "@testmaze/mcp"]
    }
  }
}
```

### TOML-configured clients (Codex CLI)

```toml
[mcp_servers.tm]
command = "npx"
args = ["-y", "@testmaze/mcp"]
```

Once connected, your agent's first tool call opens a live **agent session** in Test Maze
(sidebar → Agent Sessions), where you can watch the verify loop happen.

### Explore with your own browser

If your agent also has a browser MCP (Playwright MCP is the common one), it can run exploratory
testing against your app and record it in Test Maze without Test Maze ever launching a browser:

```
/mcp__tm__explore-feature url=https://staging.example.com goal="guest checkout"
```

The prompt drives the loop: `exploration.start` → (act in the browser → `exploration.observe`)* →
`exploration.finish` → `exploration.to_cases` → `case.create_batch`. Each observe returns what the
server saw (elements with stable locators, forms with sample values, suggested assertions), whether
the last action did what was expected, coverage against a budget, and ranked next actions. A `hover`
next action means "hover, then observe again" — that is how menus are discovered. The session,
with every verified step, is visible under Agent Sessions and via `exploration.get`.

### Independent verification (verifier subagent)

Planning, writing tests, exploring and coding all happen in your agent's normal context. **Grading
does not**: `init` installs `.claude/agents/testmaze-verifier.md`, a Claude Code subagent (Cursor
reads `.claude/agents/` too). When a feature is ready, your agent hands the feature's suiteId to
`testmaze-verifier`, which, in its own context and without Write/Edit tools:

1. calls `feature.verify` (read-only without a sessionId) and follows the `verification.instructions`
   it returns — the server's `verify-feature` prompt, so the steps never go stale in your repo;
2. creates a test run tied to the current commit, runs every acceptance case against the running app,
   records results with `recordedBy: "verifier"`, and grades them with `pdlc.verify`;
3. returns only the verdict and what it observed on failing cases.

`pdlc.verify` reports `independence` (how many graded results came from a verifier vs. the
implementing agent); it never changes the verdict. You can also ask for it explicitly:
*"Use the testmaze-verifier subagent to verify the password reset feature."*

**Which tools get a verifier.** `init` always writes `.claude/agents/testmaze-verifier.md`, and adds a
file for each other tool the project shows signs of using (or exactly the ones you list with
`--agents claude,codex,copilot,gemini`):

| Tool | File | Separate context | Edits blocked | Delegation |
| --- | --- | --- | --- | --- |
| Claude Code | `.claude/agents/testmaze-verifier.md` | yes | `disallowedTools: Write, Edit, NotebookEdit` (the shell can still write) | automatic, from the description |
| Cursor | same file (Cursor reads `.claude/agents/`) | yes | `readonly: true`, which also blocks state-changing shell commands | automatic, or `/testmaze-verifier` |
| VS Code Copilot Chat | same file (VS Code reads `.claude/agents/`) | yes | `disallowedTools` is mapped to VS Code tools | the main agent may delegate; or ask for it |
| Copilot CLI / coding agent | `.github/agents/testmaze-verifier.agent.md` (when `.github/copilot-instructions.md`, `.github/agents/` or `.github/instructions/` exists) | yes | `tools` allowlist without `edit`; `execute` can still write | name it in the prompt, `/agent`, or `--agent` |
| Codex CLI | `.codex/agents/testmaze-verifier.toml` + a managed block in `AGENTS.md` (when `.codex/` exists) | separate thread | `sandbox_mode = "read-only"` | via the `AGENTS.md` block: Codex does not delegate on a description alone |
| Gemini CLI | `.gemini/agents/testmaze-verifier.md` (when `.gemini/` or `GEMINI.md` exists) | yes | `tools` allowlist without write tools; `run_shell_command` can still write | automatic, or `@testmaze-verifier` |
| Cline | — | — | — | no per-project agent format; the agent records results itself (`recordedBy: "implementer"`) |

Caveats: outside Cursor and Codex the verifier keeps a shell, so "cannot edit" is a strong instruction
plus the removal of the editing tools, not a sandbox. Codex's read-only sandbox may also block the
shell's network access (e.g. `curl` to a local app); MCP tools such as a browser MCP are not affected.
The Copilot file allowlists your Test Maze server by the name found in `.mcp.json`, `.vscode/mcp.json`
or `.cursor/mcp.json` (falling back to `testmaze` and `tm`) plus `playwright`; re-run `init` if you
rename the server.

**Cheaper verifier model.** The files set no `model`, because each tool spells models differently;
the verifier inherits the main model. Running tests and comparing results is mostly mechanical, so in
Claude Code you can set `CLAUDE_CODE_SUBAGENT_MODEL=sonnet` (or `haiku`); in Codex and Gemini, add a
`model` to their file and remove its `managed-by` line so `init` leaves it alone.

Commit the files so teammates' agents grade the same way. Re-running `init` refreshes them; remove the
`managed-by` comment line from a file to keep your own edits, or pass `--no-agents` to skip them all.

## 5. CLI reference

```
testmaze-mcp init <tmt_xxx> [--url https://testmaze.com] [--agents LIST | --no-agents]
testmaze-mcp [--token tmt_xxx] [--url https://testmaze.com]

Commands:
  token          Print step-by-step instructions for creating a token in the web app.
  init <token>   Verify the token, print workspace + user, write .env.testmaze, gitignore it,
                 and install the verifier agents (.claude/agents/ always; Codex, Copilot
                 CLI and Gemini CLI files when the project uses those tools).
  whoami         Resolve credentials for this directory as the proxy would; print the
                 workspace, user, token and the file the token came from.
  (none)         Run the proxy — this is what the MCP client launches.

  --token, -t    Per-space token. Overrides every other source.
  --url,   -u    Backend URL. Default: https://testmaze.com.
  --agents LIST  init: install exactly these verifier agents (claude,codex,copilot,gemini).
  --no-agents    init: install no verifier agent files.
  --version, -v  Print version and exit.
  --help,    -h  Print help and exit.
```

## 6. Self-hosted / custom backend

Pass `--url` to `init`; it is stored in `.env.testmaze` alongside the token, so the editor config
needs nothing extra:

```sh
npx -y @testmaze/mcp init tmt_xxx --url https://your-host
claude mcp add tm --scope project -- npx -y @testmaze/mcp
```

The wrapper verifies the token at `${url}/api/v1/mcp/tokens/exchange` and connects to
`${url}/api/v1/mcp`. Setting `TESTMAZE_MCP_URL` in the client's `env` block also works and takes
precedence over the file.

## 7. Troubleshooting

| Symptom | Likely cause / fix |
|---|---|
| `missing token` on startup | No `.env.testmaze` (or `.env`) found from the working directory upward, and `TESTMAZE_MCP_TOKEN` is unset. Run `npx -y @testmaze/mcp init tmt_xxx` in the project root — or `npx -y @testmaze/mcp token` if you still need to create one. |
| Agent can't connect / `tools/list` is empty | Check the wrapper's stderr line `connecting to … (token: tmt_… from <source>)` — it names the file or env var the token came from. |
| `401` / unauthorized | Token revoked or rotated. Issue a fresh one in Settings → MCP Tokens and re-run `init`. |
| `init` says the token was rejected | Revoked, regenerated, or issued by a different Test Maze instance — check `--url`. Nothing was written. |
| Connects but wrong workspace | The token is scoped to the space it was issued in. Issue one from the intended space. |
| Connection refused / times out | `TESTMAZE_MCP_URL` points somewhere unreachable. It defaults to `https://testmaze.com`; only override it for a self-hosted backend. |
| `npx` can't find the package | Network/registry issue, or Node < 20. The package is public: `npx -y @testmaze/mcp --version` should print a version. |

For what the agent can actually *do* once connected, see [functionality.md](https://unpkg.com/@testmaze/mcp@latest/docs/functionality.md).
