webflow-mcp
v0.4.0
Published
WebFlow MCP MVP with local flow workspaces and MCP tool exposure
Downloads
603
Readme
WebFlow MCP
Local worker and unified MCP gateway for the WebFlowMCP signed remote automation catalog. A legacy
local-authoring mode can still discover .webflow/flows/* and expose each valid flow directly.
Requirements
- Node 20+
- npm 10+
SaaS quickstart
npm install -g webflow-mcpPair this computer once. The CLI creates both scoped credentials locally: the worker credential stays in the existing owner-only file and the MCP token is stored in the operating system credential manager.
webflow login --app https://app.webflowmcp.es
webflow login --statusThen register the local stdio gateway in your user-global MCP client configuration. For OpenCode:
{
"mcp": {
"webflow": {
"type": "local",
"command": ["webflow-mcp"]
}
}
}No literal token is needed in supported local stdio clients. Advanced or ephemeral environments may still
set both WEBFLOW_RELAY_MCP_URL and WEBFLOW_RELAY_MCP_TOKEN; explicit environment values take priority.
To use a separately paired staging endpoint, set only WEBFLOW_RELAY_MCP_URL and the gateway reads that
endpoint's token from the OS credential manager. The worker never inherits the MCP token. Start and stop it
through start_worker/stop_worker.
Gateway mode exposes a stable catalog surface rather than one dynamic tool per saved flow. Discover with
browse_catalog/search_flows, inspect with describe_flow, and execute saved flows with
run_flow_with_options. The machine-local repertoire is stored in ~/.webflow/repertoire.json; saving a
flow does not create a dynamic MCP tool.
Normal synchronous calls are the primary execution path: they wait and return the final result, including for flagship flows certified for up to 30 minutes. The relay hard job budget is 31 minutes and the local gateway relay-call budget is 32 minutes. The timeout imposed by the external MCP host/caller is outside WebFlowMCP; configure that host for at least 35 minutes where it exposes such a setting. WebFlowMCP sends immediate progress plus a bounded heartbeat while synchronous work is running.
Use start_flow_run only as a fallback for MCP hosts with incompatible caller limits or for disconnect
recovery. It returns an exe_... run ID after worker-local durable acceptance without waiting for completion.
Use list_recent_flow_runs to recover an ID after a lost response. Production/staging ownership survives relay restarts through Postgres; standalone mode keeps ownership only for the relay process lifetime.
Use get_flow_run_status, get_flow_run_result, or cancel_flow_run afterward. Full results are retained
only in the expiring worker-local spool (WEBFLOW_RUNS_DIR or ~/.webflow/runs), never in relay/Postgres.
CLI
webflow init
webflow create flow checkout
webflow fork checkout checkout-copyThe workspace contract is:
.webflow/
flows/
_template/
<flow>/
schema.json
index.ts
skills/Provider profiles are machine-global under ~/.webflow/profiles/; they are not part of a workspace.
Authoring a flow
For development from this repository:
npm install
npm run buildEach flow must export run(ctx, input) from index.ts and define a supported JSON-schema subset in schema.json:
export async function run(ctx, input) {
return { flow: ctx.flowName, echoed: input.message };
}Flows with their own input.timeoutMs may declare signed capabilities.executionTimeoutMs. It must be an
integer from 1 second through 30 minutes, and runtime rejects input values above that certified bound.
Supported schema keys: type, properties, required, items, enum, description, default.
Legacy local-authoring MCP
The projectRoot argument is the absolute path to the repo you are working in — it decides where downloads land. The flows themselves do not need to live there: if projectRoot has no .webflow/flows, the server automatically uses the flows shipped with this package (Model B). So a single flows repo serves many working repos, and each repo keeps its own outputs.
During development from this repo:
node --import tsx src/mcp/cli.ts /absolute/path/to/your-working-repoOr after building:
npm run build
node dist/src/mcp/cli.js /absolute/path/to/your-working-repoFor MCP client configuration from another repo, point the entrypoint at this package and the argument at that repo. Flows come from here; downloads stay in that repo:
{
"mcpServers": {
"webflow": {
"command": "node",
"args": [
"C:/absolute/path/to/webflow-mcp/dist/src/mcp/cli.js",
"C:/absolute/path/to/your-other-repo"
]
}
}
}To override where flows are loaded from, set WEBFLOW_FLOWS_DIR to a workspace that has .webflow/flows. The server falls back to CLAUDE_PROJECT_DIR then cwd when no argument is given, and prints which flows root, session store, and downloads dir it resolved as startup diagnostics.
On Windows, run-mcp.bat is only a local helper. It requires npm run build first and expects the workspace root as its first argument:
run-mcp.bat C:\absolute\path\to\workspaceDoctor
Use doctor before wiring MCP from another repo:
webflow doctor /absolute/path/to/workspace
node dist/src/cli/index.js doctor /absolute/path/to/workspacedoctor checks Node, the workspace layout, exposed flows, parseable schemas, build output, Playwright/Chromium availability, and warns about fragile flow patterns such as fixed CDP port 9222, taskkill, Profile 1, process.cwd(), hardcoded Windows paths, or connectOverCDP.
Sessions vs downloads
Two things are deliberately decoupled:
- Login sessions are global. Chrome profiles live in one shared store, default
~/.webflow/profiles(override withWEBFLOW_PROFILES_DIR). Log in once and every workspace reuses the session — you do not re-authenticate per repo. - Downloads are per-repo. Each flow writes to
<projectRoot>/.webflow/downloads/<flow>/..., so outputs are organized by the workspace you ran from. Point the MCP server'sprojectRootat a repo and that repo collects its own downloads while sharing the global login.
The MCP server prints both locations as diagnostics on startup.
Browser Flow Setup
Run webflow setup <flow> once per profile to log in. By default this opens a plain real Chrome window with no automation flags, pointed at the flow's dedicated global profile. Log in, accept any prompts, then close the window — the session persists and is reused by automated runs. This avoids the "unsafe browser" block that strict providers raise against automation-controlled login windows.
webflow setup chatgpt-prompt
webflow setup google-speech-generate --url https://aistudio.google.comFlows share a profile by declaring the same capabilities.profile (for example chatgpt-prompt and chatgpt-image-generate both use chatgpt), so one login covers the whole provider.
Browser modes
A flow declares how it drives the browser via capabilities.browser:
true/'managed'— Playwright-managed persistent profile (default). Clean, isolated, works headless from any repo. Used by ChatGPT, Gemini app, X, and the scrapers.'cdp'— the runner attaches to a real Chrome over CDP. Used by strict providers (google-speech-generatefor Google AI Studio,adobe-lipsync). The runner launches Chrome against the dedicated profile on a free port, connects, and closes that window when the flow finishes (it owns what it launched). If you instead attach to a Chrome you already had open viacdpUrl, it only disconnects and leaves your window alone. Optional per-run overrides:cdpUrl,chromePath,userDataDir.
Flows consume ctx.page/ctx.browser and are mode-agnostic, so switching a flow between managed and cdp needs no code changes.
Verifying whether a strict provider can use the managed profile
'cdp' is the proven path for AI Studio/Adobe; the managed profile is unverified there. To test whether managed + one-time login is enough (and unify on a single mechanism), run a cdp flow once with:
# PowerShell: $env:WEBFLOW_CDP_AS_MANAGED = '1'
WEBFLOW_CDP_AS_MANAGED=1 webflow run google-speech-generate --headed --input '{ ... }'If the provider accepts the login, switch that flow's capabilities.browser to 'managed'. If it rejects, leave it on 'cdp'.
Running multiple agents in parallel
A Chrome user-data-dir can be opened by only one process at a time, so parallel agents cannot share one profile directory. Set WEBFLOW_PROFILE_INSTANCE=<id> per agent: the runner clones the logged-in base profile into a per-instance directory on first use. Off by default (a single agent reuses the shared base profile). Note that some providers may then ask the extra session to re-verify as a new device.
Signed remote flows
Official relays send immutable Ed25519-signed flow releases. The worker verifies the relay-bound public
key, manifest and exact code/schema/capability hashes before importing any code. Every new flow digest
also requires an explicit local decision. Through the unified MCP, call list_flow_approvals, inspect the
full digest/signer/trust domain, ask the user, and then call approve_flow_update with the exact returned
identity and confirmed:true. The interactive CLI remains available:
webflow approvals list
webflow approvals approve <flow> --trust-domain <domain>
webflow approvals revoke <flow> --trust-domain <domain>The remote relay cannot approve a release; MCP approval tools mutate only worker-local state. Their
confirmed:true argument is a user-interaction convention, while typing APPROVE in the CLI remains the
stronger manual path. Flow source is not installed as a local catalog, but code
executed on a user-controlled computer cannot be made impossible for that computer's owner to capture.
Signed code still has the worker process's OS permissions; this release does not add a sandbox.
Signing keys and release commands are operator-only; end users never generate or manage signing keys.
Verification
npm test
npm run smoke
npm pack --dry-run --json