npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

brandbrain-flow-orchestrator-mcp

v0.1.0

Published

MCP server for BrandBrain flow orchestration — compose asset flows, run mock/live generation runs, and fetch outputs from any MCP client

Readme

Flow Orchestrator MCP

MCP server for BrandBrain FlowSpec sessions and asset-generation workflow execution.

This server sits on top of the BrandBrain backend flow-session API. It does not talk to model providers directly. The backend remains the source of truth for:

  • canonical template bundles
  • persisted flow sessions
  • validation
  • mock and live execution
  • run traces and artifacts

What it is for

Use this MCP server when an LLM or operator needs to:

  • seed a new asset-generation flow from a canonical template
  • build or refine a stored asset canvas workflow on demand
  • validate chaining and node structure before generation
  • run the workflow in mock mode first
  • run a confirmed provider call in live mode, or restart supported local asset-set processing from a preserved source sheet
  • fetch a canvas URL that opens the stored workflow in the app

Quickstart

The server is published on npm as brandbrain-flow-orchestrator-mcp and runs over stdio — no global install needed:

npx -y brandbrain-flow-orchestrator-mcp

Point any MCP client at it with the production endpoints:

{
  "mcpServers": {
    "brandbrain-flow-orchestrator": {
      "command": "npx",
      "args": ["-y", "brandbrain-flow-orchestrator-mcp"],
      "env": {
        "BRANDBRAIN_API_URL": "https://api.brandbrain.dev",
        "BRANDBRAIN_APP_URL": "https://brandbrain.dev"
      }
    }
  }
}

Then call login_brandbrain (browser OAuth) and verify with whoami_brandbrain.

Installer script

From a repo checkout, ./install.sh detects Claude Code, Claude Desktop, and Codex, registers the server with the production env, and offers to install the bundled agent skill (see Agent skill):

cd mcp/flow-orchestrator
./install.sh          # or ./install.sh --yes to accept every prompt

Smithery

A smithery.yaml is included for the Smithery registry (stdio, npx command, config schema for the env vars).

Live runs need a spend ceiling

Live (paid) runs are refused unless the server's environment sets BRANDBRAIN_MAX_PROVIDER_SPEND_USD_MICROS (USD micros; 1000000 = $1.00) — see Environment. Mock runs work without it.

Backend contract

The server wraps these backend endpoints:

  • GET /api/v1/canvas/templates
  • POST /api/v1/canvas/sessions
  • GET /api/v1/canvas/sessions/:sessionId
  • PUT /api/v1/canvas/sessions/:sessionId
  • DELETE /api/v1/canvas/sessions/:sessionId
  • PUT /api/v1/canvas/sessions/:sessionId/asset-set-plans/:planNodeId
  • POST /api/v1/canvas/sessions/:sessionId/asset-set-plans/:planNodeId/approve
  • GET /api/v1/canvas/sessions/:sessionId/asset-set-plans/:planNodeId/history
  • POST /api/v1/canvas/sessions/:sessionId/validate
  • POST /api/v1/canvas/sessions/:sessionId/runs
  • GET /api/v1/canvas/sessions/:sessionId/runs/:runId
  • GET /api/v1/canvas/sessions/:sessionId/runs/:runId/outputs
  • GET /api/v1/canvas/sessions/:sessionId/runs/:runId/artifacts/:artifactId
  • POST /api/v1/canvas/sessions/:sessionId/regenerate

In production, the backend must have access to the canonical flow bundles at BRANDBRAIN_FLOW_TEMPLATE_ROOT.

