Troupeby AliveDocsSign in

Integrate

MCP

Troupe serves its device operations as Model Context Protocol tools, so a coding agent can boot a simulator, install your app, read the screen and tap. This page covers connecting an agent to your local runtime, the two toolsets, and how an agent works with a device.

For the hosted service, see MCP in the cloud.

Connect a coding agent

In a project that has @alivelabs/troupe installed, write the client's project config:

Shell
bunx troupe mcp install --client claude-code --write
--clientFile it writes
claude-code.mcp.json
cursor.cursor/mcp.json
vscode.vscode/mcp.json
codex.codex/config.toml

Without --write the command prints the entry and changes nothing. The entry starts troupe mcp, a bridge between the client's stdio and the local runtime daemon, which it starts when none runs:

JSON
{
  "mcpServers": {
    "troupe": {
      "type": "stdio",
      "command": "/path/to/bun",
      "args": ["/path/to/project/node_modules/@alivelabs/troupe/bin/troupe.ts", "mcp"]
    }
  }
}

VS Code's file uses the key servers instead of mcpServers. Codex's file is TOML:

TOML
[mcp_servers.troupe]
command = "/path/to/bun"
args = ["/path/to/project/node_modules/@alivelabs/troupe/bin/troupe.ts", "mcp"]

Four things to know about the entry:

  • It holds no credential. The bridge reads the daemon's current token from runtime.json each time it starts.
  • It is your machine's own. The paths are absolute, because a client launched from the Dock does not have your shell's PATH. Do not commit it: each developer runs the command in their own checkout. For Claude Code, claude mcp add-json troupe '<entry>' keeps it in your local scope instead of .mcp.json.
  • It runs the project's Troupe. The entry names the troupe in the project's node_modules, so the agent uses the version the project pins.
  • --write merges. It adds the entry beside the servers already in the file. The same entry again changes nothing. A different entry of the same name, or a file it cannot merge safely, stops the command with exit code 2.

Codex reads a project's .codex/config.toml only in a project it trusts, and says nothing otherwise. Claude Code and Cursor ask before they start a project's server.

The connection has been tried with Claude Code, Cursor's CLI, Codex, VS Code and the MCP Inspector.

The endpoint

The daemon serves MCP over Streamable HTTP at /mcp on its loopback port. It is stateless: every request stands alone. troupe mcp forwards each JSON-RPC message to that endpoint as the runtime's owner and writes the answers to stdout. Nothing else is written to stdout.

Toolsets

A client gets one of two sets of tools.

ToolsetToolsHow to choose it
curated31, for working with a deviceThe default
all61: every procedure that can be a tooltroupe mcp --toolset all, or --toolset all on troupe mcp install

The curated set:

GroupTools
Sessionssessions_create, sessions_get, sessions_list, sessions_wait, sessions_heartbeat, sessions_release
Catalogcatalog_deviceTypes, catalog_osVersions
Appsapps_install, apps_launch, apps_terminate, apps_list, apps_clearData, apps_openUrl
Screenui_snapshot, ui_query, ui_waitFor, media_screenshot
Inputinput_tap, input_doubleTap, input_longPress, input_swipe, input_type, input_fill, input_pressKey, input_pressButton, input_scroll, input_setOrientation, input_hideKeyboard
Logslogs_read, logs_crashes

all adds the runtime's status and environment checks, uninstalling apps and setting permissions, drag, pinch and raw gestures, device settings such as location and appearance, and reads of the event streams. Three procedures are never tools, because they mint or return credentials: pairings.create, pairings.redeem and streams.ticket.

On the HTTP endpoint the choice is the toolset query parameter: /mcp?toolset=all.

How tools behave

  • Names and inputs. A tool's name is its procedure's id with _ for the dot: input.tap is input_tap. Its input is the procedure's input, so the reference describes every tool.
  • Results. A tool returns structured content and the same as JSON text. ui_snapshot returns its text as an aria snapshot. media_screenshot returns an image.
  • Errors. A failure is a tool result with isError: true. Its text is the error as JSON, with the same code, message, data and retryable as on every other surface.
  • Timeouts. timeoutMs is capped at 50 seconds. A longer wait is a repeated call: sessions_wait answers done: false, and the agent calls it again.
  • Sessions. A session created through MCP is held as mcp. It ends after 10 minutes without a call that names it, and after 60 minutes at the latest. sessions_heartbeat keeps it alive.
  • Annotations. Each tool says whether it is read-only, idempotent or destructive.

How an agent works with a device

A typical task runs like this.

  1. catalog_deviceTypes and catalog_osVersions say what can be started.

  2. sessions_create starts a device, with the app to install:

    JSON
    {
      "spec": { "platform": "ios-simulator", "deviceType": "iPhone 18 Pro" },
      "install": [{ "path": "/Users/me/shop/build/Shop.app" }]
    }
  3. sessions_wait answers when the session is ready. apps_launch starts the app by its bundleId.

  4. ui_snapshot reads the screen. Each element has a ref:

    Output
    - application "Shop" [ref=e1]:
      - toolbar "Cart" [ref=e2]:
        - button "Back" [ref=e3]
        - heading "Cart" [level=1] [ref=e4]
      - textbox "Email" [testId="email"] [ref=e5]: "a@b.co"
      - switch "Remember me" [checked] [ref=e6]
  5. The agent acts on a ref, or on a locator:

    JSON
    { "sessionId": "ses_…", "target": { "ref": "e5" }, "value": "ada@example.com" }

    That is an input_fill. The runtime waits until the element can take the action, 5 seconds by default.

  6. media_screenshot shows the result. logs_read and logs_crashes explain a failure.

  7. sessions_release gives the device back.

A ref is valid until the next ui_snapshot of that session. A ref the runtime can no longer find fails with LOCATOR_REF_STALE: take a new snapshot. A target may also be a locator, which survives snapshots:

JSON
{
  "target": {
    "locator": {
      "v": 1,
      "steps": [{ "by": "role", "role": "button", "name": { "text": "Sign in" } }]
    }
  }
}

You can watch an agent work: its session appears in Troupe Studio with the live screen.

Next

esc

    Type to search every page, the protocol reference and the cloud API.