Skip to content

Flowbrew MCP reference

Check or regenerate this reference whenever MCP tool registrations change; stale hand-maintained counts have already drifted from 14 assumed tools to 26 live tools.

This is the current 37-tool set: 36 tools registered in packages/functions/src/mcp-server.ts plus list_plugins, registered in packages/functions/src/mcp-plugin-catalog.ts. All tools run in the workspace derived from the authenticated API key or OAuth identity. In the input column, ? means optional; null is a required explicit JSON null where shown.

Breaking change (#137): Unknown top-level MCP tool arguments are now rejected instead of silently ignored. The JSON-RPC tool response has isError: true and names the offending key in an MCP error -32602 (InvalidParams) message.

Unexpected tool exceptions return isError: true with Internal error (correlation <uuid>). Unexpected authentication failures return HTTP 500 with the same correlated text in the JSON error field. The MCP Worker logs the ID once with the tool name (or authentication) and a bounded cause category/type; raw messages, stacks, arguments, credentials and nested causes are excluded. Use that ID and the response’s cf-ray to locate the failing request. Expected validation/domain messages and explicit error results retain their existing text. Correlation adds no retries: an error or disconnect can still leave a mutation’s effects uncertain. Legacy e2e callTool uses the shared [e2e-io] diagnostics and remains a single attempt with the SDK’s default timeout.

Discovery, plugins, feedback, and connections

Section titled “Discovery, plugins, feedback, and connections”
Tool Input Purpose and caller note
get_started {} Returns a concise concept map and the recommended cross-tool sequence for building and running a workflow, adding a connection-backed brick, and subscribing a trigger.
list_plugins {} Lists public plugins and private plugins installed in the workspace, including connection types, brick contracts, and trigger definitions. Execution and trigger lifecycle endpoints are never returned.
delete_plugin { id } Deletes a private plugin owned by the authenticated workspace and all its versions. Public plugins cannot be deleted. Remove its connections and trigger instances first. Returns { id, deleted }.
promote_plugin { pluginId, versionId } Atomically makes a tested candidate current/live without re-checking semver. Private ownership or public publishing permission is required. Clears overrides selecting that version; other overrides and existing instance pins remain.
set_plugin_version_override { pluginId, versionId } Selects a candidate for this workspace; versionId: null clears the override. Catalogs and new instance resolutions use the selected version. See plugin version channels.
submit_feedback { title, body } Submits a Flowbrew observation, complaint, or bug for human review; title is limited to 120 characters and body to 4,000.
list_feedback { status? } Lists workspace feedback newest first; status filters by open, accepted, rejected, or resolved. Results contain only id, title, body, a friendly status label, resolution, created_at, and updated_at; internal issue links are never returned.
list_connections { projectId: string | null } With null, lists workspace connections only; with a verified project ID, lists workspace plus same-project connections. Returns metadata and status, never credentials or audit fields.
create_connection { pluginId, connectionTypeKey, label, projectId: string | null } Creates a credential-less pending_verification connection using the existing owner and scope checks. Returns { connectionId, consoleUrl }; a human completes credential entry and validation in the console using their normal browser session.
update_connection { id, label } Owner-only label update. Trims and validates the label like create; returns { id, label }. Credentials, type, scope, and workflow/trigger references stay intact.
call_brick { brickId, input, connections?: { <slot>: <connectionId> }, connectionId? } Executes one synchronous workspace-scoped brick and returns { callId, brickId, pluginVersionId, output }. Required input accepts any JSON value. Explicit bindings only; shorthand requires exactly one declared slot and cannot accompany a map. See the direct-call contract below.

call_brick uses the same authenticated workspace API-key/OAuth authority as start_workflow, with a credential recheck before preparation. It creates no workflow, version, durable instance, workflow history or instance-plugin pin. It selects one workspace-visible plugin version for the request. Project-owned connections and a caller projectId are unsupported; use list_connections({ projectId: null }) for eligible metadata. It never selects a connection automatically. Zero/multiple slots with connectionId, or both binding forms, return an actionable error. All ordinary slot, workspace, provider, type, configured-status and revision checks still apply.

One attempt only: repeated calls are new executions. Timeout/disconnect means effects may have occurred; do not automatically retry. Callbacks, child workflow starts and project-required core bricks are rejected before dispatch. The overall deadline is 30 seconds; endpoint time is at most 25 seconds and the manifest timeout. Serialized input and endpoint response are each limited to 1 MiB UTF-8. The stage-isolated workspace limiter admits 10 calls per 60 seconds, coarse and per location rather than a global quota. input.dryRun passes through unchanged as schema-validated business input; there is no top-level dryRun or platform side-effect guarantee.

Unknown arguments retain -32602. Execution errors return isError: true and { error: { code, message, phase, correlationId?, upstreamStatus?, outcome }, callId? }. outcome is not_started (no dispatch) or unknown (effects may have occurred). Codes are unavailable, input_invalid, connection_invalid, output_invalid, unsupported, rate_limited, timeout, invocation_failed, and admission_failed. Output must be JSON-representable and match the selected manifest schema before success is recorded. Invalid output returns output_invalid, outcome: "unknown" and the callId; validation cannot undo effects. Handler-supplied error text is never returned or stored in caller-visible records for direct calls, including explicitly marked SDK handler errors. Failures use stable codes and generic, platform-authored messages. Arbitrary errors and credentials are suppressed.

Plugins see reserved workflowId: "mcp-call-brick" and the fresh callId as workflowInstanceId; these do not identify a persistent workflow. Separate direct_brick_calls records capture business input/output up to 64 KiB per field, with a 30-day working window and bounded cleanup; physical deletion can lag. Abandoned records become unknown, never inferred success. They are not a guaranteed audit or replay log. There is no direct-call history MCP reader in v1; list_brick_calls continues to read workflow attempts only. See retention and payload capture.

Tool Input Purpose and caller note
create_trigger_instance { triggerDefinitionId, scope, projectId, config, connectionId } Registers a workspace- or project-scoped trigger. projectId and connectionId are required fields that may be null; the instance becomes active only after plugin acknowledgment. This does not subscribe any workflow; call create_trigger_subscription afterward to receive events.
update_trigger_instance { id, config } Replaces the full config in place for core cron/webhook/email only; id, public endpoint/inbound address, secrets, subscriptions, connection, and enabled state stay intact. Endpoint/secret and schema-marked create-only fields cannot change. Cron recomputes its next fire from the new crontab/timezone; a due fire or pending retry returns a retryable error until delivery succeeds. Only active instances accept updates; disabled instances, reconnect, or registration in progress return a retryable error. Identical config is a no-op only while active. External/generic plugin updates are not supported in place; recreate the trigger.
list_trigger_instances {} Lists trigger instances in the workspace. Inactive instances do not expose a public endpoint.
reconnect_trigger_instance { id, connectionId } Tears down the old binding, binds the selected connection (or null where allowed), and registers a fresh ingestion credential.
disable_trigger_instance { id } Immediately invalidates ingestion and removes the public endpoint, then requests idempotent plugin teardown.
enable_trigger_instance { id } Re-registers a disabled instance with a fresh ingestion credential and activates it only after acknowledgment.
delete_trigger_instance { id } Tears down and deletes an instance. Delete all of its subscriptions first.
create_trigger_subscription { triggerInstanceId, workflowId } Subscribes one scope-compatible workflow. Duplicate pairs are rejected and each trigger instance permits at most five subscriptions.
list_trigger_subscriptions { triggerInstanceId?, workflowId? } Lists subscriptions in the authenticated workspace. Both filters must match when supplied; omit them to list all subscriptions. Returns { triggerSubscriptions: [{ id, triggerInstanceId, workflowId, createdAt }] }, including the IDs needed for deletion.
delete_trigger_subscription { id } Deletes one subscription in the authenticated workspace. Use list_trigger_subscriptions to find its ID.
Tool Input Purpose and caller note
list_api_keys {} Lists key IDs, labels, timestamps, and revocation state without exposing raw key values.
revoke_api_key { apiKeyId } Revokes a key by ID in the workspace; it never reads or returns the raw value.
create_project { name } Creates an organizational project in the workspace. Trims surrounding whitespace and collapses internal whitespace to one space; the normalized name must be 1–100 characters and unique in the workspace, ignoring case. A duplicate is a tool error: project 'X' already exists in this workspace: <id>.
list_projects {} Lists projects in the workspace.
rename_project { id, name } Renames a project in the workspace with the same normalization, length and uniqueness rules as creation. Conflicts are tool errors naming the existing project id.
delete_project { id } Deletes an empty project. Workflows are never silently deleted or detached, so references must be removed first.

The project-name migration applies the same whitespace normalization, preserving embedded NULs and all following text. Legacy names that normalize to empty use Untitled project before duplicate suffixes are allocated. Suffixes skip every normalized name already present in the workspace, using the unique index’s ASCII case-insensitive comparison; migrated names remain unchanged when normalized again.

Tool Input Purpose and caller note
create_workflow { name, projectId? } Creates an empty shell, optionally in a verified project. It has no deployed code until a description-driven update_workflow succeeds.
read_workflow_source { workflowId, versionNumber? } Read retained generated TypeScript for an authenticated workspace workflow. Defaults to the current deployed version (including rollback); explicit versions must exist in the published version index. Missing/foreign workflows, versions without retained TypeScript, and missing source return not-found errors. Every literal value is replaced with a fixed placeholder, including ordinary strings, template quasis and literal parts of interpolations, numeric/bigint/regex values, JSX text/attribute strings, module specifiers, quoted object keys and type literals. Every name token (including property/private/JSX names, import/export names and TypeScript type/member/parameter names) is deterministically alpha-renamed to id1, id2, etc., consistently per distinct normalized name within one response. Only the fixed keyword, standard-global and workflow SDK API allowlist in workflow-source.ts survives; Unicode escapes are decoded before the check. Template expressions, keywords and code structure remain visible; boolean/null literals use fixed false/null values. Comments and hashbangs are removed. Parsing/sanitization failures or AST node types outside the explicit allowlist return only [redacted]. Sanitization precedes the 64 KiB UTF-8 cap. Returns workflowId, versionNumber, versionName, source, bytes (sanitized full size), and truncated. Read-only; never returns compiled bundles.
list_workflows { projectId? } Lists workflows: omit projectId for all, pass a project ID for that project, or pass null for workspace-global workflows only.
update_workflow { id, description?, name?, projectId? } Either generates, compiles and mock-test-gates a complete new version from plain language, or changes metadata. Do not combine description with name/projectId; projectId: null makes it workspace-global. Before storage/publication, independent declarative tests execute the candidate with fake steps and explicit brick mocks, creating no real instances or brick calls. Any failed case, malformed/empty suite, infrastructure error or timeout preserves the live version. Tool errors include bounded numbered case pass/fail diagnostics with fixed codes, never payloads or raw exceptions; no automatic repair. Callback/waitForEvent operations currently block publication. After rollback, generation reads the selected version’s source and allocates above the highest reserved/stored version (normally max+1).
rollback_workflow { workflowId, versionNumber } Makes an existing successfully published version current without regenerating code. Returns the workflow and selected schemas. Already-current is a no-op; history and running instances are retained. New starts, including trigger/cron/webhook dispatch, select the rolled-back version. See rollback design and code map.
delete_workflow { id } Deletes the definition and all deployed code versions and frees its name. Existing subscriptions block deletion with “Delete trigger subscriptions before deleting the workflow”; discover and remove them with the subscription tools first. Instance and trigger history remains as an audit log until its own TTL expires.
start_workflow { idempotencyKey, workflowId?, name?, payload? } Starts the current version and immediately returns { instanceId }; it does not await completion. Supply workflowId or name, and a non-empty workspace-scoped idempotency key; a retry with the same key resolves to the same instance. Payload must be a JSON object and becomes event.payload.input.
list_instances { workflowId? } Lists persisted instances, optionally for one workflow, with best-effort live status.
list_brick_calls { instanceId, cursor? } Returns attempt timing, retry metadata and recorded business input/output JSON, including optional handler report objects and callback creators’ immediate responses. Reports share the payload cap, marker and retention; absent reports are null with reportCaptureStatus: "not_produced". Complete JSON is limited to 64 KiB per field; larger values return { "truncated": true, "bytes": N } with status stored. Availability statuses distinguish legacy, absent and previously omitted payloads. Missing, expired or foreign-workspace instances return Instance not found. Pages contain up to 200 whole attempts and 512 KiB; pass nextCursor as cursor to continue. Pointers are not dereferenced.
read_instance `{ instanceId, include?: (“trace” “calls”)[], traceCursor?, callsCursor? }`

Workflow brick invocations also suppress all handler error text, including marked SDK errors. Step/run errors include a platform message, invocation_failed, and the attempt callId; platform DNS diagnostics retain their existing messages. See the workflow error surface and authorization audit.

Omitting include preserves the existing read_instance response: status, optional output/error, durationMs, brickCount, and terminalStatus. Use { instanceId, include: ["trace"] } to add a trace envelope. To continue, pass its nextCursor as traceCursor and repeat include: ["trace"]. An empty/duplicate/unknown include, a cursor without trace inclusion, or a malformed or foreign cursor is an error. The tool surface is 37 tools.

{
"source": "d1",
"coverage": "recorded_attempts",
"triggerOrigin": "unavailable",
"entries": [],
"truncated": false,
"nextCursor": null,
"availability": "available",
"code": null
}

Brick entries have kind: "brick", callId, brickId, invocationId, nullable mode (sync, callback_self, callback_create, callback_wait), startedAtMs, nullable endedAtMs/durationMs, outcome, nullable code, nullable upstreamStatus, attempt: { ordinal, count, retryLimit }, nullable phase, and requestSummary: { inputBytes }. The request summary contains only the recorded input byte count, which is null when unavailable.

outcome preserves the recorded state: running, waiting, completed, failed, timed_out, unknown, expired, or abandoned. Failed or unknown attempts do not prove that upstream effects did not happen. Entry codes are invocation_failed, invocation_timed_out, settlement_unknown, callback_expired, and callback_abandoned, respectively; running, waiting, and completed entries have a null code. A phase is one of resolve brick, authorize connections, validate endpoint DNS, mint job token, fetch brick endpoint, or wait for callback; unrecognized historical text becomes null. Callback wait timing describes the wait, not an outbound request. Callback creators retain their recorded HTTP timing semantics.

Attempt ordinals start at one and counts cover all retained attempts in the logical invocation, including attempts on other pages. They are observations, not guaranteed native retry indexes; legacy mode, retryLimit, byte counts, and end times can be null.

Plugin handlers receive the same attempt callId as the optional signed context.claims.callId, allowing authors to correlate their own intentional diagnostics with an error envelope or trace entry. Retries have distinct call IDs within one invocationId; older tokens can omit the claim. See the SDK logging guidance.

Triggered runs can also have one kind: "trigger_dispatch" entry. It contains callId (a stable platform trace reference), triggerId (definition identity), triggerInstanceId, subscriptionId, nullable receivedAtMs (platform receipt, not a source-event timestamp), startedAtMs (dispatch reservation), nullable endedAtMs/durationMs, outcome (pending, done, or quarantined), code: null, upstreamStatus: null, and attempt: { count }. Brick-only fields are absent. The count observes admitted dispatch/reconciliation operations, including background recovery; DONE redelivery, circuit-breaker rejection and input preflight do not increment it. It is neither a queue delivery count nor a native workflow retry index. done means workflow creation was acknowledged or reconciled from runtime existence, not that the workflow completed. pending can include an ambiguous create; it makes no claim about effects. The ingestion acknowledgement’s HTTP 202 is never an upstream status.

triggerOrigin is recorded when the retained origin exists (even on a page whose cursor excludes its entry), not_applicable for newly recorded manual, MCP or direct/child workflow starts, and unavailable for historical runs or missing best-effort provenance. Recording runs through a private coordinator-only service; write failure never gates dispatch or independently retries creation. Partial/unsettled coverage is possible. There is no backfill. Registration, unregistration, provider failures before ingestion, rejected ingestion and other subscriptions are outside this run’s trace.

Entries are ordered by (startedAtMs, kind, callId). Each page contains at most 200 entries and 512 KiB of UTF-8 serialized trace envelope, including cursor and availability fields. Cursors are opaque, bound to the workspace and instance, and grant no authority. The workspace principal is rechecked on each page, including after the first call’s long poll. Retained instance ownership and all trace queries are scoped to that workspace and run. Expired or foreign instances are rejected. Existing thirty-day visibility applies; this is a live, best-effort view of recorded attempts and dispatch provenance, not a full step journal or an audit/replay guarantee. Restart pagination to refresh earlier unsettled entries. Trigger origins are deleted atomically with history under the existing bounded, terminal-verified cleanup; they add no archive or retention extension. Plugin ownership grants no trace access.

The trace uses a dedicated metadata SQL projection. It never selects or parses business payloads, captures, callback references/tokens, handler error text, exception properties, source-event text, credentials, headers, or URLs. Existing status/output/error semantics and the handler-error suppression boundary remain unchanged. Payload inspection is available separately through list_brick_calls.

The trace database operation has a two-second wait budget. Database failures or an entry that cannot fit the page return availability: "unavailable" with code: "trace_unavailable"; budget expiry returns code: "trace_timeout". Both have empty entries, truncated: false, and a null cursor, while preserving the instance status result. A missing summary from the same unavailable database uses null metrics. Authorization failures remain errors and never return status or trace data. An unavailable trace does not establish that no attempts occurred.