Environment

  • BRANDBRAIN_API_URL
    • Backend API base URL.
    • Default: http://127.0.0.1:4444
  • BRANDBRAIN_APP_URL
    • Optional app base URL used to generate returned canvas links.
    • Default: https://brandbrain.dev
  • BRANDBRAIN_FLOW_MCP_BEARER_TOKEN
    • Optional static bearer token.
    • Usually not needed once browser OAuth login is configured.
  • BRANDBRAIN_MAX_PROVIDER_SPEND_USD_MICROS
    • The most this server may authorise a single live run to spend, in USD micros (1000000 = $1.00). The backend caps any one run at 100000000 ($100.00) regardless.
    • There is no default and no value meaning unlimited. Leave it unset and every live run is refused before any request reaches the backend; only mock runs work.
    • This is deliberately process environment rather than a tool argument, so an agent driving this server cannot choose its own spend ceiling. Set it yourself, in your MCP client configuration, to the most you are willing to lose on one run.
    • The amount actually authorised is never this number. It is the backend's own conservative estimate for that exact run; this value only decides whether the run is allowed to proceed at all.

See .env.example.

Install and run

From a repo checkout (mcp/flow-orchestrator):

cd mcp/flow-orchestrator
npm install
npm run build
BRANDBRAIN_API_URL=http://127.0.0.1:4444 npm start

For local iteration:

cd mcp/flow-orchestrator
npm install
BRANDBRAIN_API_URL=http://127.0.0.1:4444 npm run dev

MCP client configuration

Published package (preferred — see Quickstart):

{
  "mcpServers": {
    "brandbrain-flow-orchestrator": {
      "command": "npx",
      "args": ["-y", "brandbrain-flow-orchestrator-mcp"],
      "env": {
        "BRANDBRAIN_API_URL": "https://api.brandbrain.dev",
        "BRANDBRAIN_APP_URL": "https://brandbrain.dev"
      }
    }
  }
}

Built server from a checkout:

{
  "mcpServers": {
    "brandbrain-flow-orchestrator": {
      "command": "node",
      "args": [
        "<repo-checkout>/mcp/flow-orchestrator/dist/index.js"
      ],
      "env": {
        "BRANDBRAIN_API_URL": "https://api.brandbrain.dev",
        "BRANDBRAIN_APP_URL": "https://brandbrain.dev"
      }
    }
  }
}

Agent skill

The source of truth for the agent skill that teaches agents how to drive this server safely lives in this repository at skill/ (SKILL.md + references/ + agents/openai.yaml). It also ships inside the published npm package, next to this README.

The skill encodes the intended usage pattern for agent sessions:

  • list templates first
  • create template-backed flows when possible
  • validate after edits
  • run mock before live
  • return canvas_url to the user

To install it for Codex, copy the directory into the skills root:

cp -R skill/ ~/.codex/skills/brandbrain-flow-orchestrator/

For other agents, copy skill/ into their skills directory (or point them at the packaged copy inside brandbrain-flow-orchestrator-mcp). ./install.sh offers the Codex copy automatically.

Dev server with tsx:

{
  "mcpServers": {
    "brandbrain-flow-orchestrator": {
      "command": "npx",
      "args": [
        "tsx",
        "<repo-checkout>/mcp/flow-orchestrator/src/index.ts"
      ],
      "env": {
        "BRANDBRAIN_API_URL": "http://127.0.0.1:4444",
        "BRANDBRAIN_APP_URL": "http://127.0.0.1:3000"
      }
    }
  }
}

Tools

