flutterflow-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@flutterflow-mcplist my FlutterFlow projects"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
flutterflow-mcp
flutterflow-mcp is an original FlutterFlow MCP server built around a command-palette model.
What Orbit Is
Orbit is different from multi-tool MCP servers by design:
One primary tool:
orbitCommand palette verbs inside
cmd(help,snapshots.create,changeset.apply, etc.)SQLite snapshot cache with persisted indices/graphs
Policy engine (
orbit.policy.json+ env overrides)Transactional ChangeSet flow:
new -> add -> preview -> validate -> apply
Related MCP server: community-ff-mcp
Features
Node.js 18+ TypeScript strict implementation
MCP stdio transport for Claude Desktop/Cursor/Windsurf/Claude Code/Codex
Separate HTTP health/status/policy service on port
8080FlutterFlow API adapter (set
FLUTTERFLOW_API_TOKENfor remote reads/writes)SQLite snapshot store (files, hashes, symbols, edges)
Navigation + component usage graph index
Safe editing policy engine with manual-approval mode
Orbit schema pack resources:
orbit://schema/indexorbit://schema/doc/{id}orbit://schema/snippet/{id}
Quick Start
1) Install
npm i2) Configure env (optional for local startup)
export FLUTTERFLOW_API_TOKEN=your_token_hereWithout a token, the MCP server still starts, but commands that call FlutterFlow APIs (for example projects.list, snapshots.create, snapshots.refresh) will return a missing-token error until you set it.
Optional API path overrides:
export FLUTTERFLOW_API_BASE_URL=https://api.flutterflow.io/v2
export FLUTTERFLOW_API_LIST_PROJECTS_PATH=/l/listProjects
export FLUTTERFLOW_API_LIST_PARTITIONED_FILES_PATH=/listPartitionedFileNames
export FLUTTERFLOW_API_PROJECT_YAMLS_PATH=/projectYamls
export FLUTTERFLOW_API_VALIDATE_PROJECT_YAML_PATH=/validateProjectYaml
export FLUTTERFLOW_API_UPDATE_PROJECT_YAML_PATH=/updateProjectByYaml
export FLUTTERFLOW_API_MIN_INTERVAL_MS=10003) Run
npm startThis starts:
MCP server over stdio
HTTP server on
http://localhost:8080
Endpoints:
GET /healthGET /statusGET /policy
If the HTTP port is already in use, Orbit now continues with MCP stdio and logs a warning to stderr. You can disable HTTP endpoints entirely with:
export ORBIT_HTTP_ENABLED=0MCP Client Setup
Use npm start as your MCP command.
Claude Desktop / Cursor / Windsurf
Use templates/mcp-config.local.json as a base:
{
"mcpServers": {
"ff_orbit_mcp": {
"command": "npm",
"args": ["start"],
"cwd": "/absolute/path/to/ff-mcp",
"env": {
"FLUTTERFLOW_API_TOKEN": "YOUR_TOKEN"
}
}
}
}Claude Code
Use project config file .mcp.json (you can copy templates/claude-code.mcp.json):
{
"mcpServers": {
"ff_orbit_mcp": {
"command": "npm",
"args": ["start"],
"cwd": "/absolute/path/to/ff-mcp",
"env": {
"FLUTTERFLOW_API_TOKEN": "YOUR_TOKEN"
}
}
}
}Or add it with CLI:
claude mcp add -s project -e FLUTTERFLOW_API_TOKEN=YOUR_TOKEN ff_orbit_mcp -- npm startCodex (CLI / App)
Option A: add via CLI:
codex mcp add ff_orbit_mcp --env FLUTTERFLOW_API_TOKEN=YOUR_TOKEN -- node /absolute/path/to/ff-mcp/dist/main.jsBuild once before using the command above:
npm run buildOption B: add to ~/.codex/config.toml (see templates/codex-config.toml):
[mcp_servers.ff_orbit_mcp]
command = "npm"
args = ["start"]
cwd = "/absolute/path/to/ff-mcp"
env = { FLUTTERFLOW_API_TOKEN = "YOUR_TOKEN" }
enabled = trueAgent Rule Pack
This repository now includes a clone-ready rule/setup pack for agents:
Rules:
.cursor/rules/Playbook:
docs/agent-playbook.mdMCP config templates:
templates/mcp-config.local.json,templates/mcp-config-remote.json,templates/claude-code.mcp.json,templates/codex-config.tomlInstaller script:
scripts/install-cursor-rules.sh
Install rules into another repo:
./scripts/install-cursor-rules.sh /absolute/path/to/target-projectOrbit Command Palette
Primary tool signature:
orbit({
cmd: string,
args?: object,
snapshot?: string,
format?: "json" | "explain"
})Discover commands:
orbit({ cmd: "help" })Core groups:
Discovery:
projects.list,snapshots.create,snapshots.refresh,snapshots.refreshSlow,snapshots.ensureFresh,snapshots.info,snapshots.lsAPI capabilities:
api.capabilitiesQuery:
search,page.create,page.scaffold,page.get,page.update,page.preflightDelete,page.remove(recommended),page.delete(low-level),page.clone,component.get,component.extractFromWidget,component.instance.insert,tree.locate,tree.subtree,tree.find(alias),tree.validate,tree.repair,graph.nav,graph.usage,pages.list,textfields.list,widget.get,widget.getMany,widgets.list,widgets.find,widgets.findText,widgets.updateMany,widgets.copyPaste,widget.create,widget.insert,widget.wrap,widget.unwrap,widget.duplicate,widget.deleteSubtree,widget.replaceType,widget.removeChildren,widget.move,widget.moveMany,widget.reorder,widget.action.list,widget.action.get,widget.bindAction,widget.bindData,widget.set,widget.delete,selection.get,selection.clear,intent.run,routes.list,routes.listByPage,routes.validate,routes.upsert,routes.delete,settings.getSummaries:
summarize.page,summarize.component,summarize.projectEditing:
changeset.new,changeset.add,changeset.preview,changeset.validate,changeset.apply,changeset.applySafe,changeset.rollback,changeset.revert,changeset.dropSchema:
schema.search,schema.read,schema.snippet
High-ROI recipes
Generate best-practice pages with deterministic recipes (preview-first):
orbit({ cmd:"page.scaffold", args:{ name:"login2", recipe:"auth.login", preview:true } })orbit({ cmd:"page.scaffold", args:{ name:"preferences", recipe:"settings.basic", params:{ toggles:["Notifications","Dark mode"] }, apply:true } })orbit({ cmd:"intent.run", args:{ text:"create a list page called products with search", preview:true } })
Duplicate a widget twice:
orbit({ cmd:"widget.duplicate", args:{ nameOrId:"login", nodeId:"id-Text_a", count:2, apply:true } })
Insert a widget in one call:
orbit({ cmd:"widget.insert", args:{ nameOrId:"login", type:"Divider", beforeNodeId:"id-Button_b", apply:true } })
Batch-update text widgets:
orbit({ cmd:"widgets.updateMany", args:{ nameOrId:"login", filter:{ type:"Text" }, set:{ text:"James NC" }, apply:true } })
Find widgets with one canonical search command:
orbit({ cmd:"widgets.find", args:{ nameOrId:"login", type:"TextField", textContains:"Password" } })
Batch get exact widgets:
orbit({ cmd:"widget.getMany", args:{ nameOrId:"login", nodeIds:["id-Text_a","id-Button_b"] } })
Route a button to another page:
orbit({ cmd:"routes.upsert", args:{ nameOrId:"login", nodeId:"id-Button_b", toPageNameOrId:"DailyDashboard", apply:true } })
Inspect and validate page routes:
orbit({ cmd:"routes.listByPage", args:{ nameOrId:"login", direction:"both" } })orbit({ cmd:"routes.validate", args:{ nameOrId:"login", strict:true } })
Batch move and unwrap widgets:
orbit({ cmd:"widget.moveMany", args:{ nameOrId:"login", nodeIds:["id-Text_b","id-Text_c"], afterNodeId:"id-Button_b", apply:true } })orbit({ cmd:"widget.unwrap", args:{ nameOrId:"login", nodeId:"id-Row_wrap", apply:true } })
List/get widget trigger actions:
orbit({ cmd:"widget.action.list", args:{ nameOrId:"login", nodeId:"id-Button_b" } })orbit({ cmd:"widget.action.get", args:{ nameOrId:"login", nodeId:"id-Button_b", trigger:"ON_TAP" } })
Ensure snapshot freshness before read/write:
orbit({ cmd:"snapshots.ensureFresh", args:{ staleMinutes:30 } })
Use sticky selection in conversational follow-ups:
orbit({ cmd:"selection.get" })orbit({ cmd:"intent.run", args:{ text:"unwrap this", apply:true } })
Safe apply orchestration:
orbit({ cmd:"changeset.applySafe", args:{ changesetId:"chg_x", confirm:true } })
Rollback latest applied changeset:
orbit({ cmd:"changeset.rollback", args:{ latestApplied:true, confirm:true, apply:true } })
Safer page delete flow:
orbit({ cmd:"page.preflightDelete", args:{ nameOrId:"login2" } })orbit({ cmd:"page.remove", args:{ nameOrId:"login2", apply:true } })page.removeattempts hard delete first, and can fall back to archive mode if FlutterFlow rejects delete payloads.
Validate/repair tree integrity:
orbit({ cmd:"tree.validate", args:{ nameOrId:"login" } })orbit({ cmd:"tree.repair", args:{ nameOrId:"login", fixOrphans:true, fixMissingNodes:true, apply:true } })
Optional support tools:
orbit_policy_getorbit_policy_set(disabled unlessORBIT_ALLOW_POLICY_WRITE=1)orbit_export_changeset
Snapshot Workflow
orbit({cmd:"projects.list"})orbit({cmd:"snapshots.create", args:{projectId:"..."}})Query against
snapshotidRun
snapshots.refreshbefore sensitive changes to avoid stale decisions (now throttled by default to reduce 429s)
Orbit snapshots are point-in-time and may be stale.
When FlutterFlow returns versionInfo (partitionerVersion, projectSchemaFingerprint), Orbit stores it and uses it for smarter incremental refresh decisions.
If FlutterFlow is rate-limiting refresh (429), prefer budgeted crawl mode:
orbit({ cmd:"snapshots.refreshSlow", args:{ passes:4, pauseMs:15000, maxFetch:25, concurrency:1, sleepMs:250 } })orbit({ cmd:"snapshots.refresh", args:{ mode:"full", fetchStrategy:"bulk" } })for low-request full sync attemptsorbit({ cmd:"snapshots.refresh", args:{ mode:"full", fetchStrategy:"file", maxFetch:5, concurrency:1, sleepMs:1000 } })for chunked full sync without relisting each pass (chunkedFulldefaults to true for full+file)
Safe Editing Workflow
changeset.newchangeset.add(one or many patches)changeset.preview(diff + risk)changeset.validate(YAML + structure + policy)changeset.applywithconfirm:true(includes remotevalidateProjectYamlchecks before push)
If policy requires manual approval, apply is refused and a manual payload is returned.
Policy Engine
Default file: orbit.policy.json
Fields:
allowProjectsallowFileKeyPrefixesdenyFileKeyPrefixesmaxFilesPerApplymaxLinesChangedrequireManualApprovalallowPlatformConfigEditssafeMode:readOnly | guidedWrite | fullWrite
Env overrides:
ORBIT_POLICY_ALLOW_PROJECTSORBIT_POLICY_ALLOW_FILE_PREFIXESORBIT_POLICY_DENY_FILE_PREFIXESORBIT_POLICY_MAX_FILES_PER_APPLYORBIT_POLICY_MAX_LINES_CHANGEDORBIT_POLICY_REQUIRE_MANUAL_APPROVALORBIT_POLICY_ALLOW_PLATFORM_CONFIG_EDITSORBIT_POLICY_SAFE_MODEORBIT_ALLOW_POLICY_WRITE=1(to enableorbit_policy_set)
Fly.io Deployment
flyctl launch
flyctl secrets set FLUTTERFLOW_API_TOKEN=your_token_here
flyctl deployfly.toml is configured with auto-suspend:
auto_start_machines = trueauto_stop_machines = "suspend"min_machines_running = 0
Development
npm run build
npm test
npm startSecurity Notes
Keep
FLUTTERFLOW_API_TOKENonly in environment variables or secret managers.Snapshot DB can include full project YAML and should be treated as sensitive data.
orbit.policy.jsonshould be code-reviewed because it controls write boundaries.Built-in read-only guards deny direct apply for custom code and unlocked
lib/main.dart.
FlutterFlow Project APIs Coverage
Orbit supports all currently documented FlutterFlow Project APIs (v2) through the adapter layer:
POST/GET /l/listProjectsGET /listPartitionedFileNamesGET /projectYamls(includingprojectYamlBytesbase64 zip decoding)POST /validateProjectYamlPOST /updateProjectByYaml
This server cannot be deployed
Maintenance
Related MCP Connectors
- SupabaseOAuthcom.supabase
MCP server for interacting with the Supabase platform
A Model Context Protocol (MCP) server for Selise Blocks Cloud integration
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseCqualityAmaintenanceA Model Context Protocol server that connects Flutter apps with AI coding assistants like Cursor, Claude, and Cline, enabling AI-powered analysis of widget trees, navigation, and layout issues.62377MIT
- AlicenseAqualityDmaintenanceEnables AI-assisted FlutterFlow development by reading, exploring, and understanding FlutterFlow projects through Claude and other MCP-compatible clients.25123 npm9MIT
- AlicenseAqualityDmaintenanceAn MCP server that wraps Flutter CLI tools into structured, agent-friendly tools for AI coding agents, with features like two-phase test results, output capping, and managed app lifecycle.1511 npm4MIT
- FlicenseBqualityCmaintenanceMCP server that fetches Figma data (file trees, nodes, images, components, styles, variables) and serves as a foundation for Flutter code generation.13-