Describe an automation in plain English and get a working flow with human approval steps, built in your FlowRunner workspace. Free plan, no credit card.
claude mcp add flowrunner --transport http https://app.flowrunner.ai/mcp
FlowRunner Prompt to Flow MCP Server tools
37
Read first, before any other tool. Covers working with the user, how a flow runs, expressions ({ text }, {{...}} references), ways to start a flow, the blocks, groups and loops, extensions and connections, the build workflow, and what is not supported.
List the workspaces this user can access: { id, name }. Pass id as workspaceId.
List the workspace's flows, one row per flow: flowId, name, versionId (latest saved version; build against it), version, status, liveVersionId (non-null only when a different version is LIVE; a LIVE version cannot be edited).
Create an empty flow in the workspace; returns { id, flowId, version, name } — pass id as versionId to later calls. The workspace's connections and extension catalog load automatically. name: 3-50 characters. description: optional text saying what the flow does.
Make a saved flow version LIVE: its triggers start and its schedule takes effect. Also resumes a PAUSED version. Rejected if the flow is invalid (server status INVALID, or the validation saved by the last apply_operations is false): fix the issues in get_flow_summary globalIssues and node issues, apply_operations, then retry. withSwitchover: true when another version of this flow is LIVE (without it the call fails): the old version takes no new runs, finishes running ones, and its schedule is dropped.
Pause a LIVE flow: its triggers and schedule stop starting runs; nothing is deleted. Running runs continue, but a run waiting at a mid-flow callback cannot be resumed while paused. A PAUSED flow is not editable — to edit, use stop_flow. start_flow resumes it.
Stop a LIVE or PAUSED flow: no new runs start; runs waiting (at a wait or a callback) are ended; then the version returns to READY, editable with apply_operations. Nothing is deleted; start_flow makes it LIVE again.
Run the whole flow on a clock, for a flow without a trigger ("every morning at 9", "every weekday"); not a polling trigger's frequency. Requires a saved flow version that is not LIVE or PAUSED; the schedule takes effect at start_flow; each version has its own. schedule: "once" | "custom" (everySeconds required) | "daily" (everyDays, default 1) | "weekly" (weekDays required: 1-7, 1 = Sunday … 7 = Saturday; everyWeeks default 1); no monthly. The schedule values are the same as a polling trigger's frequency. enabled defaults to true. Partial update: omitted startDate/endDate/enabled/onlyScheduledInitiations/allowApiInitiationsWhenScheduleExpired keep their current values; with no existing schedule, startDate defaults to about 30 s from now and endDate to none. endDate: null clears the expiry. enabled: false saves the schedule turned off (as stop-scheduled-runs does). onlyScheduledInitiations: true — only the scheduler may start runs. allowApiInitiationsWhenScheduleExpired: true — after endDate other starts are allowed again; only together with onlyScheduledInitiations.
Return the flow version's current schedule (metaInfo.schedule), or null if none was ever set.
Built-in block catalog by category. Block categories list { blockType, name, description }; pass blockType to get_block_schema and add_block. agentTools lists { toolDefinitionId, name, description }: built-in AI Agent tools for add_agent_tool, no get_block_schema step. Only types this server can add and configure are listed. Extension blocks are not listed: search_extensions, search_shared_extensions, list_extension_methods.
List this workspace's own extension services, one row per service: custom extensions, MCP servers, and with isAgentTool: true also knowledge bases. Shared extensions: search_shared_extensions or search_extensions. search filters by name/integrationName; limit caps rows (default 30). Each row: hostType/packageId/serviceId, requiresOAuth (true/false/null), actionCount, triggerCount. Pass the row's triple to list_extension_methods.
List the methods (actions and triggers) of one extension service, given by hostType/packageId/serviceId. search and registerAs filter. isAgentTool (default false): false lists methods for add_block, true lists tools for add_agent_tool, as in search_extensions. Pass a method id to get_block_schema next.
Keyword search (1-3 words, e.g. "send email", "google sheets") over extension methods: this workspace's own and shared extensions. Each row is one method: id, registerAs, requiresOAuth, and its service's hostType/packageId/serviceId. isAgentTool (default false): false finds actions and triggers for add_block; true finds tools for add_agent_tool (actions, MCP tools, knowledge bases; never triggers). registerAs filters by method kind: "KNOWLEDGE_BASE" only with isAgentTool: true; "POLLING_TRIGGER" / "REALTIME_TRIGGER" only with isAgentTool: false. To narrow to one service pass hostType, packageId and serviceId together; a partial triple is rejected. requiresOAuth: a connection is needed, not that one exists — see get_block_schema connections. limit: default 30, max 100. Without query: empty when isAgentTool is false; with isAgentTool: true lists this workspace's own tools (built-in tools: list_block_types agentTools).
Search shared extensions by the meaning of a task ("send notifications to a team chat" → Slack, Telegram, Teams…); returns services. Default 15, max 100. Next: list_extension_methods with the row's hostType/packageId/serviceId and the same isAgentTool.
Return a block type's fields, params, sampleResult, connectionInfo/connections, serviceConfig and guide. type: a blockType from list_block_types, or an extension method id from search_extensions / list_extension_methods; not an agentTools id or a knowledge base. guide says how to configure this block; expression syntax is in the readme (Expressions). Call before add_block / configure_block of that type. type may be an array: an invalid type comes back as { type, error } in its position, the others resolve normally. operationId: single type only (rejected with an array); returns only { type, blockType, hasResult, operationDetail }.
Read this workspace's current config values for an extension service (e.g. a Sandbox/Production environment) as { [name]: value }. Applies only when get_block_schema serviceConfig is non-null. Which required values are missing: serviceConfig.missingRequired.
Set this workspace's config values for an extension service (e.g. a Sandbox/Production environment, a cluster zone). configValues: { [name]: value }; names, types and required come from get_block_schema serviceConfig.fields; any other name is rejected with the list of valid names.
List this workspace's knowledge bases: the source of knowledgeBaseId for knowledge-base-* blocks (get_block_schema does not list ids). status "STALE": needs re-syncing, may return outdated results.
Apply a batch of operations to the flow at versionId, in order, then save — one call for everything you need built, not one per block/edge. Operations: add_block, configure_block, wire_edge, remove_edge, remove_block, set_condition_parts, add_router_case, remove_router_case, add_ai_router_decision, set_data_bucket_fields, add_agent_tool, configure_agent_tool, remove_agent_tool, add_placeholder, update_placeholder, remove_placeholder, set_flow_descriptions; each variant describes its fields. A LIVE version (list_flows' liveVersionId) cannot be edited: the whole batch is rejected. To change a live flow: stop_flow (pause_flow does not make it editable), apply_operations, then start_flow if it should run again. Atomic: if any operation fails, nothing is applied or saved; the error names the failed operation's index — fix it and resend the whole batch. localAlias: add_block (nodeId), add_router_case (caseId), add_ai_router_decision (decisionId) and add_agent_tool (the new toolId) accept a localAlias — any string (e.g. "trigger1"), unique across the batch. Any later operation in the same batch can pass it wherever that id is expected (nodeId, groupId, fromNodeId, toNodeId, branch, caseId, toolId); it is replaced by the generated id once the creating operation has run. It exists only within this call; for anything created before the batch use the real id. Result: { id, version, elementCount, status, errorMessage, results, issues, suggestions } — status/errorMessage: the server's verdict; results: one { index, operationName, operationResult } per operation, in order; issues: validation state of the final flow after all operations (as get_flow_summary shows), not per operation — they never block the save; fix them in a later batch. suggestions: optional improvements (descriptions for callers); they never affect running the flow.
Rearrange every block's position as a left-to-right tree (the canvas auto-arrange), saved immediately — including blocks a person placed. apply_operations positions only the blocks it adds (to the right of the existing ones) and never moves blocks already on the canvas.
List the operators for a condition block's conditionParts, optionally filtered by data type ("STRING", "INT", "DOUBLE", "BOOLEAN", "OBJECT", "ARRAY", "DATETIME"). Use the returned operator values in set_condition_parts.
Return the current options (label + value) of a param whose get_block_schema entry has a non-null dictionary, e.g. a spreadsheet, a mail label, a chat user. connectionId: only for a service that requires OAuth; an attached tool's connectionId is in get_node_details tools[], a block or tool not yet added has none saved, so pass it. dependsOnValues: { [paramName]: value } for each param in the entry's dependsOn (e.g. the document before its sheet); a configured node's values are in get_node_details fields; a missing one is rejected, naming the param to set first. search filters; cursor pages. Pass the chosen value in configure_block / configure_agent_tool params and its label in labels[paramName].
List the OAuth connections that exist in this workspace — created in the console or through a get_connection_url link. Use a connection's _id as a block's connectionId.
Return an OAuth sign-in URL for a service that needs a connection. hostType/packageId/serviceId: from get_block_schema connectionInfo or a search/list row. Rejected for a service that does not use OAuth. Once the account is connected, list_connections returns the new connection; set it with configure_block connectionId.
List AI providers and their current models: the only source of valid aiProvider / aiModel values (ai-agent, ai-router, condition aiQuestion, …). Provider ids are upper case with underscores ("ANTHROPIC", "OPEN_AI", "GOOGLE_GEMINI_AI"); configure_block accepts any string, and a wrong id fails at save or run. Deprecated models are excluded. Each model: { id, label, features }. provider: a provider id, to return only its models.
List the AI keys saved in this workspace: { id, provider, name } each; the secret is never returned. Pass id to the block's AI API key field.
Compact overview of the flow: every node's id/type/name/status, every edge, the flow description, the Initial Data fields the blocks read (each with its description and the blocks using it), the flow's Placeholder Data (values truncated), flow-wide issues (including services still missing required config, per service), and suggestions: optional improvements that never affect running the flow. Call before wiring or finishing, not after every add_block. configId (from get_flow_instance, together with that run's versionId): the flow as it was when that run started, without issues, status and suggestions; read-only, a snapshot of the blocks' configuration rather than an exact reconstruction.
Full human-readable detail for one node: every current field value, its connections, and validation issues. configId (from get_flow_instance, together with that run's versionId): the block as it was when that run started, without validation issues; read-only, a snapshot of the blocks' configuration rather than an exact reconstruction.
Final internal-format flow JSON for the flow — call once you're done, not to inspect progress. configId (from get_flow_instance, together with that run's versionId): the flow as it was when that run started; read-only, a snapshot of the blocks' configuration rather than an exact reconstruction.
Execute one saved block now, with real side effects (sends the email, writes the row, calls the API); not idempotent. Runs the version saved on the server at this versionId; the node must have no issues. A TRIGGER only arms itself and returns at once without polling. repeat, list-iterator, actions-group, triggers-group are rejected — test-run a child. condition, router and ai-router do execute. A block reading upstream results sees data only from upstream blocks test-run earlier. Polls up to ~24 s, then returns the last state with completed: false; calling again re-runs the block.
List what can run in the workspace, one row per tool: toolId (pass unchanged to get_dynamic_tool_schema and run_dynamic_tool), type, name, description, available (false comes with unavailableReason). Type callFlow runs a workspace flow with data you provide. Read the readme "Dynamic tools" section first.
Return a dynamic tool's description and paramsSchema (JSON Schema of run_dynamic_tool params; fields differ per tool). toolId: from get_dynamic_tools. Call it before run_dynamic_tool.
Run a dynamic tool, with side effects. toolId: from get_dynamic_tools; params must match paramsSchema from get_dynamic_tool_schema. The result always carries executionId.
List the runs of one flow version, newest first. flowId, versionId and liveVersionId come from list_flows; without versionId the LIVE version is searched; a flow with no LIVE version needs versionId. Filters: status, onlyWithErrors, nameContains, period (lastHour, last24h, last7d, last30d) or a custom range from/to (ISO time); not both, limit (default 20, max 50), offset. includeSummary adds the count per status for the same period and other filters; the status filter does not apply to it. Row: instanceKey (pass it to get_flow_instance, get_flow_instance_block, get_flow_instance_logs), executionId, name (only when set), status, initiator, startedAt, endedAt (ISO UTC), durationMs, hasErrors, errorHandled (an error happened and the run continued), terminationReason (TERMINATED runs), isTestRun. Result: { versionId (the version searched), runs, summary?, olderRunsHiddenByPlan (true when older runs are not visible), nextOffset (pass as offset for the next page) }.
Overview of one run; instanceKey comes from list_flow_instances. include: initialData (the run's Initial Data, values shortened, initialDataTruncated lists cut paths), pendingOn (blocks a waiting run is paused at; [] when it waits on nothing); each is fetched only when listed. Result: executionId, versionId, configId (pass it with versionId to get_flow_summary / get_node_details / get_flow_json to see the flow as it was when this run started), configChangedSinceRun (false: the flow has not been started again since this run, it runs the same configuration now; true: it was stopped and started again since, its blocks may have changed — compare before fixing; compared with the configuration the version was last started with, so edits made after stopping it and not started yet are not covered), status, terminationReason, startedAt/endedAt (ISO UTC), durationMs, initiator, failure (the first failure not routed to a failure path: nodeId, nodeName, nodeType, iteration, blockExecutionKey, message, httpCode, errorCode; only for TERMINATED runs), steps (one row per block in order of first execution: runs, failedRuns, failedIterations [{ iteration, blockExecutionKey }], firstError, totalMs, failureRouted), notExecuted (blocks the run never reached). Block names and types come from the run's own configuration; they are absent when it is no longer available. A RUNNING run is partial. A block's input and output come from get_flow_instance_block: blockExecutionKey for a failed execution, or instanceKey with nodeId and iteration for any other. olderRunsHiddenByPlan: true replaces the result when the run is not visible.
Input, output and error of one execution of a block in a run. One of two ways to call: (1) blockExecutionKey from get_flow_instance (failure.blockExecutionKey, steps[].failedIterations[].blockExecutionKey); (2) instanceKey (from list_flow_instances) with nodeId and optional iteration (default 0, position inside the innermost loop). When several executions share that nodeId and iteration (nested loops) the result is matches [{ match, startedAt, failed, blockExecutionKey }]; repeat the call with match or a blockExecutionKey. Do not mix the two ways. Result: success, error { message, httpCode, errorCode }, input, output, retryAttempts (attempt, at, httpStatus, transportErrorType, errorMessage, delayBeforeNextMs, responseBody); way (2) also returns nodeId, iteration, startedAt, durationMs. Data is returned as recorded. Long values are shortened; truncated lists { path, kind, originalSize } for each cut. path (the same syntax as expressions, e.g. output.items.[3].body, output.items.[last]; paths start with input, output or retryAttempts; copy them from truncated[]) returns only that branch in full detail. maxChars (2000..60000, default 30000) is the size budget of the response. A block with no executions in the run returns executed: false; an iteration it did not run returns executed: false with ranIterations. A run whose data is too large returns its structure with tooLarge: true. olderRunsHiddenByPlan: true replaces the result when the run is not visible.
Log messages one run wrote; instanceKey comes from list_flow_instances. Pages run from the newest messages backwards: the first call returns the latest limit messages (default 100, max 1000), offset skips that many newest messages; each page is in time order (oldest to newest). Filters: nodeId (one block), level, textFilter (text contains), period (lastHour, last24h, last7d, last30d) or a custom range from/to (ISO time); not both. Result: { executionId, logs [{ at (ISO UTC), level, nodeId, nodeName, text }], hasMore (older messages exist), nextOffset (pass as offset for the older page) }. Text longer than 1000 characters is shortened. olderRunsHiddenByPlan: true replaces the result when the run is not visible. An instanceKey that matches no run returns an empty list.
Overview
Workflows you can talk to
FlowRunner Prompt to Flow turns a plain-English description into a working automation, built as editable blocks in your FlowRunner workspace. From the same chat you can run the flow, ask why a run failed and get the fix, and set its schedule.
When a decision matters, the flow stops and asks a person over email or Slack, then carries on with their answer.
What you can build
Flows that start on a schedule, a webhook, or an event in another app
Steps across 2,300 integrations, including Google Sheets, QuickBooks Online, Stripe, Slack, HubSpot, and NetSuite
AI agent steps that use your own model keys
Approval steps that pause the run until the right person decides
If something in your description is unclear, the builder asks before it builds. You connect your accounts through the links it gives you, review the flow on the canvas, and start it when you're ready. Nothing runs until you do.
Example prompt
Build a flow that turns my monthly sales log in Google Sheets into QuickBooks Online invoices. On the 1st of each month, read last month's rows that don't have an invoice number, group them by customer, and match each customer and product in QuickBooks. Email me one approval request with each customer's total and any rows that didn't match. Create invoices only for the customers I approve, and write the invoice number back to the sheet so nothing is billed twice.
That prompt produced a 23-block flow in under six minutes.
Setup
Claude Code:
claude mcp add --transport http flowrunner https://app.flowrunner.ai/mcp
Then run /mcp and sign in.
Claude Desktop or claude.ai: Settings, Connectors, Add custom connector, and paste https://app.flowrunner.ai/mcp.
Sign in with a FlowRunner account. The Free plan works, no credit card needed. Every plan includes every integration, unlimited users, and unlimited workflows. Pricing: https://flowrunner.ai/pricing
Frequently asked questions
1
What is the FlowRunner Prompt to Flow remote MCP server?
The FlowRunner Prompt to Flow remote MCP server is a hosted Model Context Protocol endpoint at https://app.flowrunner.ai/mcp, so AI assistants can connect to it without installing or running anything locally.
2
How do I connect to the FlowRunner Prompt to Flow MCP server?
Add the endpoint https://app.flowrunner.ai/mcp to any MCP-compatible client such as Claude Code, Cursor, or VS Code. The setup snippets on this page configure each client in one step.
3
Does the FlowRunner Prompt to Flow MCP server require authentication?
Yes. FlowRunner Prompt to Flow uses OAuth: the first time you connect, your MCP client opens a browser window to sign in and authorize access, then reuses the credentials for future sessions.
4
Which transport does the FlowRunner Prompt to Flow MCP server use?
FlowRunner Prompt to Flow exposes a Streamable HTTP endpoint, the transport used by remote MCP servers and supported by all major MCP clients.
Comments