CLI and MCP server setup

The FlurryPORT CLI (npm package flurryport) does three jobs: it forwards live captures from an endpoint to your local machine, it manages accounts and targets from the terminal, and it runs FlurryPORT as an MCP server so an AI agent in your editor can capture, inspect and replay webhooks. It is the only way to forward live traffic to a localhost dev server, because FlurryPORT runs in the cloud and cannot reach your machine on its own. Install it with npx and you can be forwarding in about a minute.

This page is the reference for all three jobs. If you set the tool up before September 2026, the MCP server section and the environment variable table are new and worth a fresh read.

Install

npx flurryport@latest --help

npx runs it without installing anything. Install it globally if you use it daily:

npm install -g flurryport

The CLI is built and tested on Node 22. Source is on GitHub and releases are on npm.

Sign in

Run login with no token and the CLI prints a link. Open it, approve the login in the browser, and the CLI stores the token it is handed. Nothing sensitive is typed into the terminal or copied through the clipboard, and the link expires after about ten minutes.

flurryport login --use

--use makes the new account the active one in a single step. --name <name> stores it under a name other than default, which is how you keep several accounts on one machine.

Pasting a personal access token still works and is the right choice for CI, where no browser exists. Mint one in the app under Settings, then:

flurryport login fp_your_token_here              # stored as "default"
flurryport login --name alice fp_your_token_here # stored as "alice"

First run

From a fresh install to a request arriving on your machine, with no backend of your own:

flurryport login --use                    # approve in the browser
flurryport target create http://localhost:3000/webhooks
flurryport echo 3000                      # terminal 1: a receiver that prints what arrives
flurryport listen                         # terminal 2: forward captures to it
# now send a request to your capture URL, or replay one from the dashboard

target create picks the only project and endpoint when there is one and prompts when there are several. Once your own app is listening, point the target at it instead of echo.

Commands

Thirteen commands. Every command that talks to the API accepts --account <name> to use a stored account other than the active one for that run.

CommandWhat it doesKey flags
login [token]Sign in by browser approval, or with a personal access token for CI.--name, --use
accountList, switch and remove stored accounts.list, use <name>, remove <name>
config showPrint the active environment, account and API URL.
target create [url]Create a replay target without leaving the terminal.--project, --endpoint, --name
listenForward live captures to a replay target on your machine.--interval
echo [port]Run a local server that returns 200 and mirrors what it received.--host, --path
post [body]Post a signed intent to an endpoint from a script or a pipe.--project, --endpoint, --file, --content-type
keysList or remove the signing keys stored on this machine. Values are never printed.list, remove <ref>
mcpRun FlurryPORT as an MCP server for an AI editor or a hosted agent.eight flags, see below
join <token>Accept an invite to an endpoint and store its contributor key.--name, --interval, --window, --url
seat <guest-name>Mint a pairing code that seats another agent in a room.--project, --endpoint, --hours, --code-minutes, --recipe
seat-serverRun the seat connector a room guest connects to.--host, --port, --allowed-hosts, --api-url, --idle-minutes
consoleOpen the interactive room console with its colon commands.--json

listen

Discover the replay targets that point at your machine, attach to one, and forward executions until you press Ctrl+C.

flurryport listen
flurryport listen --interval 5000          # poll interval in ms (default 3000)
flurryport listen --account alice          # use a stored account for this run

When the CLI starts, any executions that were already pending are marked stale and dead-lettered, so a restart never replays a backlog you did not ask for. Sequence runs started from the dashboard are grouped under a header in the output.

target create

# Interactive
flurryport target create

# Fully scripted
flurryport target create http://localhost:3000/webhooks \
  --project my-project \
  --endpoint stripe-webhook \
  --name "My laptop"

Targets created from the CLI always start with auto-forward off. Turn it on in the dashboard when you want every new capture delivered without asking. The plan limits on targets are on the plans and limits page.

post

Post an intent to an endpoint from the terminal, signed with the key stored on this machine (the owner key from the dashboard, or the contributor key join stored). The key never appears in output or arguments. The payload comes from --file first, then the argument, then piped stdin.

flurryport post --project <id> --endpoint <id> --file event.json
echo '{"type":"order.created","id":42}' | flurryport post --project <id> --endpoint <id>

echo

Start a local HTTP server that returns 200 to every request, logs it, and mirrors it straight back. It is the fastest way to confirm forwarding is wired correctly with no backend of your own.

