Documentation Index
Fetch the complete documentation index at: https://docs.useswarm.co/llms.txt
Use this file to discover all available pages before exploring further.
MCP Server
Swarm ships an MCP (Model Context Protocol) server that plugs directly into your AI coding editor. Instead of jumping to a browser or terminal, you can drive a full UX test loop from inside Claude Code, Codex, or Cursor with one prompt.
How it works
You (in your AI editor) → "test my signup flow"
↓
Editor calls dev_test → tunnels localhost → runs AI agents
↓
Editor calls dev_watch → gets structured issues + recommendations
↓
Editor fixes the code → calls dev_test again
The MCP server runs locally over stdio — no remote server, no extra services. It tunnels your localhost via Cloudflare so Swarm’s cloud agents can reach your dev server.
If your account has persistent tunnels enabled, the MCP server reuses your stable localhost hostname automatically. See Persistent Tunnels.
Install
One command registers the MCP with your editor and signs you in:
npx @useswarm/mcp@latest setup
What this does:
- Registers the MCP with Claude Code (via
claude mcp add) and Codex CLI (writes ~/.codex/config.toml).
- Opens your browser to the device-code login page.
- Stores the API key at
~/.useswarm/config.json (mode 0600).
After it finishes, open your editor and the four tools (dev_test, dev_watch, dev_status, dev_close) are ready to use.
Limit to one editor
npx @useswarm/mcp@latest setup --client claude # Claude Code only
npx @useswarm/mcp@latest setup --client codex # Codex CLI only
Register without signing in
For CI, containers, or shared machines using USESWARM_API_KEY env:
npx @useswarm/mcp@latest setup --skip-login
The MCP is registered but won’t run until a key is provided (via env or a later login call).
Authentication
Swarm uses API keys scoped to your account. Auth happens at setup time, in your terminal — never mid-session inside the editor.
| Command | What it does |
|---|
npx @useswarm/mcp setup | Register + sign in (one shot — what most users want) |
npx @useswarm/mcp login | Sign in only (you’ve already registered) |
npx @useswarm/mcp logout | Clear stored credentials |
npx @useswarm/mcp whoami | Check who you’re signed in as |
The MCP does not trigger an interactive login when the editor spawns it. If no key is found at startup, the MCP exits with a clear message telling you to run setup or login from a terminal. This avoids broken device-code prompts fighting your editor’s UI.
Switching accounts
npx @useswarm/mcp logout
npx @useswarm/mcp login
Manual setup
If you don’t want to use setup, you can register the MCP yourself.
Claude Code
claude mcp add useswarm -- npx @useswarm/mcp
npx @useswarm/mcp login # one-time, in a terminal
Codex CLI
Edit ~/.codex/config.toml:
[mcp_servers.useswarm]
command = "npx"
args = ["@useswarm/mcp"]
Then sign in once: npx @useswarm/mcp login.
Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"useswarm": {
"command": "npx",
"args": ["@useswarm/mcp"]
}
}
}
VS Code (Copilot)
Add to .vscode/settings.json:
{
"mcp": {
"servers": {
"useswarm": {
"command": "npx",
"args": ["@useswarm/mcp"]
}
}
}
}
Updating
npx @useswarm/mcp@latest setup
Same command, with @latest. It pulls the newest release from npm and rewrites the path in your editor’s config so the next launch picks up the new code. Then restart your editor — MCP processes are spawned at startup, so a running one won’t see the update.
Already signed in? setup notices, skips the login step, and finishes faster.
Power-user alternative
npm install -g @useswarm/mcp
useswarm-mcp setup
# upgrades become:
npm update -g @useswarm/mcp
The global path is stable, so you only run setup once.
Tools
The MCP exposes five tools that drive a test → fix → retest loop.
dev_list_swarms
List the saved persona swarms tied to your account (personal + active org). Use this to discover swarm IDs before passing one to dev_test.
Returns: { swarms: Array<{ id, name, description, agentCount, personaCount, createdAt }>, count }.
Example prompt:
Use dev_list_swarms to show me which Useswarm persona sets are saved on my account.
dev_test
Tunnel your localhost (or hit a public URL directly) and start an AI agent swarm test.
Parameters:
| Parameter | Required | Description |
|---|
targetUrl | Yes | Frontend URL (e.g. http://localhost:3000) |
goal | Yes | What agents should accomplish (e.g. “complete the signup flow”) |
swarmId | Conditional | UUID of a saved swarm (from dev_list_swarms). When set, runs that swarm’s stored personas instead of generating new ones. Mutually exclusive with userDescription/agentCount. |
userDescription | Conditional | Target audience (e.g. “first-time SaaS users”). Required when swarmId is not set. Drives persona generation. |
agentCount | No | Number of AI personas to generate (1-20, default 3). Ignored when swarmId is set — the saved swarm’s count is used. |
backendUrl | No | Separate backend URL (e.g. http://localhost:8080) |
backendPaths | No | Extra path prefixes for backend routing (e.g. ["/ws", "/v1"]) |
maxSteps | No | Cap on actions per agent |
provider | No | openai or anthropic |
model | No | Model override |
auth | No | Login credentials or session cookies — see Authenticated testing |
Returns: { batchId, dashboardUrl, _devLoop: { tunnel, nextStep, ... } }.
Example prompts:
Use dev_test to test http://localhost:3000 with goal “sign up and reach the dashboard”. The audience is first-time users.
Use dev_list_swarms, then dev_test on http://localhost:3000 with goal “complete checkout” using my “B2B Buyers” swarm.
dev_watch
Wait for a test to finish, then return structured findings.
Parameters:
| Parameter | Required | Description |
|---|
batchId | Yes | Batch ID returned by dev_test |
wait | No | Poll up to 3 minutes for completion (default true); false returns immediately with current status |
Returns:
{
status: "completed" | "failed" | "still_running" | ...,
batchId: string,
overallUxScore: number | null,
issueCount: number,
issues: Array<{
severity: "critical" | "high" | "medium" | "low",
title: string,
description: string,
recommendation?: string,
effortSize?: "XS" | "S" | "M" | "L" | "XL",
userImpact?: string,
dropOffRisk?: "high" | "medium" | "low" | "none",
agentCount?: number,
flaggedBy?: Array<{ personaName, personaIndex, stepNumbers }>
}>,
recommendedActions: Array<{ ... }>, // judge's concrete fix suggestions
judgeVerdict: { verdict, confidence, evidenceBlocks } | null,
synthesis: { executiveSummary, positives } | null,
agentResults: Array<{ personaName, status, uxScore, verdict, ... }>,
_devLoop: { tunnelClosed, previousTunnelUrl, nextStep }
}
Tunnel is torn down automatically when the test completes.
dev_status
Diagnose the MCP setup. Returns auth state, tunnel state, and any active test.
{
auth: {
configured: boolean,
valid: boolean | null,
apiUrl: string,
keyHint: string | null, // last 4 chars
recovery: string | null // actionable next step if something's wrong
},
tunnel: { active, url, provider, reachable, ... },
activeTest: { batchId, status, startedAt }
}
Call this first when something seems off — recovery will name the exact command to run.
dev_close
Tear down the active tunnel manually. Usually not needed — dev_watch auto-closes on completion.
Split frontend/backend
If your app runs the frontend and backend on separate ports, pass backendUrl and the MCP spins up a reverse proxy automatically:
Use dev_test on http://localhost:3000 with backend at http://localhost:8080. Goal: “create a new project”.
| Path | Destination |
|---|
/api/* | Backend |
/auth/* | Backend |
/graphql | Backend |
/trpc/* | Backend |
| Everything else | Frontend |
Custom API paths
Test http://localhost:3000 with backend http://localhost:8080
and extra backend paths /ws and /socket.io. Goal: "send a message in the chat".
Each entry must start with / and be longer than just /.
Authenticated testing
auth supports three modes. All accept an optional startUrl to land the agent on a specific page after authentication.
If mode is omitted, the MCP infers it from which fields you pass:
cookies array → cookie_injection
signupEmail → signup
- otherwise →
agent_login
Mode 1: agent_login (username + password)
The agent navigates to loginUrl (or the target URL if omitted), fills the credentials, and submits.
{
"auth": {
"mode": "agent_login",
"loginUrl": "http://localhost:3000/login",
"username": "test@example.com",
"password": "testpass123",
"startUrl": "http://localhost:3000/dashboard"
}
}
Use dev_test on http://localhost:3000 with goal “edit profile settings”. Auth: username test@example.com, password testpass123, login URL http://localhost:3000/login.
Mode 2: signup (sub-aliased emails per persona)
For testing sign-up flows where no account exists yet. You supply one base email; each persona registers with a unique local+<hash>@domain sub-alias derived from it. All confirmation emails route to your single inbox, but each persona has its own isolated address.
{
"auth": {
"mode": "signup",
"signupEmail": "you@yourdomain.com",
"signupPassword": "optional-shared-password",
"startUrl": "http://localhost:3000/signup"
}
}
| Field | Required | Description |
|---|
signupEmail | Yes | Base email. Each persona-run gets a unique local+<base36(timestamp)+random>@domain alias derived from this. |
signupPassword | No | Shared password the agent uses on every persona’s sign-up form. If omitted, the runtime mints a strong random password per persona-run (not persisted). |
startUrl | No | URL where the sign-up form lives — agent navigates here first. |
How the alias works:
- The base email
you@yourdomain.com becomes you+lwk3a8x7@yourdomain.com, you+lwk3a902@yourdomain.com, etc. — one per persona.
- The timestamp half guarantees the alias can never be replicated in the future; the random half disambiguates personas spawned in the same millisecond.
- The agent is instructed to type that exact alias verbatim.
- The generated alias is persisted to the test run record (
cua_runs.generated_email) so confirmation emails can be correlated back later.
Use any provider that supports plus-addressing — Gmail, Fastmail, custom domains all work.
Use dev_test on http://localhost:3000 with goal “sign up for a free account”. Auth: signup mode, base email you@yourdomain.com.
Mode 3: cookie_injection (skip login entirely)
Inject session cookies and have the agent start already authenticated on a specific page.
{
"auth": {
"mode": "cookie_injection",
"cookies": [
{
"name": "session_token",
"value": "abc123...",
"domain": ".example.com",
"path": "/",
"secure": true,
"httpOnly": true
}
],
"startUrl": "http://localhost:3000/dashboard"
}
}
Each cookie needs name, value, domain at minimum; everything else is optional. The easiest way to capture cookies is the Cookie-Editor browser extension — Export → JSON.
Always set startUrl for cookie injection. Without it the agent lands on targetUrl (often the login page) and won’t realize it’s already authenticated. The MCP warns about this on stderr, but the agent runs cleaner if you set it explicitly to the post-login destination.Cookie domains captured from a different host (e.g. .production.com) are automatically rewritten to match the tunnel host. Cookie names, values, and other attributes are preserved unchanged.
Use test-only accounts. Credentials and cookies are sent to the Swarm API over HTTPS but should not be production credentials.
The dev loop
A typical end-to-end prompt:
Test my app at localhost:3000. The goal is “complete the checkout flow”. If there are issues, fix them and re-test until it passes.
The editor will:
- Call
dev_test with sensible defaults.
- Call
dev_watch and read the issues[] + recommendedActions[].
- Edit code based on those findings.
- Call
dev_test again to verify.
- Loop until
issues[] is empty.
For more copy-paste prompts covering smoke tests, auth flows, multi-step loops, and diagnostics, see Sample Prompts.
Troubleshooting
”No API key found” when starting
The MCP couldn’t find a key in ~/.useswarm/config.json or USESWARM_API_KEY. Run either of these from a terminal and restart your editor:
npx @useswarm/mcp setup # if you haven't registered yet
npx @useswarm/mcp login # if already registered
“API 401: Unauthorized” from a tool call
Your stored key was revoked or your account was logged out elsewhere. dev_status will show auth.valid: false. Fix:
“cloudflared not found”
Install Cloudflare’s tunnel binary — used to expose localhost to Swarm’s cloud agents:
# macOS
brew install cloudflared
# Linux
curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \
-o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared
The MCP falls back to ngrok if Cloudflare is unavailable.
Agents see only the frontend (API calls 404)
Your app has a separate backend on another port. Re-run with backendUrl:
Use dev_test on http://localhost:3000, backend http://localhost:8080, goal ”…”
Tools aren’t showing up in the editor
# verify what's registered
cat ~/.codex/config.toml | grep -A3 useswarm # Codex
claude mcp list # Claude Code
# clean re-register
npx @useswarm/mcp@latest setup
# then quit and re-open the editor
Tunnel providers
The MCP uses Cloudflare Quick Tunnels by default (free, no account needed). Falls back to ngrok if Cloudflare is unavailable. See the troubleshooting section above for install instructions.
Framework notes
| Framework | Notes |
|---|
| Next.js / Nuxt / Remix | Single port — no backendUrl needed |
| React + Express | Use backendUrl for the Express server |
| Django / Rails | Add the tunnel URL to ALLOWED_HOSTS / CORS config |
| Vite with proxy | If Vite proxies API calls, no backendUrl needed |