> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firstflow.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool catalog

> Every tool the FirstFlow MCP server exposes what each one does, the scope it requires, and how a coding agent calls it over the OAuth-authenticated /mcp endpoint.

The MCP server turns FirstFlow into a set of tools a coding agent (Claude Code, Cursor, Claude Desktop) can call to read your workspace, build experiences, and edit their flows. This page is the canonical list of those tools, grouped exactly as the server groups them, with the [scope](/mcp/scopes) each one requires.

The catalog is **server-authoritative**. It lives in `mcp/policy/mcp-tool-catalog.ts`, the dashboard renders it verbatim, and on startup the server asserts every name here maps to a real registered tool so the list below cannot silently drift from reality. The [**Discovery**](#discovery-always-on) group is always on; every other group is gated by scope, and the gating happens before the tool ever appears to your agent.

## How tool gating works

Tool visibility is decided per request, server-side, and you never see a tool you can't use. When your agent calls `/mcp`, the controller authenticates the OAuth bearer token, resolves your scopes from your organization role, then builds a fresh `McpServer` for that one request. During build, `server.registerTool` is wrapped: a tool that your organization has disabled, or that your scopes don't cover, is **skipped at registration**. It is absent from `tools/list`, and a `tools/call` against it returns the SDK's native "method not found".

That is why a missing tool and a `403` mean the same thing the caller lacks the scope, not that the feature is gone. Read with the role you have; to gain a tool, get the [scope](/mcp/scopes) it needs.

```mermaid theme={null}
flowchart LR
  A["Agent calls /mcp<br/>(Bearer token)"] --> B{"Authenticate<br/>OAuth token"}
  B -- "no/invalid" --> E["401 + WWW-Authenticate<br/>(start OAuth)"]
  B -- "ok" --> C["Resolve scopes<br/>from org role"]
  C --> D["Build per-request McpServer<br/>register only enabled tools<br/>your scopes cover"]
  D --> F["tools/list<br/>(only your tools)"]
  D --> G["tools/call<br/>scope re-checked, then runs"]
  style A fill:#1f2937,color:#fff,stroke:#111
  style D fill:#0e7490,color:#fff,stroke:#0b5563
  style E fill:#b91c1c,color:#fff,stroke:#7f1d1d
  style F fill:#15803d,color:#fff,stroke:#14532d
  style G fill:#15803d,color:#fff,stroke:#14532d
```

Each tool body also re-asserts its scope at call time and **records a metering event**, so quota counts against the call, not the listing. Connecting and authenticating the endpoint is covered in [Connect an agent](/mcp/quickstart); the role-to-scope mapping is in [Scopes & roles](/mcp/scopes).

## Calling a tool

You rarely invoke tools by hand your agent reads the descriptions and picks them. The interaction is plain natural language once the server is connected:

```text theme={null}
Call list_agents and tell me which agents exist, then create a 3-step
onboarding tour on the first one that shows when chat opens.
```

The agent will call `list_agents` (no arguments), then `create_tour` with a structured payload. The tool arguments are exactly the JSON schema the server registers. A `create_tour` call, for example, looks like this on the wire:

```json theme={null}
{
  "name": "create_tour",
  "arguments": {
    "agentId": "agt_…",
    "name": "New-user onboarding",
    "description": "Three steps shown when chat first opens",
    "steps": [
      { "title": "Welcome", "body": "Let's get you set up in under a minute." },
      { "title": "Connect your data", "body": "Add a source so dashboards populate." },
      { "title": "You're ready", "body": "Explore on your own we're here if you need us.", "ctaLabel": "Got it" }
    ],
    "config": { "trigger": "chat_open", "frequency": "once" }
  }
}
```

Most write tools return the new entity's ID plus a dashboard link so the agent can suggest a review before you publish. Read tools return JSON text content. A failed call returns `isError: true` with a JSON `error` message rather than throwing for example `get_agent` on an unknown ID returns `{"error":"Agent not found"}`.

## Discovery (always on)

The Discovery group is `alwaysOn` it is registered for every caller so an agent can bootstrap context (list agents, read settings) before it has any other permission. Note that "always on" means the tool is always *registered*; the scopes below are still enforced at call time, so a member without `org:admin` calling `get_organization_settings` still gets a scope error.

| Tool                        | Required scope | What it does                       |
| --------------------------- | -------------- | ---------------------------------- |
| `list_integrations_catalog` | none           | Browse the integrations catalog.   |
| `get_organization_settings` | `org:admin`    | Read workspace settings.           |
| `list_agents`               | `agents:list`  | List all agents in your workspace. |
| `get_agent`                 | `agents:read`  | Get an agent's details and config. |

## Agents

Manage the [agent](/concepts/agents) workspace containers themselves read their public SDK config, create, rename, or delete them.

| Tool                   | Required scope  | What it does                                                               |
| ---------------------- | --------------- | -------------------------------------------------------------------------- |
| `get_agent_sdk_config` | `agents:read`   | Get the public SDK config for an agent (experiences, widget UI, settings). |
| `create_agent`         | `agents:create` | Create a new AI agent.                                                     |
| `update_agent`         | `agents:update` | Update an agent's name or description.                                     |
| `delete_agent`         | `agents:delete` | Permanently delete an agent.                                               |

## Build experiences

Compose a new [experience](/concepts/experiences) from a high-level intent. Each builder tool returns the new experience ID; `publish_experience` flips its status to active. Widget composition is handled server-side, so the agent describes the content and FirstFlow renders it.

| Tool                  | Required scope        | What it does                                            |
| --------------------- | --------------------- | ------------------------------------------------------- |
| `create_tour`         | `experiences:create`  | Build a multi-step onboarding tour.                     |
| `create_announcement` | `experiences:create`  | Create a single-card announcement.                      |
| `create_survey`       | `experiences:create`  | Build an NPS / scale / multi-choice / open-text survey. |
| `create_guide`        | `experiences:create`  | Create a dynamic AI-filled guide widget.                |
| `publish_experience`  | `experiences:publish` | Publish an experience (status → active).                |

A guide is the one widget whose content is filled by AI at runtime; the others are static steps the agent authors directly. See [Experiences](/features/experiences) for the distinction.

## Flow editing

Edit the [flow graph](/concepts/flows-and-nodes) behind an experience its steps, branching, and logic nodes. The step tools operate on linear experiences by index; the node tools operate on the underlying graph and require you to wire edges yourself with `connect_nodes`. All of these require `experiences:update`.

| Tool                  | What it does                                                                                         |
| --------------------- | ---------------------------------------------------------------------------------------------------- |
| `add_step`            | Add a step / question / card to an experience.                                                       |
| `update_step`         | Edit a step at a specific index.                                                                     |
| `delete_step`         | Remove a step from an experience.                                                                    |
| `reorder_steps`       | Move a step to a new position.                                                                       |
| `update_guide_widget` | Regenerate or refine a guide's widget frame.                                                         |
| `add_classifier`      | Add an LLM intent-branching (classify) node to a guide flow.                                         |
| `add_classify_node`   | Insert a bare classify (branching) node into any flow; wire branches with `connect_nodes`.           |
| `add_flow_node`       | Insert a logic node (`api_call` / `slack_message` / `webhook` / `assign_segment`) into a guide flow. |
| `remove_flow_node`    | Remove a node from a guide flow and re-stitch it.                                                    |
| `connect_nodes`       | Add or re-point an edge between two nodes in a guide flow.                                           |

<Note>
  The classify and logic nodes execute server-side at runtime branching decisions and flow navigation (`flow.next` / `flow.back`) never run in the browser. A classify node is how a guide chooses a branch based on user intent; a logic node is how it calls out to [Slack](/integrations/slack), a [webhook](/integrations/webhooks), or an [API](/integrations/api-calls), or assigns an audience segment.
</Note>

## Manage experiences

List, read, configure, and delete existing experiences, plus read their flow as a graph and trigger logic webhooks.

| Tool                               | Required scope       | What it does                                                                          |
| ---------------------------------- | -------------------- | ------------------------------------------------------------------------------------- |
| `list_experiences`                 | `experiences:list`   | List all experiences for an agent.                                                    |
| `get_experience`                   | `experiences:read`   | Get a single experience by ID.                                                        |
| `get_flow_graph`                   | `experiences:read`   | Read an experience flow as a node/edge graph.                                         |
| `update_experience`                | `experiences:update` | Update name, description, or status.                                                  |
| `configure_experience`             | `experiences:update` | Configure START-point settings: trigger, frequency, presentation, audience, priority. |
| `delete_experience`                | `experiences:delete` | Permanently delete an experience.                                                     |
| `trigger_experience_logic_webhook` | `experiences:update` | Enqueue a logic-webhook run for an experience.                                        |

`configure_experience` is where [eligibility](/concepts/triggers-and-audience) is set trigger, frequency, presentation, audience, and priority all live on the experience's START point and are evaluated server-side. The builder tools accept the same settings inline via an optional `config` block, so you can set them at creation time too.

## Audience

Read [audience](/concepts/audience) segments and membership for targeting.

| Tool                      | Required scope  | What it does                                     |
| ------------------------- | --------------- | ------------------------------------------------ |
| `list_segments`           | `audience:list` | List audience segments (id, name) for targeting. |
| `list_audience_members`   | `audience:list` | List audience members for an agent.              |
| `get_audience_membership` | `audience:read` | Get an end-user's membership and segments.       |

## Organization

| Tool                           | Required scope | What it does                       |
| ------------------------------ | -------------- | ---------------------------------- |
| `update_organization_settings` | `org:admin`    | Update workspace settings (admin). |

## Parameters

The two parameters you will pass most often are an `agentId` and an optional `config` block. The rest are tool-specific and your agent fills them from the registered JSON schema; the descriptions above are the source of truth your agent reads.

<ParamField path="agentId" type="string" required>
  The agent a tool acts on (`agt_…`). Most write and read-by-agent tools take it as the first argument. The recommended flow is to call `list_agents` first if exactly one agent exists, use its ID; if several exist, ask which one. Every agent-scoped tool re-verifies that this agent belongs to your organization before doing any work, so an ID outside your workspace returns an access error.
</ParamField>

<ParamField path="config" type="StartConfig">
  Optional START-point settings accepted by the builder tools (`create_tour`, `create_announcement`, `create_survey`, `create_guide`) and set directly by `configure_experience`. It controls eligibility `trigger`, `frequency`, `presentation`, `audience`, and `priority`. Omit it and the experience defaults to "when chat opens", shown once.

  ```json theme={null}
  {
    "config": {
      "trigger": "chat_open",
      "frequency": "once",
      "priority": 10
    }
  }
  ```
</ParamField>

<ParamField path="steps" type="Step[]">
  Ordered step list for `create_tour` (and similar shapes for surveys). Each step has a `title` and `body`, with optional `ctaLabel`, `ctaUrl`, and widget-chrome overrides (`headerTitle`, `headerIcon`, `skipLabel`). At least one step is required.
</ParamField>

## Notes

* **Authentication is OAuth-only.** The `/mcp` endpoint accepts OAuth-issued tokens; static keys from **Settings → API Keys** are explicitly rejected there and a compliant client is told to start the OAuth flow via the `WWW-Authenticate` header. Those static keys still work on the REST API. See [API keys & OAuth](/mcp/api-keys).
* **Scopes come from your organization role.** Admins (`org:admin`) get the full catalog; members get read and list tools only (`agents:list`, `agents:read`, `experiences:list`, `experiences:read`, `audience:list`, `audience:read`). Legacy coarse scopes on older keys (`experiences:write`, `agents:write`, `analytics:read`) are expanded to their granular equivalents. Full mapping in [Scopes & roles](/mcp/scopes).
* **Errors are returned, not thrown.** A tool that can't complete returns `isError: true` with a JSON `error` payload (e.g. `{"error":"Agent not found"}`). A scope failure surfaces as a missing tool or a `403`.
* **Every call is metered.** The `/mcp` endpoint is rate-limited and metered per organization; in the self-hosted `community` edition, quotas are unlimited.
* **The list is exact.** This catalog mirrors `mcp/policy/mcp-tool-catalog.ts` one-to-one. If a tool name here doesn't appear to your agent, it's gated by scope or disabled for your organization not missing.

## Next

Resolve a missing tool in [Scopes & roles](/mcp/scopes), connect a client in [Connect an agent](/mcp/quickstart), or understand the entities these tools manipulate in [Experiences](/concepts/experiences) and [Flows & nodes](/concepts/flows-and-nodes).