flurryport echo mirroring a request back as X-Echo headers plus a JSON envelope
flurryport echo [port] [--host <host>] [--path <path>]
ArgumentDefaultDescription
[port]3000Port to listen on.
--host <host>localhostInterface to bind.
--path <path>all pathsIf set, only that exact path returns 200 and everything else returns 404. A leading slash is added for you and query strings are ignored when matching.

Every incoming request header is echoed back prefixed with X-Echo-, and the JSON body carries the method, path, query, headers, body and a request counter for the run.

Worked example: secret substitution and redaction

# 1. Add a secret in the dashboard (Secrets > Add secret): STRIPE_KEY
# 2. On a replay target, set a header:  X-Api-Key: $secrets.STRIPE_KEY
# 3. Start a local receiver:
flurryport echo 3000
# 4. In another terminal, forward captures to it:
flurryport listen
# 5. Send a test webhook to your FlurryPORT endpoint.

FlurryPORT substitutes the real key into X-Api-Key on the way out. echo mirrors it back as X-Echo-X-Api-Key. Because the destination echoed a secret, FlurryPORT scrubs it before storing, so the recorded response shows [REDACTED:STRIPE_KEY]. The real secret reached the destination and the stored response never contains it. See secrets for the full workflow.

keys

keys list shows the signing keys stored on this machine by reference, and keys remove <ref> deletes one. Key values are never printed by any command.

join, seat, seat-server and console

These four belong to rooms. join <token> accepts an invite token (it starts with fpi_) and stores the contributor key for that endpoint. seat <guest-name> mints a pairing code that seats another agent in a room, valid for ten minutes by default, for a seat that lasts 24 hours by default. seat-server runs the connector a seated agent talks to, on 127.0.0.1:8791 by default, closing after 30 idle minutes. console opens the interactive room console, a short colon-command language for reading and posting to a room; --json switches its output to machine-readable form.

Environment variables

Seventeen variables. Most exist so the CLI can be pointed at a different instance; the first two are the ones a CI job sets.

VariableWhat it setsDefault
FLURRYPORT_TOKENA personal access token for the MCP server to boot with. Must start with fp_.
FLURRYPORT_ACCOUNTStored account name for the MCP server to boot as.active account
FLURRYPORT_API_URLAPI base URL. Beats --api-url.https://api.flurryport.io
FLURRYPORT_ANON_URLAnonymous capture base URL.https://flurryport.dev
FLURRYPORT_REFAttribution source for anonymous session start. --ref beats it.
FLURRYPORT_WEB_URLWeb base URL used in login and claim links.https://flurryport.io
FLURRYPORT_VIEWER_URLCapture viewer base URL.the anonymous base URL
FLURRYPORT_BILLING_URLBilling API base URL, used by get_upgrade_options.the API URL
FLURRYPORT_CATALOG_URLRecipe catalog API base URL.https://api.flurryport.io
FLURRYPORT_ROOMS_URLFull URL of the rooms MCP endpoint.{API URL}/rooms/mcp
FLURRYPORT_ROOMS_IDLE_MINUTESMinutes of silence before the seat server closes an idle session. Beats --idle-minutes.30
FLURRYPORT_ROOMS_STANDING_IDLE_MINUTESIdle bound for standing sessions.none
FLURRYPORT_MCP_ALLOWED_HOSTSExtra Host header values accepted in HTTP mode, comma-separated. Adds to --allowed-hosts.
FLURRYPORT_MCP_ALLOWED_ORIGINSOrigin header values accepted in HTTP mode, comma-separated.
FLURRYPORT_PUBLIC_API_HOSTMarks the public API host for hosted rooms.
FLURRYPORT_ACCEPT_GRACE_MSGrace window for an invite accept race, in milliseconds.30000
FLURRYPORT_LANDING_CHECK_MSPoll interval while waiting for an invite to land, in milliseconds.30000
⚠️ Three precedence traps. FLURRYPORT_API_URL beats --api-url, and FLURRYPORT_ROOMS_IDLE_MINUTES beats --idle-minutes, but --ref beats FLURRYPORT_REF. In two of the three the environment wins over the flag you just typed. If a command appears to ignore an argument, check the environment first.

MCP server

npx flurryport mcp

That command is the whole install for most clients. It starts FlurryPORT as an MCP server over stdio, and an agent can ask for a capture URL in one tool call with no account.

Install per client

The block is identical for three of these clients and differs for two. Restart the editor after any of these edits; most clients read MCP configuration only at startup.

