# Connect an AI agent to the Workflow Builder tools

The Workflow Builder API is also served as a Model Context Protocol endpoint, so an assistant that speaks MCP can build, test, deploy and read a Data Workflow through tools instead of raw HTTP. The tools call the same operations as the [REST routes](https://docs.returning.ai/workflow-builder.md#the-build-journey), under the same one permission.

## Endpoint and credential

- URL: `https://api.returning.ai/v1/workflow-builder/data-workflows/mcp`
- Transport: Streamable HTTP, stateless. There is no session id and nothing to resume.
- Credential: a community API key holding `workflowBuilder`, sent as `Authorization: Bearer <API_KEY>`.
- Reference: [Call the workflow tools over MCP](https://docs.returning.ai/api-reference/workflow-builder/call-the-workflow-tools-over-mcp.md), with the JSON-RPC request and response shapes and the transport's own errors.

Create the key in **Settings > Integrations > API Keys**: add a key, open **Permissions**, and tick **Workflow Builder**. That one checkbox covers every tool. The key belongs to one community, so every tool it calls works on that community's workflows and nothing else.

> MCP Tokens vs API keys
>
> These are two different features and they do not overlap.
>
> - **Settings > Integrations > MCP Tokens** issues tokens for the API documentation tools: "Generate API tokens for AI-powered tools to access your API documentation via the MCP protocol." An MCP token cannot build a workflow.
> - **Settings > Integrations > API Keys** issues community API keys. Building, testing, deploying and reading workflows needs an API key whose Permissions include **Workflow Builder**.
>
> If an agent can read your API documentation but cannot list your workflows, it has an MCP token where it needs an API key.

## Client configuration

Every client below sends the same header. Replace `<API_KEY>` with the key value; keep it out of the config file where the client can read it from an environment variable instead.

The examples name the server `returning-workflows`; any name works.

### JSON configuration

Most MCP clients read an `mcpServers` object from a JSON file, either in the project (for example `.mcp.json`) or in a user-level config. Add the server there, then start a session in the project:

`Configuration`

```json
{
  "mcpServers": {
    "returning-workflows": {
      "type": "http",
      "url": "https://api.returning.ai/v1/workflow-builder/data-workflows/mcp",
      "headers": {
        "Authorization": "Bearer <API_KEY>"
      }
    }
  }
}
```

Some clients take the same block without `"type": "http"`:

`Configuration`

```json
{
  "mcpServers": {
    "returning-workflows": {
      "url": "https://api.returning.ai/v1/workflow-builder/data-workflows/mcp",
      "headers": {
        "Authorization": "Bearer <API_KEY>"
      }
    }
  }
}
```

A client with an `mcp add` command takes the same values on the command line: the transport `http`, the URL, and the `Authorization` header.

### TOML configuration

If your client's TOML config supports `bearer_token_env_var`, it can read the key from an environment variable rather than a literal. Add a server block to the client's config file:

```toml
[mcp_servers.returning_workflows]
url = "https://api.returning.ai/v1/workflow-builder/data-workflows/mcp"
bearer_token_env_var = "RAI_WORKFLOW_API_KEY"
```

Then export the key in the environment the client starts in: `export RAI_WORKFLOW_API_KEY="$(cat <path-to-key>)"`.

### Other clients

Any client that supports a remote MCP server over Streamable HTTP works with the URL and one header. Send `Authorization: Bearer <API_KEY>` and `Content-Type: application/json`, and accept both `application/json` and `text/event-stream` in `Accept`.

Do not configure a session id. The endpoint answers `400` to a request that carries one, rather than pretending a session exists.

## Which tools a key sees

Tool registration follows the key's permission, and there is one: a key holding `workflowBuilder` sees all 27 tools, and a key without it gets `403` rather than an empty list, so an empty list never means a misconfigured server. `tools/list` returns the whole set, and every call is checked again before it runs.

| `workflowBuilder` grants | Tools |
| --- | --- |
| The eight reads | `rai_list_node_types`, `rai_get_node_schema`, `rai_list_workflows`, `rai_get_workflow`, `rai_export_workflow`, `rai_validate_workflow`, `rai_list_runs`, `rai_get_run` |
| The fourteen writes | `rai_create_workflow`, `rai_import_workflow`, `rai_rename_workflow`, `rai_delete_workflow`, `rai_duplicate_workflow`, `rai_set_trigger`, `rai_add_node`, `rai_update_node`, `rai_delete_node`, `rai_move_node`, `rai_duplicate_node`, `rai_add_branch`, `rai_update_branch`, `rai_delete_branch` |
| Test, deploy and retry | `rai_test_node`, `rai_test_workflow`, `rai_deploy_workflow`, `rai_undeploy_workflow`, `rai_retry_run` |

There is no per-tool permission and no second permission for a live test or a retry. What keeps an agent from spending money is the mode it passes to the two test tools, and the `Idempotency-Key` discipline on a retry, both described below, and both things to put in the agent's instructions rather than in its key.

## The tools, grouped like the catalog

### Discover

- `rai_list_node_types` returns the catalog: every step type and trigger type, its option schema, whether it has side effects, the limits, and `platformActionFields`. That last block is the community's own field keys, split into what a read action may use and what a write action may use. Call it first, then take every field key from it rather than guessing one.
- `rai_get_node_schema` returns one type's schema, for a node type or a trigger type. For `platform-action` it also returns the field keys. Pass a `workflowId` and it also returns `triggerOutput`: the trigger id, the trigger type, the dotted paths present in the trigger's last recorded sample payload, and the sample itself, so a step can be bound to the real event shape without reading anything else.

### Read

- `rai_list_workflows` lists the community's workflows with their status and revision, paged.
- `rai_get_workflow` returns one workflow: trigger, steps in canvas order with router branches nested, the revision, and `redactedPaths`.
- `rai_export_workflow` returns the portable definition with every credential redacted.
- `rai_validate_workflow` runs the static check. Every finding is a warning, `valid` is always `true` (so read `warnings`, not `valid`), and nothing is refused: deploy returns the same list.
- `rai_list_runs` lists a workflow's runs, newest first, paged and filterable by status.
- `rai_get_run` returns one run with its per-step results.

### Write

- `rai_create_workflow` creates a draft with an optional trigger and steps.
- `rai_import_workflow` creates a workflow from an export document.
- `rai_rename_workflow` changes the name.
- `rai_delete_workflow` deletes a workflow with its trigger, steps and run history, including a deployed one.
- `rai_duplicate_workflow` copies a workflow into a new draft.
- `rai_set_trigger` creates or replaces the trigger. It shows a webhook key only when the call mints one, which is when the workflow had no webhook key stored.
- `rai_add_node`, `rai_update_node`, `rai_delete_node`, `rai_move_node`, `rai_duplicate_node` manage steps, including Code steps: `type: "code"` with `options.inputs` and `options.sourceCode.code`.
- `rai_add_branch`, `rai_update_branch`, `rai_delete_branch` manage router branches.

### Test

- `rai_test_node` runs one step. `live` is the default; pass `mode: "dry-run"` for a side-effect-free check.
- `rai_test_workflow` walks the whole workflow and returns a per-step trace, with the same two modes. It needs a trigger.

### Deploy

- `rai_deploy_workflow` takes a workflow live and returns the validator's findings as `warnings`. It never refuses on them.
- `rai_undeploy_workflow` takes it out of service without deleting it.

### Runs

- `rai_retry_run` queues a failed run again. It needs no confirmation argument, and accepts an `idempotencyKey`.

## The revision chain

Every mutating tool takes an optional `revision` and returns the new one, so an agent chains edits without re-reading the workflow between calls.

1. `rai_create_workflow` returns the first `revision`.
2. Pass that value as `revision` to the next write, for example `rai_add_node`.
3. The tool returns the workflow or step with its new `revision`. Use that one for the next write.

Passing `revision` is the guard, not the rule. Send it and an out-of-date value returns an error result with `code: "VERSION_CONFLICT"` and `currentRevision`; tell the agent to call `rai_get_workflow`, take the new revision, and rebuild the change rather than retrying with the old one. Omit it and the call applies as the last write, which is fine for an agent working alone and wrong for one editing a workflow a person also edits.

A deployed workflow takes the same tool call with no extra argument. Editing live is immediate: there is no draft to promote and no flag to pass.

## Live or dry run: the one choice that costs money

Three tools can spend money or call an outside system. On the two test tools `mode` is the switch, and **`live` is the default**, so an agent that says nothing runs the real services. The retry tool has no switch:

- `rai_test_node` and `rai_test_workflow` with `mode: "live"` really run the steps, which can pay a reward or send an email. With `mode: "dry-run"` nothing is written, sent or paid: read-only steps execute, and everything that would write, send or call returns the inputs it would have used.
- `rai_retry_run` has no modes at all: it always re-executes the workflow.

> Put this in the agent's instructions
>
> The API will not ask: pass `mode: "dry-run"` for every iteration, and choose `live` only when a person has asked for that specific run.

A retry's repeat behaviour has two guards that work in a fixed order:

1. If this run already accepted a retry under the same `idempotencyKey`, that answer is replayed with `alreadyRequested: true` for 24 hours and nothing is queued.
2. Otherwise, a claim is taken on the run and its current state: while it is held (ten minutes) a call with no key or a different key is refused with `RETRY_IN_PROGRESS`.

After the claim expires, a new key queues a new re-run, which really executes and can pay twice. So the key protects the same repeat; it does not make retrying harmless.

Two more properties keep a tool call honest:

- **Arguments are strict.** A tool rejects an argument it does not declare instead of ignoring it, so a call cannot carry an HTTP-only field by accident. When a refusal says an argument is unexpected, check the tool's schema rather than retrying.
- **Destructive tools say so.** `rai_delete_workflow`, `rai_delete_node`, `rai_delete_branch`, `rai_test_node`, `rai_test_workflow`, `rai_deploy_workflow` and `rai_retry_run` are annotated `destructiveHint: true`, because each can change what members get or remove configuration. A client that honours the hint asks a person first. The two test tools carry it because `live` is their default.

## A worked prompt

Paste this into an agent with the key configured. It exercises the whole journey without spending anything:

`Prompt`

```plaintext
Using the returning-workflows MCP tools, build a draft workflow called "VIP bonus" with an event ingest trigger whose batch id path is `metadata.batch_id` and whose record id is `trade_id`. Add a step that reads the member's XP by email from the trigger payload, then a router with one condition branch "XP over 100" and a fallback branch. In the condition branch add a step that adds 10 XP. Take every field key from the catalog rather than guessing one, and tell me which keys you used. Validate the workflow, dry-run it with a payload for `trader@example.com` showing 150 XP and one showing 50 XP, and tell me which branch each one takes and what the write step would do. Do not deploy and do not run anything live.
```

What a correct run of that prompt looks like:

1. `rai_list_node_types` for the step and trigger schemas and for `platformActionFields`: the read key is `total_xp` and the write key is `xp`.
2. `rai_create_workflow` with the event ingest trigger.
3. `rai_add_node` for the read step, then for the router, then for the write step inside branch 0.
4. `rai_validate_workflow` returning `warnings: []`. If it returns `ROUTER_OPERATOR_UNSAFE`, the agent built an `OR` or `WHERE` group and should rebuild it with `AND`: no validation finding refuses the deploy, so the agent is the last check.
5. `rai_get_node_schema` with `type: "platform-action"` and the workflow id, to read the trigger's output shape before binding the member email.
6. `rai_test_workflow` twice with `mode: "dry-run"` and the two payloads, reporting `simulated` for the write step and the branch each payload takes.
7. A summary that names the workflow id, the revision it left and the field keys it used, and states that nothing was deployed.

Ask for deployment in a separate turn, so a person can read the dry-run trace first.

## Errors an agent will meet

A tool failure is a normal tool result with `isError: true`; the text carries the same structured code the REST route returns.

| Code | What the agent should do |
| --- | --- |
| `VALIDATION_FAILED` | Read the named `path`, fix the argument, call again. |
| `VERSION_CONFLICT` | Re-read with `rai_get_workflow`, take `currentRevision`, rebuild the change, or omit `revision`. |
| `RATE_LIMITED` | The community's API rate limit was reached: wait for the window, then retry. |
| `REDACTED_VALUE_WITHOUT_STORED_SECRET` | A redaction marker was sent where nothing is stored: send the real value. |
| `NODE_NOT_IN_WORKFLOW` | A branch or an option names a step of another workflow. Reference only steps this workflow owns. |
| `LEADERBOARD_NOT_IN_COMMUNITY` | A leaderboard step names another community's leaderboard. Use one of this community's. |
| `RETRY_IN_PROGRESS` | A retry of this run is already claimed. Read the runs list rather than retrying. |
| `API_KEY_PERMISSION_DENIED` | The key has no `workflowBuilder` permission. |

The remaining findings, such as

- MISSING_SECRET
- UNKNOWN_USER_FIELD
- REFERENCE_UNKNOWN_FIELD
- REFERENCE_SHAPE_MISMATCH
- REFERENCE_UNRESOLVABLE
- POST_LOGIC_REQUIRED
- NODE_LISTED_TWICE
- RETRY_REPEATS_SIDE_EFFECTS
- ROUTER_OPERATOR_UNSAFE
- TRIGGER_REQUIRED

, are not errors. They arrive in the `warnings` of `rai_validate_workflow` and of `rai_deploy_workflow`, the deploy goes ahead, and an agent should treat each one as a task rather than a failure.

## Gotchas

- The tool list is all or nothing: a key with `workflowBuilder` sees all 27 tools, and a key without it gets `403`. If the client shows fewer, check the client, not the key.
- A webhook trigger key is returned in clear only by the call that mints it. If the agent loses that response, `rai_get_workflow` shows only `["__REDACTED__"]`, and `rai_set_trigger` keeps the stored key rather than accepting a new one. A different key needs the trigger set to another type and back to `webhook`, which mints one.
- Tools return the same redacted payloads as the API: credentials arrive as `["__REDACTED__"]`. Send the marker back to keep a stored credential.
- Tool payloads are JSON text inside `result.content[0].text`. Parse the text, then check `isError` before reading fields.
- The endpoint holds no state between calls, so an agent that forgets the revision has to read the workflow again. Ask it to keep the revision in its working notes for the session, and tell it to send that revision on every write so a concurrent change is refused rather than overwritten.
- Neither mode proves the trigger is reachable from outside. Firing the trigger for real is a separate, deliberate step.

## Endpoints on this page

- POST [Call the workflow tools over MCP](https://docs.returning.ai/api-reference/workflow-builder/call-the-workflow-tools-over-mcp.md) `/mcp`

Paths that start with `/v1/` are full paths; the rest are relative to `https://api.returning.ai/v1/workflow-builder/data-workflows`.
