Skip to content

MCP Server ​

Experimental

tapflow's AI Automation axis — the MCP server and the flow runner — is experimental. The manual QA dashboard is the mature, production path; this axis is additive and still maturing. Expect rough edges, especially in selector matching and in timing right after an app launches.

@tapflowio/mcp-server exposes tapflow as a Model Context Protocol (MCP) server. Claude Code, Codex, and any other MCP-compatible LLM agent can control iOS simulators and Android emulators as native tools — no scripting, no hardcoded selectors.

These three guides fit together: connect a coding agent here, learn the flow YAML format in the Flow Reference, then combine them in MCP in CI/CD, where an agent authors a flow once and CI replays it deterministically.

When to use this ​

Repeatable automated testing is where this shines. One-off manual checks are still faster done by hand.

  • CI/CD regression tests — After each build, an agent boots a simulator, installs the build, walks through key flows, captures screenshots, and reports regressions. No human intervention needed. → MCP in CI/CD
  • Multi-device matrix — Run the same flow on iPhone SE (iOS 16), iPhone 15 Pro (iOS 17), and an Android emulator in sequence without manually switching devices.
  • Natural language QA scripts — Non-developers (QA, PM) describe test scenarios in plain text; the agent executes them. No coordinate mapping or brittle selectors.

How it connects ​

text
LLM Agent (Claude Code, etc.)
    ↓  MCP protocol (stdio)
@tapflowio/mcp-server
    ↓  WebSocket + REST
tapflow relay
    ↓  WebSocket
Mac Agent (iOS · Android)

The MCP server is a local process that bridges the LLM agent to your self-hosted relay. App data never leaves your network.

Prerequisites ​

  • A running tapflow relay.
  • A personal access token (PAT) created in the dashboard. Go to Settings → Tokens → New token and choose the API Type. The Tokens page is shown to Admins, Developers and QA.

Installation ​

sh
npm install -g @tapflowio/mcp-server

Setup ​

Claude Code ​

Register tapflow with the claude mcp add command:

sh
claude mcp add tapflow --scope project \
  --env TAPFLOW_RELAY_URL=ws://localhost:4000 \
  --env TAPFLOW_TOKEN=tflw_pat_your_token_here \
  -- tapflow-mcp

--scope project saves the config to .mcp.json so the whole team shares it. Use --scope local (the default) if you only want it for yourself.

If the relay is on a remote server, change the URL:

sh
claude mcp add tapflow --scope project \
  --env TAPFLOW_RELAY_URL=wss://your-relay.example.com \
  --env TAPFLOW_TOKEN=tflw_pat_your_token_here \
  -- tapflow-mcp

Other MCP clients (Cursor, VS Code, Codex) ​

Any MCP-compatible client can use tapflow. Add the following to your MCP config JSON:

json
{
  "mcpServers": {
    "tapflow": {
      "command": "tapflow-mcp",
      "env": {
        "TAPFLOW_RELAY_URL": "ws://localhost:4000",
        "TAPFLOW_TOKEN": "tflw_pat_your_token_here"
      }
    }
  }
}

Environment variables ​

VariableDescriptionDefault
TAPFLOW_RELAY_URLRelay WebSocket URLws://localhost:4000
TAPFLOW_TOKENAn API-type PAT (view, builds:write), created in Settings → Tokens. Leave it out to read the token file(none)
TAPFLOW_TOKEN_FILEPath of the token file~/.config/tapflow/credentials.json

Keep the token in a file ​

Without TAPFLOW_TOKEN, the MCP server reads its token from a file. tapflow token set is the simplest way to store one, and it checks the token with the relay first; on a computer without the tapflow CLI, write the file by hand in the shape below. When the relay refuses the token, the server reads the file again and retries once, so replacing a token needs no re-registration and no restart.

  • The file is ~/.config/tapflow/credentials.json, or tapflow/credentials.json under XDG_CONFIG_HOME when that is set, or the path in TAPFLOW_TOKEN_FILE. It is in the home directory of the computer that runs the MCP server, not of the Mac the relay runs on.
  • Each relay has its own token, keyed by the relay address's origin; write ws:// as http:// and wss:// as https://.
  • The file holds a token, so keep it at mode 600, readable only by you. The MCP server warns when other users can read it (not checked on Windows).
json
{
  "version": 1,
  "relays": {
    "http://localhost:4000": { "token": "tflw_pat_your_token_here" }
  }
}

TAPFLOW_TOKEN, when set, wins over the file and is never replaced by it. To move a registered TAPFLOW_TOKEN into the file, remove it from the MCP registration.

When a token is revoked, the relay closes the connection right away. Write the new token to the file and the next tool call reconnects with it. The relay releases the sessions the old connection held, so call connect_device again.