ClientConfig fileShape
Claude Codenoneclaude mcp add flurryport -- npx -y flurryport mcp
Claude Desktopclaude_desktop_config.jsonstandard mcpServers block
Cursor~/.cursor/mcp.jsonidentical block, different path
Windsurf~/.codeium/windsurf/mcp_config.jsonidentical block, different path
VS Code.vscode/mcp.jsontop-level key is servers, and needs "type": "stdio"
ChatGPTnonenot supported on this path, see below
{
  "mcpServers": {
    "flurryport": {
      "command": "npx",
      "args": ["-y", "flurryport", "mcp"]
    }
  }
}

VS Code differs twice, so it gets its own block:

{
  "servers": {
    "flurryport": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "flurryport", "mcp"]
    }
  }
}

ChatGPT cannot connect to a server that runs locally over stdio, so no configuration makes the block above work there. A hosted client like that needs the HTTP mode below behind a public hostname, or a hosted MCP endpoint.

Flags

FlagDefaultWhat it is for
--allow-lanoffLet forward_to_localhost deliver to private LAN addresses as well as loopback.
--ref <source>FLURRYPORT_REFAttribution recorded when an anonymous session starts. Registry listings set this. It does not bind or accept an invite.
--account <name>FLURRYPORT_ACCOUNTBoot as a specific stored account without changing the active one.
--anon-url <url>https://flurryport.devOverride the anonymous capture base URL.
--httpoffServe streamable HTTP at /mcp instead of stdio, for remote MCP clients and hosted agents. Each MCP session gets its own server state.
--port <port>8790HTTP port, with --http.
--host <host>127.0.0.1HTTP bind address, with --http. Loopback by default so nothing is exposed without an explicit choice.
--allowed-hosts <hosts>Comma-separated public hostnames accepted in the Host header, with --http: the tunnel or reverse proxy in front of this server.

HTTP mode and the transport guard

--http serves the same server over streamable HTTP at /mcp for remote MCP clients and hosted agents. It binds to 127.0.0.1 on port 8790 unless you say otherwise, so nothing is reachable from outside the machine without an explicit choice. To expose it, put a tunnel or reverse proxy in front and pass its hostname with --allowed-hosts.

Every request is checked against that list. The Host header must be a loopback name, the bind host, or a hostname you allowed; and if the request carries an Origin header at all, that origin must be on the allowed-origins list (FLURRYPORT_MCP_ALLOWED_ORIGINS). A request that fails either check is refused with a 403 whose body names the flag that would allow it. This is what stops a page in your browser, or a stray process on the same network, from driving the server through your credentials.

Which tools you get

The server registers one toolset of sixty tools at startup, and the list is the same whether or not you are signed in. Twenty of them work before a session is claimed: the capture loop (get_capture_url, list_captures, get_capture, get_capture_digest, forward_to_localhost), test traffic and pacing (send_test_event, capture_count, start_echo_server), standing watches, the recipe catalog (search_recipes, get_recipe, lint_recipe, verify_chain), invites, upgrade options, secret setup and get_server_info. The other forty answer a structured account_required error until you claim the session or sign in, at which point the same tools simply start succeeding. Nothing is added or removed, so a client that cached the tool list at startup is never holding a stale shape.

flurryport seat-server is a separate process with its own thirteen tools for a seated room guest: read, post, wait for posts, the roster, canon and sections, and the pairing and standing-credential ceremony.

Try it without an account

With no token configured the server starts in anonymous mode. Ask your agent for a capture URL, point a provider at it, and debug the payload in the editor. Anonymous sessions are honest about their limits: 250 captures of up to 32 KB each, 90 minutes of idle life, 24 hours maximum, and plaintext storage. Do not send real production data to one; claim the session, which is free, for encryption at rest. The session survives editor restarts.

Forwarding is loopback-only by default (--allow-lan opts in) and delivers the raw bytes with the original headers, so provider signatures verify on your app exactly as they would in production.

To claim, open the link the server hands your agent and sign up or sign in. Your captures move into a real project, re-encrypted, and your watches wake up on it. The CLI completes a device-code hand-off: a read-only token is minted for you and the toolset flips to authenticated live, with no restart and nothing to paste. Already have an account? Sign in with the login command above and restart the MCP server; token scope is read at startup, so the restart matters.

Permissions and tokens

This answers the question a careful reader has before installing anything: what can this thing do to my systems, and what does it do with my keys.