| Tool | Purpose | Typical use | | --- | --- | --- | | login_brandbrain | Open BrandBrain browser login and store a local MCP session | First step when the backend is protected and no local agent session exists | | whoami_brandbrain | Inspect the locally stored MCP session | Confirm the active BrandBrain agent identity before editing flows | | logout_brandbrain | Revoke the stored MCP refresh session and clear local credentials | Explicitly end a local MCP login | | list_asset_flow_templates | Discover canonical starter flows | First step when the user describes a goal but not a template | | create_asset_flow | Create a stored flow session from a template, goal, or explicit flowSpec | Start a new workflow | | get_asset_flow | Fetch the stored session and flow spec | Inspect current graph before editing | | edit_asset_flow | Replace title/description and optionally replace the stored flowSpec | Structural edits after creation | | update_asset_set_plan | Atomically update asset-set authoring fields and persist a complete draft manifest proposal | Configure a named asset set before asking the user to approve it | | approve_asset_set_plan | Approve the exact persisted manifest revision and content hash | Record explicit user approval without forging manifest authority fields | | get_asset_set_plan_history | Fetch manifest revisions and approval events | Audit the persisted asset-set lifecycle | | validate_asset_flow | Run backend graph validation | After every material edit and before any run | | start_asset_flow_run | Start a background mock or live run and return a pollable task envelope | Standard MCP clients that only support ordinary tool calls | | get_asset_flow_task | Poll a background task started by the MCP server | Check status until completed or failed | | start_regenerate_asset_flow_node | Start a background regeneration request from a target node | Polling clients; asset-set local targets rerun the set-scoped pipeline from split, while legacy graphs may invoke the full runner | | run_asset_flow | Execute the session in mock or live mode | Task-backed execution for long-running runs | | regenerate_asset_flow_node | Start a task-backed regeneration request from a target node | Asset-set local reprocessing or a separately confirmed legacy regeneration whose full reachable scope has been inspected | | get_asset_flow_run | Inspect a stored run result and traces | Audit what happened after execution | | get_flow_processor_health | Read normalized local processor readiness and pinned revisions | Verify zero-token local split/background/vector processing is available before relying on it | | get_flow_outputs | Return every durable output's metadata and app URL, with a bounded inline preview | View/validate generated assets after a run (run the flow first) | | get_flow_trace | Per-node run trace: status, timing, resolved prompts, provider request highlights, artifacts, errors | Investigate why a run/node failed or what was sent to the provider | | open_flow | Open the flow's canvas (/crm/canvas/{id}) in your default browser | Jump from MCP to the visual editor | | open_flow_gallery | Open the flow's run gallery (/crm/canvas/{id}/gallery) in your default browser | Review every run's output images + per-image cost/trace after running |

Asset-set authoring and delivery

Asset sets use a persisted plan and manifest lifecycle. Do not skip or combine the two approval boundaries: the user first approves the proposed manifest, then later confirms the specific paid provider call immediately before live execution.

Use this sequence for a new or changed asset set:

get_asset_flow
-> update_asset_set_plan
-> present the persisted draft and wait for explicit user manifest approval
-> approve_asset_set_plan
-> validate_asset_flow
-> run_asset_flow(mode="mock") or start_asset_flow_run(mode="mock") + polling
-> present the paid-call scope and wait for explicit confirmation
-> run_asset_flow(mode="live") or start_asset_flow_run(mode="live") + polling
-> get_flow_outputs
-> open_flow_gallery

The three plan-lifecycle tools accept camelCase MCP inputs:

| Tool | Required inputs | | --- | --- | | update_asset_set_plan | sessionId, planNodeId, positive expectedFlowRevision, non-negative expectedManifestRevision, non-empty label, brief, and desiredPurpose, requestedCount from 1 through 64, requestedFormats exactly ["png"] or ["png", "svg"], and the complete proto-JSON manifest object | | approve_asset_set_plan | sessionId, planNodeId, positive expectedFlowRevision, positive expectedManifestRevision, and the exact non-empty manifestContentHash returned with the persisted draft | | get_asset_set_plan_history | sessionId and planNodeId |

update_asset_set_plan is one atomic write: it updates the plan authoring fields and complete manifest proposal together, recomputes the canonical manifest hash, and persists a new draft. Read the returned persisted draft, revisions, and content hash; do not calculate or invent approval fields in the client. Present the named items, layout, required formats, and spend policy to the user. Only after the user approves that exact proposal should the client call approve_asset_set_plan using the returned revisions and hash. Stale revision or hash conflicts must be surfaced to the caller, followed by a fresh get_asset_flow; do not retry with guessed values. get_asset_set_plan_history is the source of truth for revision and approval records.