Connected straight to the relay's port on the Mac it runs on, the MCP server starts and drives devices without a token. screenshot, query_ui_tree, list_builds, list_runs and get_run still need one. When the relay refuses a token, the error says which relay refused it, where the token came from, and how to fix it.

Available tools ​

ToolDescription
list_buildsList the apps and builds on the relay (where the buildId for install_app and launch_app comes from)
list_devicesList connected simulators and emulators
connect_deviceJoin a session (required before controlling a device). Also returns watchUrl, a dashboard link for watching the device
disconnect_deviceEnd a session
boot_deviceBoot a simulator or emulator
shutdown_devicePower the device down (frees resources; forces a cold boot next time)
screenshotCapture the current screen (PNG, or JPEG on iOS when requested)
query_ui_treeRead the on-screen UI as a structured accessibility tree (role, label, identifier, frame)
tapTap at a coordinate
swipeSwipe between two coordinates
type_textType text into the focused field
press_keyPress a keyboard key
press_buttonPress a hardware button (home, lock, etc.)
install_appInstall an app
launch_appLaunch an installed app
run_flowReplay a YAML flow deterministically (no further LLM calls)
list_runsList the run records tapflow flow run left on the relay (filter by status, build, flow name, CI or local)
get_runRead one run record: each failed flow's steps (a passed flow's step count), failure message and file path, and screenshots of up to four failed flows

Typical workflow ​

An LLM agent typically calls tools in this order:

text
list_devices       → get available devices and sessionIds
connect_device     → join a session, hand the person the watch link
boot_device        → wait for the device to be ready (skip if already booted)
install_app        → install the build
launch_app         → launch the app
screenshot         → capture screen → LLM analyzes
tap / swipe / ...  → interact
screenshot         → verify result → repeat
disconnect_device  → end the session

Device already booted

If list_devices returns "status": "booted" for a device, you can skip boot_device.

For running this in a CI pipeline, see MCP in CI/CD.

Watch the device a coding agent drives ​

connect_device returns watchUrl in its result. Open that link in a browser to watch, live, the device the coding agent is driving. The tool description tells the coding agent to hand the link to the person who asked, so when you ask for something like "test the login screen", the coding agent gives you the link as it starts testing.

The page is reached only through the link. It is for the person who asked the coding agent to test, to watch their own work, so the dashboard has no list of these sessions. A teammate who opens the QA session's device list sees the device as in use, with Coding agent is driving it as the reason.

  • Watching is read-only. Nothing you click reaches the device, and there is no sound.
  • Any teammate signed in to the dashboard can watch. Whatever the coding agent types on the device shows on screen too, so do not test with values the team should not see.
  • Up to 4 people can watch one session at a time.
  • Agent activity beside the device shows what the coding agent does, in the order it happens, and a blue cursor stays on the device where it last tapped, gliding to the next spot with a brief trail. When a flow runs through run_flow or tapflow flow run, each step is a row with the actions taken in it underneath; steps start open and can be collapsed. Typing shows as a count of characters, and the URL opened or the character key pressed is not named. Nothing is stored, so the log starts when you open the page. When the session ends, the last picture and the log stay until you close the page.
  • If the coding agent's connection drops briefly, the page keeps the last picture and waits for it to come back. The watch ends when the coding agent finishes with the session (disconnect_device) or another teammate starts using the device.
How the link address is chosen

If the relay has an address teammates can open (a tunnel or relay.url), the link uses it, and you can share it with teammates as it is. Otherwise, including when relay.url is a localhost address, the MCP server builds the link from the TAPFLOW_RELAY_URL it connected with. For example, connected with ws://localhost:4000, the link starts with http://localhost:4000/automation/sessions/ and opens on the computer running the MCP server. For an address to send to teammates, see External access.

Fix a failed run ​

When tapflow flow run fails in CI, a coding agent can read the run and repair the flow. Ask it something like "fix last night's failed CI run", and it uses the tools in this order:

text
list_runs   → find the failed run (status: failed)
get_run     → which step failed and why, the screen at that moment, and the flow file's path
(edit)      → change the YAML at the recorded path
run_flow    → replay the fixed flow on a device to check it
  • A recorded flow file path is as the runner was given it, relative to the directory it ran in (the CI job's working directory, for a CI run). The coding agent finds that file in its own checkout.
  • Run records are only read. The coding agent replays the flow itself with run_flow; nothing re-runs the CI job.
  • get_run returns up to 4 screenshots of failed flows with the record. A passed flow's last screen and the videos are not included; open the run's page at the result's url to see them.
  • A failure whose failureKind is environment was the device, the agent or the relay, not the app or the flow. There is nothing to fix in the flow.

The result's url is built from the TAPFLOW_RELAY_URL the MCP server connected with. Unlike the watch link, it does not use the relay's public address, so a server that connected by a name inside a container network (such as ws://relay:4000) gets a link a browser cannot open.

For when runs are recorded and what the dashboard shows, see Flow Runs.

Released under the MIT License.