Replay is the only destructive operation. replay_to_target is the only tool on the server annotated as destructive, so a client that asks before running destructive tools will ask before anything is sent. Server-side replay works only against replay targets you registered in the dashboard; the agent can never supply an arbitrary URL.

Token scopes fail closed. A personal access token has two settings at mint time, both on by default, because the most common reason to mint one is handing it to an AI.

ScopeDefaultWhat it enforces
Read-onlyOnInspect and forward to your own machine only. No server-side replays, no changes. Enforced by the server, not a client hint.
Redact PIIOnCapture reads come back with emails, cards, SSNs, phones and IPs masked, best effort. Forwards to your own local target stay untouched so signatures keep verifying.

Service credentials stay server side. When a recipe acts on Slack, GitHub or Azure DevOps, the credential lives on our side. The agent sends a typed intent and receives the receipt: what was sent, what the service answered, when. It does not receive the key, and the key does not enter the model’s context.

A personal access token is accepted by the Core API only, as Authorization: Bearer fp_.... The Users and Billing APIs refuse it entirely, so a token can never change account settings, billing or terms. Within Core it can:

Read projects, endpoints, captures, replay targets, executions, sequences and collections
Create and update replay targets, create endpoints, and manage collections
Trigger replays, batch replays, sequence replays and collection runs (read-write tokens only)
Record CLI replay results back to FlurryPORT
Delete replay targets, change or delete endpoints, edit captures, or touch plans, billing or domain verifications

So a teammate holding your token can forward your captures to their machine and create their own replay targets, but cannot delete your projects, change your billing or extend their own access. Lost or leaked tokens are revoked individually from Settings.

Use it in CI

No browser exists in CI, so this is the one place a pasted token is the right answer. Mint a token, store it as a secret, and pass it in the environment. This example posts a fixture event to an endpoint on every run, so the pipes bound to that endpoint fire and leave a receipt:

- name: Post the release event to FlurryPORT
  env:
    FLURRYPORT_TOKEN: ${{ secrets.FLURRYPORT_TOKEN }}
  run: npx -y flurryport post --project ${{ vars.FLURRYPORT_PROJECT }} --endpoint ${{ vars.FLURRYPORT_ENDPOINT }} --file fixtures/release.json

Configuration file

Config lives at ~/.flurryport/config.json and is rewritten on every command. The directory is created with mode 0700 and the file with 0600 on macOS and Linux; Windows gets best-effort ACLs. Direct editing is supported.

{
  "activeEnvironment": "prod",
  "environments": {
    "prod": {
      "apiUrl": "https://api.flurryport.io",
      "activeAccount": "me",
      "accounts": {
        "me":    { "apiKey": "fp_..." },
        "alice": { "apiKey": "fp_..." }
      }
    }
  }
}

How listen works

1
The CLI fetches your projects, endpoints and replay targets, and keeps the ones whose URL points at your machine.
2
You pick which target to attach to; it is chosen for you when there is only one.
3
Executions that were pending before this session started are claimed and dead-lettered, so a restart never replays a stale backlog.
4
The CLI polls the pending-executions endpoint every few seconds for the chosen target.
5
For each pending execution it fetches the original captured request, decrypted server side, and POSTs the bytes to your URL with the original method, headers and body.
6
The response (status, headers, body preview, duration) is recorded back to FlurryPORT, so the execution shows up complete in the dashboard.

Limitations and notes

⚠️ The server-side replay processor deliberately skips targets on your machine; only the CLI can deliver to them. A replay to a local target while the CLI is not running sits pending for one minute and is then dead-lettered.
⚠️ One session drains a target at a time. A second listener on the same target gets "Another session is attached" and can attach about 30 seconds after the first one stops.
⚠️ Stop-on-failure in a sequence is applied by the server, which marks the remaining items skipped on the first non-2xx response. The CLI simply never sees them on its next poll; the effect is the same.

Version and uninstall

flurryport --version

Released versions and their notes are on GitHub. When a newer version is available the CLI says so once per session.

npm uninstall -g flurryport

npx leaves nothing installed, so if you only ever used npx there is nothing to remove. Either way, the tokens and signing keys the CLI stored live under ~/.flurryport/; delete that directory to remove them, and revoke the tokens from Settings if the machine is leaving your hands.

When something does not work

Open an issue on GitHub with the command, the version and what the provider’s own delivery log says. Those three things resolve most reports in one round trip, and the third is the one people leave out.