The format contract is intentionally narrow:

  • Every item must request PNG as both a desired and required format. A transparent PNG is the required master.
  • SVG may be the optional second requested format. It is desired and published only after the sanitizer, complexity, and raster-fidelity gates pass.
  • Mark SVG required in the manifest only when the user explicitly requires vector delivery and accepts that a failed SVG gate leaves that item requiring review. An optional SVG failure must not erase or fail a valid PNG master.

Generic graph-editing tools cannot author or alter an asset-set plan node. This includes edit_asset_flow, apply_flow_edits, and the individual incremental edit tools: they cannot add, remove, or change plan fields, manifest contents, or approval state. Use update_asset_set_plan for authoring changes and approve_asset_set_plan for approval.

After approval, require validate_asset_flow to return valid=true, then finish a terminal mock run before considering live execution. Immediately before a provider-backed source-generation run:

  1. Inspect the persisted graph and selected execution target. Exactly one provider-backed node may be reachable by that live run; if more than one is reachable, stop instead of using regeneration as a workaround. A zero-provider asset-set local reprocessing run is a distinct recovery path described below and must be verified from its returned target and usage evidence.
  2. Tell the user the operation, provider/model when known, item or set scope, expected size/quality, and estimated or bounded cost when available. Ask for explicit confirmation of that one call.
  3. After confirmation, start one live run with a new idempotencyKey. Further provider calls need fresh confirmation.

For asset-set templates configured for local review, the approved source sheet then proceeds through the canonical local split, background removal, trim/normalization, vectorization, review, and output stages. These local post-processing stages consume no model/provider tokens and incur no provider cost; verify that claim from returned usage, trace, or cost evidence. Do not claim an unverified zero-token result. If semantic provider review is enabled, stop before an atomic live run because the current MCP surface cannot separately confirm and dispatch that additional paid stage.

Finish with get_flow_outputs and open_flow_gallery. get_flow_outputs returns the complete outputs metadata array for the selected run, including a browser-loadable artifact_url for every durable output. It inlines previews for at most 16 outputs, subject to the per-image byte cap; outputs beyond that preview limit remain present in metadata with their app URLs. Use those returned URLs for individual PNG/SVG delivery and the gallery for visual review.

Incremental editing

Edit a stored canvas without resending the whole graph (preferred over edit_asset_flow, which replaces the entire flowSpec).

Asset-set plan nodes are the exception: generic whole-flow and incremental edits cannot add, remove, or modify them. Follow the dedicated asset-set authoring and delivery lifecycle instead.

Important — the backend validates the WHOLE graph on every save. Each single-op tool below does its own PUT, so it is rejected (HTTP 400) if it leaves the graph transiently invalid. A generate node needs ≥1 input edge and ≥1 output edge, so you cannot add a bare generate node and wire it up in separate calls. To build or extend a generate chain, use apply_flow_edits, which applies an ordered op list in memory and saves once — only the final graph is validated.

| Tool | Purpose | | --- | --- | | apply_flow_edits | Batch: apply an ordered list of ops in one save. Use this to build/extend chains. | | add_flow_node | Add one node (type = prompt/reference/style/generate/output/frame/note); auto-positions and auto-ids if omitted. Safe alone only for nodes valid while unwired (prompt/style/reference/note/frame). | | update_flow_node | Patch a node's fields and/or position by nodeId | | remove_flow_node | Remove a node and any edges referencing it | | connect_flow_nodes | Add an edge between two existing nodes (prompt\|reference\|style\|output -> generate, or generate -> output) | | disconnect_flow_nodes | Remove an edge by edgeId, or by sourceNodeId+targetNodeId | | set_generate_target | Set a generate node's surface/aspectRatio/composition target |

Tool inputs are camelCase (modelId, sourceNodeId); they are emitted to the backend as the canonical snake_case proto-JSON. Each tool returns the backend validation result.

Example — build a full prompt+style → generate → output chain in one call:

apply_flow_edits {
  "sessionId": "flow_session_...",
  "ops": [
    { "op": "add_node", "type": "prompt",   "id": "bp", "text": "A bold packshot on seamless paper" },
    { "op": "add_node", "type": "style",    "id": "bs", "palette": ["#0a0a0a", "#f5f5f0"] },
    { "op": "add_node", "type": "generate", "id": "bg", "provider": "openai", "modelId": "gpt-image-1" },
    { "op": "add_node", "type": "output",   "id": "bo" },
    { "op": "connect", "sourceNodeId": "bp", "targetNodeId": "bg" },
    { "op": "connect", "sourceNodeId": "bs", "targetNodeId": "bg" },
    { "op": "connect", "sourceNodeId": "bg", "targetNodeId": "bo" },
    { "op": "set_generate_target", "nodeId": "bg", "surface": "packshot", "aspectRatio": "1:1" }
  ]
}

Task-backed run tools

run_asset_flow and regenerate_asset_flow_node are registered as MCP task tools.

  • plain client.callTool(...) is not the right API for these tools
  • task-capable clients should use client.experimental.tasks.callToolStream(...)
  • this avoids the default short request timeout problem on real image generation
  • the MCP task stays working while the backend run is queued or running; it does not complete merely because the asynchronous dispatch returned a run ID
  • backend completed and partial runs complete the MCP task; backend failed, cancelled, and outcome_unknown runs fail it while retaining the terminal run and error in the stored task result
  • a requested task TTL that is shorter than the bounded backend polling window is raised to a safe accepted TTL so the SDK task store cannot expire before terminal evidence is persisted
  • accepted TTLs are capped at 2,147,000,000ms (just below Node's signed 32-bit timer maximum), including when a client requests a larger value
  • terminal polling retries only transient read failures (network/timeouts, HTTP 429, and HTTP 5xx) with bounded exponential backoff and jitter inside one overall deadline; authentication, other HTTP 4xx, and unsupported backend statuses fail immediately
  • the default poll deadline is mode-aware: live runs poll for up to 30 minutes (a ~10-minute asset-set generation fits inside one task), while mock runs keep the short ~9.5-minute default; a constructor/configured runPollTimeoutMs always overrides the default
  • each timed-out run read aborts and settles its fetch before retrying, so retries cannot leave overlapping orphan reads
  • task-backed and standard tools share one 32-operation execution limit across dispatch and terminal polling; capacity is released when that lifecycle reaches a terminal outcome

Claude Code (and any client without task augmentation): do NOT call run_asset_flow. It errors immediately with MCP error -32601: Tool run_asset_flow requires task augmentation (taskSupport: 'required'). Use the polling pair below instead — start_asset_flow_run → poll get_asset_flow_task until status: "completed", which returns the run_id. Same for regenerate_asset_flow_nodestart_regenerate_asset_flow_node.

Example:

const stream = client.experimental.tasks.callToolStream(
  {
    name: "run_asset_flow",
    arguments: { sessionId, mode: "mock" },
  },
  CallToolResultSchema,
  { task: { ttl: 600000 } },
);

for await (const message of stream) {
  if (message.type === "result") {
    console.log(message.result.structuredContent);
  }
}

Standard polling tools

Some MCP clients do not support the experimental task streaming API and can only issue ordinary callTool(...) requests. For those clients, the server also exposes a polling pair:

  • start_asset_flow_run
  • get_asset_flow_task

and the regenerate equivalents:

  • start_regenerate_asset_flow_node
  • get_asset_flow_task

start_* returns immediately with a task payload like:

{
  "task": {
    "id": "flow_task_...",
    "kind": "run",
    "status": "working",
    "session_id": "flow_session_...",
    "mode": "mock",
    "poll_interval_ms": 1000
  }
}

Poll until the task is terminal. queued and running backend runs remain working; completed and partial map to completed, while failed, cancelled, and outcome_unknown map to failed. The terminal polling envelope contains the refreshed backend run, including its result/error evidence. Dispatch happens once; polling only reads the pinned session/run identity and does not repeat provider work. Transient reads use bounded backoff; non-transient errors and unknown statuses fail immediately.

Each start_* call creates a distinct dispatch attempt, including keyless calls and repeated calls with the same explicit idempotency key. This ensures the backend can re-evaluate the latest flow revision and lineage; canonical idempotency/replay decisions remain backend-owned. If distinct dispatches return the same immutable session/run ID, the MCP server shares only their terminal GET FlowRun promise to avoid redundant reads. Each caller independently builds its task result from that run and its own dispatch envelope, so prepared authority is never borrowed from another caller.

Local processor health

Call get_flow_processor_health before promising no-token local asset processing. It is a read-only, allowlisted view of /health/processors that reports enabled, available, platform, execution_class, provider_token_cost, revisions.vtracer, revisions.resvg, and any health error. Its capabilities object reports split, trim_normalize, vectorize, local_review, and the background-removal fields deterministic_matte_or_alpha, local_model_fallback, and production_ready. Background removal is production-ready only when the deterministic path is present and the configured local BiRefNet fallback passes its provenance-aware health probe. A runtime without BiRefNet can still preserve valid alpha or remove a known matte, but callers must not promise general-purpose background removal when production_ready is false. Missing capability fields from older backends default to false. Unknown backend fields are dropped so credentials and transport details cannot be exposed. An unavailable optional processor is returned as health evidence even when the backend health route uses HTTP 503. These checks are local and have no provider-token cost.

Then poll:

const started = await client.callTool({
  name: "start_asset_flow_run",
  arguments: { sessionId, mode: "mock" },
});

const taskId = (started.structuredContent as { task?: { id?: string } }).task?.id;

for (;;) {
  const polled = await client.callTool({
    name: "get_asset_flow_task",
    arguments: { taskId },
  });
  const payload = polled.structuredContent as {
    task?: { status?: string };
    run?: { id?: string };
  };
  if (payload.task?.status === "completed" || payload.task?.status === "failed") {
    console.log(payload);
    break;
  }
  await new Promise((resolve) => setTimeout(resolve, 1000));
}

Notes:

  • get_asset_flow_task returns the final run payload once the background task completes
  • after completion, use get_asset_flow_run if you want the persisted run again later
  • the polling task registry is in-memory to the MCP server process, so task IDs are not durable across MCP restarts
  • the shared execution coordinator rejects new work before backend dispatch when its 32-operation capacity is full, regardless of whether work came from a standard or task-backed tool
  • capacity is released after dispatch and terminal-poll failures as well as normal completion; a rejected shared poll is removed before the next attempt, so a retry performs a fresh backend read
  • the standard registry retains at most 128 tasks, evicts the oldest terminal entries as needed, and expires terminal entries after 10 minutes
  • prepared POST requests still carry the expected flow revision and resolved-policy hash, but those values are not exposed as fenced durable identity because the persisted FlowRun does not echo them. Retained dispatch identity contains only matchable fields: session, execution mode, actual target, generated or supplied idempotency key, approved manifest hash, and execution lineage. Durable failed-run evidence is accepted only from the typed error around the actual run or regenerate POST and only when every retained field matches; get-session/prepare/authentication failures never carry run evidence, and legacy fields that were not sent remain absent
  • retained failure diagnostics follow the real FlowRun ProtoJSON shape and are explicitly allowlisted and bounded, with a hard 64 KiB serialized budget across the result diagnostic envelope. Later entries are deterministically dropped, and diagnostics_truncated is set whenever a count limit, per-string bound, or global byte budget clips retained diagnostics. Safe trace/checkpoint/processor/item/artifact identifiers, statuses, issue codes, dimensions, sizes, hashes, MIME types, and durations may be retained; prompts, request/response bodies, provider usage, evidence JSON, credentials, filesystem paths, URLs, provenance, secrets, and unknown fields are discarded

Browser OAuth login

The preferred auth path is browser-based Google OAuth through BrandBrain:

  1. call login_brandbrain
  2. the MCP server opens /login/agent on the BrandBrain app with a localhost agent_callback
  3. BrandBrain redirects to the backend Google OAuth start endpoint
  4. after Google authentication, the backend redirects back to the local MCP callback with an OTC
  5. the MCP server exchanges the OTC for agent tokens and stores them locally

Use whoami_brandbrain to confirm the current session and logout_brandbrain to revoke it.

If a flow tool returns Not authenticated. Run login_brandbrain first., do that before retrying.

Recommended workflow

1. Log in when needed

  1. login_brandbrain
  2. whoami_brandbrain

2. Start from templates unless you have a reason not to

Preferred path:

  1. list_asset_flow_templates
  2. create_asset_flow with:
    • title
    • goal if you want the tool to infer a template
    • templateId if you know the exact starter
  3. read back canvas_url

This keeps the first graph aligned with the checked-in canonical bundles.

goal/description do NOT propagate into the template's prompt nodes. A template-backed create seeds generic prompt text (e.g. campaign-visual → "Design a hero campaign visual…"). If you run as-is you get a generic hero image, not your subject. After creating, patch the brief with a single update_flow_node { nodeId: "prompt_brief", text: "<your real subject>" } (stays valid — no apply_flow_edits needed). The starter campaign-visual graph wires prompt_brief → "Context:" and prompt_guardrails → "Brief:" in the assembled provider prompt, and injects the template's style palette/theme — neutralize style_primary too if the template look fights your subject.

3. Validate structure before spending tokens

Always call validate_asset_flow before run_asset_flow.

Use mock mode first and poll or stream it to a terminal completed result. Inspect the persisted mock run before continuing. It verifies:

  • graph validity
  • runnable generates
  • downstream output materialization
  • stage/regeneration semantics
  • stored session integrity

4. Edit only when template inference is not enough

Use edit_asset_flow with a replacement flowSpec when:

  • the user needs non-template structure
  • you need to add or remove nodes explicitly
  • you want to rewrite edges or stages

If you only need a different starting point, create a new flow from a better template instead of over-editing the old one.

5. Confirm each live provider call

Before any provider-backed live run:

  1. Require the current graph to pass validation and a completed mock run.
  2. Inspect the persisted graph and selected execution target. Exactly one provider-backed node may be reachable; stop if more than one is reachable because the current runner cannot isolate those calls. If none is reachable, treat the operation as local processing rather than a paid call and verify that from the returned target and usage evidence.
  3. Disclose the operation, provider/model when known, item or set scope, expected size/quality, estimated or bounded cost when available, and whether a preserved source can avoid a new call.
  4. Ask for fresh, explicit confirmation immediately before dispatching that one provider call.

A live run additionally requires BRANDBRAIN_MAX_PROVIDER_SPEND_USD_MICROS to be configured on this server. Without it the run is refused locally and nothing is dispatched. That ceiling is the user's, not the agent's: it is not a tool argument and cannot be raised from a tool call.

The user's earlier desire for real outputs, manifest approval, or the presence of provider credentials is not authorization to spend. Confirmation is call-specific: every additional source generation, provider-backed review, retry, or regeneration needs its own immediate confirmation.

6. Pick the execution path that matches the client

  • use run_asset_flow and regenerate_asset_flow_node when the client supports MCP task streaming
  • use start_asset_flow_run or start_regenerate_asset_flow_node plus get_asset_flow_task when the client only supports ordinary tool calls

7. Regenerate without assuming provider isolation

For an asset-set session, regenerate_asset_flow_node or start_regenerate_asset_flow_node may target a local process node such as split, remove-background, trim-normalize, vectorize, or review. The backend anchors this operation at split and reruns the full set-scoped local pipeline from the latest eligible persisted raw sheet. It does not expose per-item processing or a caller-selected source run/artifact. After the run starts, inspect get_asset_flow_run and require the returned target to be local-only and downstream-only, with its base run/attempt and input checkpoint/artifact lineage, before claiming that it preserved the source and incurred no provider cost.

On legacy/non-asset-set graphs, regeneration records targetNodeId but may still invoke the full runner. It does not guarantee a single-node or single-output provider retry. Inspect the complete reachable provider scope; if any provider-backed work can run, disclose that scope/cost and obtain fresh explicit confirmation immediately before regeneration. Stop when multiple provider-backed nodes are reachable.

Example operator flows

Create a new workflow from a goal

Input:

{
  "title": "Spring launch system",
  "goal": "Build a campaign system with a hero and story adaptation"
}

Tool:

create_asset_flow

Follow-up:

  1. validate_asset_flow
  2. start_asset_flow_run with mode=mock
  3. poll get_asset_flow_task until completed
  4. inspect canvas_url

Task-stream alternative:

  1. validate_asset_flow
  2. run_asset_flow with mode=mock
  3. inspect canvas_url

Edit and rerun an existing session

  1. get_asset_flow
  2. modify the returned flow_spec
  3. edit_asset_flow
  4. validate_asset_flow
  5. start_asset_flow_run
  6. poll get_asset_flow_task

Restart asset-set local processing from a preserved sheet

{
  "sessionId": "flow_session_...",
  "targetNodeId": "split",
  "mode": "live"
}

Tool:

regenerate_asset_flow_node

For a standard client, use start_regenerate_asset_flow_node with the same arguments, poll get_asset_flow_task, then inspect get_asset_flow_run. Do not use this example for a provider retry: the zero-provider claim depends on the returned asset-set target being set-scoped, local-only, downstream-only, and anchored to the persisted raw-sheet lineage.

Validation and test commands

cd mcp/flow-orchestrator
npm test
npm run test:integration
npm run build

Troubleshooting

No templates available

The backend cannot see the canonical flow bundle root. Check:

  • BRANDBRAIN_FLOW_TEMPLATE_ROOT
  • deployed bundle files under /opt/brandbrain/evals/flows in production

400 validation on create or edit

The backend rejected the provided flowSpec. Call validate_asset_flow or inspect the returned validation payload.

401 or 403

Set BRANDBRAIN_FLOW_MCP_BEARER_TOKEN if the backend APIs are protected.

live runs fail

The MCP server is not the model execution layer. Check provider credentials and backend runtime config, not the MCP package. Credentials are only an execution prerequisite; they never replace the fresh user confirmation required immediately before each provider call.

login_brandbrain times out

login_brandbrain blocks until you finish the browser round-trip. MCP clients enforce a request timeout (the SDK default is 60s), which can abort the call before you sign in. Options:

  • complete the Google sign-in promptly, or
  • raise your client's MCP request timeout (e.g. Claude Code honours MCP_TIMEOUT), or
  • run the standalone login (15-minute window, no MCP layer), which writes the same session file: node scripts/login.mjs (with BRANDBRAIN_API_URL/BRANDBRAIN_APP_URL set).

Local verification scripts

scripts/ contains a stdio client for exercising the server end to end without a full MCP client:

node scripts/login.mjs                        # browser OAuth, 15-min window, writes the session file
node scripts/smoke.mjs list                   # list registered tool names
node scripts/smoke.mjs whoami                  # check the stored session
node scripts/smoke.mjs templates               # list flow templates
node scripts/smoke.mjs create '{"title":"X","templateId":"campaign-system"}'
node scripts/smoke.mjs call '{"name":"apply_flow_edits","arguments":{ ... }}'
node scripts/smoke.mjs runflow '{"sessionId":"flow_session_...","mode":"mock"}'

All read BRANDBRAIN_API_URL / BRANDBRAIN_APP_URL from the environment.