Skip to main content
Glama
jamesnocode

flutterflow-mcp

by jamesnocode

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: orbit

  • Command 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

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 8080

  • FlutterFlow API adapter (set FLUTTERFLOW_API_TOKEN for 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/index

    • orbit://schema/doc/{id}

    • orbit://schema/snippet/{id}

Quick Start

1) Install

npm i

2) Configure env (optional for local startup)

export FLUTTERFLOW_API_TOKEN=your_token_here

Without 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=1000

3) Run

npm start

This starts:

  • MCP server over stdio

  • HTTP server on http://localhost:8080

Endpoints:

  • GET /health

  • GET /status

  • GET /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=0

MCP 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 start

Codex (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.js

Build once before using the command above:

npm run build

Option 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 = true

Agent Rule Pack

This repository now includes a clone-ready rule/setup pack for agents:

  • Rules: .cursor/rules/

  • Playbook: docs/agent-playbook.md

  • MCP config templates: templates/mcp-config.local.json, templates/mcp-config-remote.json, templates/claude-code.mcp.json, templates/codex-config.toml

  • Installer script: scripts/install-cursor-rules.sh

Install rules into another repo:

./scripts/install-cursor-rules.sh /absolute/path/to/target-project

Orbit 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.ls

  • API capabilities: api.capabilities

  • Query: 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.get

  • Summaries: summarize.page, summarize.component, summarize.project

  • Editing: changeset.new, changeset.add, changeset.preview, changeset.validate, changeset.apply, changeset.applySafe, changeset.rollback, changeset.revert, changeset.drop

  • Schema: 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.remove attempts 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_get

  • orbit_policy_set (disabled unless ORBIT_ALLOW_POLICY_WRITE=1)

  • orbit_export_changeset

Snapshot Workflow

  1. orbit({cmd:"projects.list"})

  2. orbit({cmd:"snapshots.create", args:{projectId:"..."}})

  3. Query against snapshot id

  4. Run snapshots.refresh before 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 attempts

  • orbit({ cmd:"snapshots.refresh", args:{ mode:"full", fetchStrategy:"file", maxFetch:5, concurrency:1, sleepMs:1000 } }) for chunked full sync without relisting each pass (chunkedFull defaults to true for full+file)

Safe Editing Workflow

  1. changeset.new

  2. changeset.add (one or many patches)

  3. changeset.preview (diff + risk)

  4. changeset.validate (YAML + structure + policy)

  5. changeset.apply with confirm:true (includes remote validateProjectYaml checks before push)

If policy requires manual approval, apply is refused and a manual payload is returned.

Policy Engine

Default file: orbit.policy.json

Fields:

  • allowProjects

  • allowFileKeyPrefixes

  • denyFileKeyPrefixes

  • maxFilesPerApply

  • maxLinesChanged

  • requireManualApproval

  • allowPlatformConfigEdits

  • safeMode: readOnly | guidedWrite | fullWrite

Env overrides:

  • ORBIT_POLICY_ALLOW_PROJECTS

  • ORBIT_POLICY_ALLOW_FILE_PREFIXES

  • ORBIT_POLICY_DENY_FILE_PREFIXES

  • ORBIT_POLICY_MAX_FILES_PER_APPLY

  • ORBIT_POLICY_MAX_LINES_CHANGED

  • ORBIT_POLICY_REQUIRE_MANUAL_APPROVAL

  • ORBIT_POLICY_ALLOW_PLATFORM_CONFIG_EDITS

  • ORBIT_POLICY_SAFE_MODE

  • ORBIT_ALLOW_POLICY_WRITE=1 (to enable orbit_policy_set)

Fly.io Deployment

flyctl launch
flyctl secrets set FLUTTERFLOW_API_TOKEN=your_token_here
flyctl deploy

fly.toml is configured with auto-suspend:

  • auto_start_machines = true

  • auto_stop_machines = "suspend"

  • min_machines_running = 0

Development

npm run build
npm test
npm start

Security Notes

  • Keep FLUTTERFLOW_API_TOKEN only in environment variables or secret managers.

  • Snapshot DB can include full project YAML and should be treated as sensitive data.

  • orbit.policy.json should 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/listProjects

  • GET /listPartitionedFileNames

  • GET /projectYamls (including projectYamlBytes base64 zip decoding)

  • POST /validateProjectYaml

  • POST /updateProjectByYaml

-
license - not tested
-
quality - not tested
D
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jamesnocode/flutterflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server