Skip to main content

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:
  1. Registers the MCP with Claude Code (via claude mcp add) and Codex CLI (writes ~/.codex/config.toml).
  2. Opens your browser to the device-code login page.
  3. 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.
CommandWhat it does
npx @useswarm/mcp setupRegister + sign in (one shot — what most users want)
npx @useswarm/mcp loginSign in only (you’ve already registered)
npx @useswarm/mcp logoutClear stored credentials
npx @useswarm/mcp whoamiCheck 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:
ParameterRequiredDescription
targetUrlYesFrontend URL (e.g. http://localhost:3000)
goalYesWhat agents should accomplish (e.g. “complete the signup flow”)
swarmIdConditionalUUID 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.
userDescriptionConditionalTarget audience (e.g. “first-time SaaS users”). Required when swarmId is not set. Drives persona generation.
agentCountNoNumber of AI personas to generate (1-20, default 3). Ignored when swarmId is set — the saved swarm’s count is used.
backendUrlNoSeparate backend URL (e.g. http://localhost:8080)
backendPathsNoExtra path prefixes for backend routing (e.g. ["/ws", "/v1"])
maxStepsNoCap on actions per agent
providerNoopenai or anthropic
modelNoModel override
authNoLogin 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:
ParameterRequiredDescription
batchIdYesBatch ID returned by dev_test
waitNoPoll 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”.
PathDestination
/api/*Backend
/auth/*Backend
/graphqlBackend
/trpc/*Backend
Everything elseFrontend

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
  • signupEmailsignup
  • 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"
  }
}
FieldRequiredDescription
signupEmailYesBase email. Each persona-run gets a unique local+<base36(timestamp)+random>@domain alias derived from this.
signupPasswordNoShared 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).
startUrlNoURL 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.
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:
  1. Call dev_test with sensible defaults.
  2. Call dev_watch and read the issues[] + recommendedActions[].
  3. Edit code based on those findings.
  4. Call dev_test again to verify.
  5. 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:
npx @useswarm/mcp login

“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

FrameworkNotes
Next.js / Nuxt / RemixSingle port — no backendUrl needed
React + ExpressUse backendUrl for the Express server
Django / RailsAdd the tunnel URL to ALLOWED_HOSTS / CORS config
Vite with proxyIf Vite proxies API calls, no backendUrl needed