flutterflow-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +124 -0
  2. package/build/api/flutterflow.d.ts +11 -0
  3. package/build/api/flutterflow.js +61 -0
  4. package/build/index.d.ts +2 -0
  5. package/build/index.js +54 -0
  6. package/build/prompts/dev-workflow.d.ts +2 -0
  7. package/build/prompts/dev-workflow.js +68 -0
  8. package/build/prompts/generate-page.d.ts +2 -0
  9. package/build/prompts/generate-page.js +36 -0
  10. package/build/prompts/inspect-project.d.ts +2 -0
  11. package/build/prompts/inspect-project.js +30 -0
  12. package/build/prompts/modify-component.d.ts +2 -0
  13. package/build/prompts/modify-component.js +39 -0
  14. package/build/resources/docs.d.ts +2 -0
  15. package/build/resources/docs.js +76 -0
  16. package/build/resources/projects.d.ts +3 -0
  17. package/build/resources/projects.js +60 -0
  18. package/build/tools/find-component-usages.d.ts +7 -0
  19. package/build/tools/find-component-usages.js +225 -0
  20. package/build/tools/find-page-navigations.d.ts +7 -0
  21. package/build/tools/find-page-navigations.js +228 -0
  22. package/build/tools/get-component-summary.d.ts +22 -0
  23. package/build/tools/get-component-summary.js +193 -0
  24. package/build/tools/get-page-by-name.d.ts +3 -0
  25. package/build/tools/get-page-by-name.js +56 -0
  26. package/build/tools/get-page-summary.d.ts +22 -0
  27. package/build/tools/get-page-summary.js +220 -0
  28. package/build/tools/get-yaml-docs.d.ts +6 -0
  29. package/build/tools/get-yaml-docs.js +217 -0
  30. package/build/tools/get-yaml.d.ts +3 -0
  31. package/build/tools/get-yaml.js +47 -0
  32. package/build/tools/list-files.d.ts +3 -0
  33. package/build/tools/list-files.js +30 -0
  34. package/build/tools/list-pages.d.ts +25 -0
  35. package/build/tools/list-pages.js +101 -0
  36. package/build/tools/list-projects.d.ts +3 -0
  37. package/build/tools/list-projects.js +19 -0
  38. package/build/tools/sync-project.d.ts +3 -0
  39. package/build/tools/sync-project.js +144 -0
  40. package/build/tools/update-yaml.d.ts +3 -0
  41. package/build/tools/update-yaml.js +24 -0
  42. package/build/tools/validate-yaml.d.ts +3 -0
  43. package/build/tools/validate-yaml.js +22 -0
  44. package/build/utils/cache.d.ts +48 -0
  45. package/build/utils/cache.js +162 -0
  46. package/build/utils/decode-yaml.d.ts +7 -0
  47. package/build/utils/decode-yaml.js +31 -0
  48. package/build/utils/page-summary/action-summarizer.d.ts +9 -0
  49. package/build/utils/page-summary/action-summarizer.js +291 -0
  50. package/build/utils/page-summary/formatter.d.ts +13 -0
  51. package/build/utils/page-summary/formatter.js +121 -0
  52. package/build/utils/page-summary/node-extractor.d.ts +17 -0
  53. package/build/utils/page-summary/node-extractor.js +207 -0
  54. package/build/utils/page-summary/tree-walker.d.ts +6 -0
  55. package/build/utils/page-summary/tree-walker.js +55 -0
  56. package/build/utils/page-summary/types.d.ts +56 -0
  57. package/build/utils/page-summary/types.js +4 -0
  58. package/build/utils/parse-folders.d.ts +9 -0
  59. package/build/utils/parse-folders.js +29 -0
  60. package/docs/ff-yaml/00-overview.md +137 -0
  61. package/docs/ff-yaml/01-project-files.md +513 -0
  62. package/docs/ff-yaml/02-pages.md +572 -0
  63. package/docs/ff-yaml/03-components.md +413 -0
  64. package/docs/ff-yaml/04-widgets/README.md +122 -0
  65. package/docs/ff-yaml/04-widgets/button.md +444 -0
  66. package/docs/ff-yaml/04-widgets/container.md +358 -0
  67. package/docs/ff-yaml/04-widgets/dropdown.md +579 -0
  68. package/docs/ff-yaml/04-widgets/form.md +256 -0
  69. package/docs/ff-yaml/04-widgets/image.md +276 -0
  70. package/docs/ff-yaml/04-widgets/layout.md +355 -0
  71. package/docs/ff-yaml/04-widgets/misc.md +553 -0
  72. package/docs/ff-yaml/04-widgets/text-field.md +326 -0
  73. package/docs/ff-yaml/04-widgets/text.md +302 -0
  74. package/docs/ff-yaml/05-actions.md +843 -0
  75. package/docs/ff-yaml/06-variables.md +834 -0
  76. package/docs/ff-yaml/07-data.md +591 -0
  77. package/docs/ff-yaml/08-custom-code.md +715 -0
  78. package/docs/ff-yaml/09-theming.md +592 -0
  79. package/docs/ff-yaml/10-editing-guide.md +454 -0
  80. package/docs/ff-yaml/README.md +105 -0
  81. package/package.json +55 -0
  82. package/skills/ff-widget-patterns.md +141 -0
  83. package/skills/ff-yaml-dev.md +58 -0
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # flutterflow-mcp
2
+
3
+ MCP server for the FlutterFlow Project API. Enables AI-assisted FlutterFlow development through Claude and other MCP-compatible clients.
4
+
5
+ ## Quick Start
6
+
7
+ ### 1. Get your FlutterFlow API token
8
+
9
+ Go to **FlutterFlow > Account Settings > API Token** and copy your token.
10
+
11
+ ### 2. Add to your MCP client
12
+
13
+ **Claude Code:**
14
+
15
+ ```bash
16
+ claude mcp add flutterflow -- npx -y flutterflow-mcp
17
+ ```
18
+
19
+ Then set your token in the MCP config (`~/.claude/settings.json` or project `.claude/settings.json`):
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "flutterflow": {
25
+ "command": "npx",
26
+ "args": ["-y", "flutterflow-mcp"],
27
+ "env": {
28
+ "FLUTTERFLOW_API_TOKEN": "your_token_here"
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ **Other MCP clients (Cursor, Windsurf, etc.):**
36
+
37
+ Add to your MCP configuration:
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "flutterflow": {
43
+ "command": "npx",
44
+ "args": ["-y", "flutterflow-mcp"],
45
+ "env": {
46
+ "FLUTTERFLOW_API_TOKEN": "your_token_here"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### 3. Start building
54
+
55
+ Ask your AI assistant to list your FlutterFlow projects, inspect pages, or modify widgets — it handles the rest.
56
+
57
+ ## Tools
58
+
59
+ | Tool | Description |
60
+ |------|-------------|
61
+ | `list_projects` | List all FlutterFlow projects |
62
+ | `list_project_files` | List YAML files in a project |
63
+ | `list_pages` | List all pages with names, scaffold IDs, and folders |
64
+ | `get_page_by_name` | Fetch a page by human-readable name |
65
+ | `get_project_yaml` | Download project YAML files |
66
+ | `validate_yaml` | Validate YAML before pushing |
67
+ | `update_project_yaml` | Push YAML changes to a project |
68
+ | `sync_project` | Bulk download all YAML to local cache |
69
+ | `get_page_summary` | Quick page overview from cache (widget tree, actions, params) |
70
+ | `get_component_summary` | Quick component overview from cache |
71
+ | `find_component_usages` | Find all pages/components using a given component |
72
+ | `find_page_navigations` | Find all actions that navigate to a given page |
73
+ | `get_yaml_docs` | Search/retrieve FlutterFlow YAML reference docs by topic or file |
74
+
75
+ ## Resources
76
+
77
+ | Resource | URI | Description |
78
+ |----------|-----|-------------|
79
+ | Docs Index | `ff://docs` | List all available YAML reference documentation files |
80
+ | Doc File | `ff://docs/{path}` | Read a specific YAML reference doc (e.g. `ff://docs/04-widgets/button`) |
81
+
82
+ ## Prompts
83
+
84
+ | Prompt | Description |
85
+ |--------|-------------|
86
+ | `generate-page` | Generate a new page from a description |
87
+ | `modify-component` | Modify an existing component |
88
+ | `inspect-project` | Summarize project structure |
89
+ | `flutterflow-dev-workflow` | Efficient workflow guide for AI-assisted FlutterFlow development |
90
+
91
+ ## Built-in YAML Reference
92
+
93
+ This MCP ships with a comprehensive FlutterFlow YAML reference catalog that AI models can access at runtime:
94
+
95
+ - **`get_yaml_docs` tool** — Search by topic (e.g. `get_yaml_docs(topic: "Button")`) or browse the full index
96
+ - **`ff://docs` resources** — Direct access to all 21 reference docs covering widgets, actions, variables, theming, and editing workflows
97
+
98
+ See [docs/ff-yaml/](docs/ff-yaml/) for the full catalog.
99
+
100
+ ## Claude Code Skills
101
+
102
+ Copy skills from [`skills/`](skills/) into your project's `.claude/skills/` directory:
103
+
104
+ - **`ff-yaml-dev.md`** — Core workflow: reading, editing, and creating FlutterFlow pages/components
105
+ - **`ff-widget-patterns.md`** — Quick reference for common widget YAML patterns and snippets
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ git clone https://github.com/mohn93/ff-mcp.git
111
+ cd ff-mcp
112
+ npm install
113
+ npm run build
114
+ npm run dev # watch mode
115
+ ```
116
+
117
+ ## Requirements
118
+
119
+ - Node.js 18+
120
+ - FlutterFlow API token (paid FlutterFlow subscription required)
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,11 @@
1
+ export declare class FlutterFlowClient {
2
+ private token;
3
+ constructor(token: string);
4
+ private request;
5
+ listProjects(projectType?: string, deserializeResponse?: boolean): Promise<unknown>;
6
+ listPartitionedFileNames(projectId: string): Promise<unknown>;
7
+ getProjectYamls(projectId: string, fileName?: string): Promise<unknown>;
8
+ validateProjectYaml(projectId: string, fileKey: string, fileContent: string): Promise<unknown>;
9
+ updateProjectByYaml(projectId: string, fileKeyToContent: Record<string, string>): Promise<unknown>;
10
+ }
11
+ export declare function createClient(): FlutterFlowClient;
@@ -0,0 +1,61 @@
1
+ const FF_API_BASE = "https://api.flutterflow.io/v2";
2
+ export class FlutterFlowClient {
3
+ token;
4
+ constructor(token) {
5
+ this.token = token;
6
+ }
7
+ async request(method, endpoint, body) {
8
+ const url = `${FF_API_BASE}${endpoint}`;
9
+ const headers = {
10
+ Authorization: `Bearer ${this.token}`,
11
+ "Content-Type": "application/json",
12
+ };
13
+ const res = await fetch(url, {
14
+ method,
15
+ headers,
16
+ body: body ? JSON.stringify(body) : undefined,
17
+ });
18
+ if (!res.ok) {
19
+ const errorText = await res.text();
20
+ throw new Error(`FlutterFlow API error ${res.status}: ${errorText}`);
21
+ }
22
+ return res.json();
23
+ }
24
+ async listProjects(projectType, deserializeResponse) {
25
+ return this.request("POST", "/l/listProjects", {
26
+ project_type: projectType,
27
+ deserialize_response: deserializeResponse ?? true,
28
+ });
29
+ }
30
+ async listPartitionedFileNames(projectId) {
31
+ return this.request("GET", `/listPartitionedFileNames?projectId=${encodeURIComponent(projectId)}`);
32
+ }
33
+ async getProjectYamls(projectId, fileName) {
34
+ let endpoint = `/projectYamls?projectId=${encodeURIComponent(projectId)}`;
35
+ if (fileName) {
36
+ endpoint += `&fileName=${encodeURIComponent(fileName)}`;
37
+ }
38
+ return this.request("GET", endpoint);
39
+ }
40
+ async validateProjectYaml(projectId, fileKey, fileContent) {
41
+ return this.request("POST", "/validateProjectYaml", {
42
+ projectId,
43
+ fileKey,
44
+ fileContent,
45
+ });
46
+ }
47
+ async updateProjectByYaml(projectId, fileKeyToContent) {
48
+ return this.request("POST", "/updateProjectByYaml", {
49
+ projectId,
50
+ fileKeyToContent,
51
+ });
52
+ }
53
+ }
54
+ export function createClient() {
55
+ const token = process.env.FLUTTERFLOW_API_TOKEN;
56
+ if (!token) {
57
+ throw new Error("FLUTTERFLOW_API_TOKEN environment variable is required. " +
58
+ "Get your token from FlutterFlow > Account Settings > API Token.");
59
+ }
60
+ return new FlutterFlowClient(token);
61
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { createClient } from "./api/flutterflow.js";
5
+ import { registerListProjectsTool } from "./tools/list-projects.js";
6
+ import { registerListFilesTool } from "./tools/list-files.js";
7
+ import { registerGetYamlTool } from "./tools/get-yaml.js";
8
+ import { registerValidateYamlTool } from "./tools/validate-yaml.js";
9
+ import { registerUpdateYamlTool } from "./tools/update-yaml.js";
10
+ import { registerListPagesTool } from "./tools/list-pages.js";
11
+ import { registerGetPageByNameTool } from "./tools/get-page-by-name.js";
12
+ import { registerSyncProjectTool } from "./tools/sync-project.js";
13
+ import { registerGetPageSummaryTool } from "./tools/get-page-summary.js";
14
+ import { registerGetComponentSummaryTool } from "./tools/get-component-summary.js";
15
+ import { registerFindComponentUsagesTool } from "./tools/find-component-usages.js";
16
+ import { registerFindPageNavigationsTool } from "./tools/find-page-navigations.js";
17
+ import { registerResources } from "./resources/projects.js";
18
+ import { registerDocsResources } from "./resources/docs.js";
19
+ import { registerGeneratePagePrompt } from "./prompts/generate-page.js";
20
+ import { registerModifyComponentPrompt } from "./prompts/modify-component.js";
21
+ import { registerInspectProjectPrompt } from "./prompts/inspect-project.js";
22
+ import { registerDevWorkflowPrompt } from "./prompts/dev-workflow.js";
23
+ import { registerGetYamlDocsTool } from "./tools/get-yaml-docs.js";
24
+ const server = new McpServer({
25
+ name: "ff-mcp",
26
+ version: "0.1.0",
27
+ });
28
+ const client = createClient();
29
+ // Register tools
30
+ registerListProjectsTool(server, client);
31
+ registerListFilesTool(server, client);
32
+ registerGetYamlTool(server, client);
33
+ registerValidateYamlTool(server, client);
34
+ registerUpdateYamlTool(server, client);
35
+ registerListPagesTool(server, client);
36
+ registerGetPageByNameTool(server, client);
37
+ registerSyncProjectTool(server, client);
38
+ registerGetPageSummaryTool(server);
39
+ registerGetComponentSummaryTool(server);
40
+ registerFindComponentUsagesTool(server);
41
+ registerFindPageNavigationsTool(server);
42
+ registerGetYamlDocsTool(server);
43
+ // Register resources
44
+ registerResources(server, client);
45
+ registerDocsResources(server);
46
+ // Register prompts
47
+ registerGeneratePagePrompt(server);
48
+ registerModifyComponentPrompt(server);
49
+ registerInspectProjectPrompt(server);
50
+ registerDevWorkflowPrompt(server);
51
+ // Start server
52
+ const transport = new StdioServerTransport();
53
+ await server.connect(transport);
54
+ console.error("FlutterFlow MCP server running on stdio");
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerDevWorkflowPrompt(server: McpServer): void;
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ export function registerDevWorkflowPrompt(server) {
3
+ server.prompt("flutterflow-dev-workflow", "Efficient workflow guide for developing FlutterFlow apps through MCP tools. References the full YAML docs catalog via get_yaml_docs tool.", {
4
+ projectId: z
5
+ .string()
6
+ .optional()
7
+ .describe("Optional project ID to include in instructions"),
8
+ }, ({ projectId }) => ({
9
+ messages: [
10
+ {
11
+ role: "user",
12
+ content: {
13
+ type: "text",
14
+ text: `You are an expert FlutterFlow developer using MCP tools to read and modify FlutterFlow projects.
15
+
16
+ ## Documentation
17
+
18
+ This MCP server includes a comprehensive FlutterFlow YAML reference catalog. Use the \`get_yaml_docs\` tool to look up any schema, pattern, or convention:
19
+
20
+ - \`get_yaml_docs(topic: "Button")\` — Widget schemas (Button, Text, TextField, Container, etc.)
21
+ - \`get_yaml_docs(topic: "actions")\` — Action chains, triggers, navigation
22
+ - \`get_yaml_docs(topic: "variables")\` — Data binding, variable sources
23
+ - \`get_yaml_docs(topic: "theming")\` — Colors, typography, dimensions
24
+ - \`get_yaml_docs(topic: "editing")\` — Read/edit/add workflows and anti-patterns
25
+ - \`get_yaml_docs()\` — Full index of all available docs
26
+
27
+ Always consult the docs before writing YAML. They contain validated schemas, field references, enum values, and real examples from production projects.
28
+
29
+ ## Efficient Workflow
30
+
31
+ ${projectId ? `**Project ID:** \`${projectId}\`\n` : ""}
32
+ ### Reading / Inspecting
33
+ \`\`\`
34
+ list_projects → sync_project → get_page_summary / get_component_summary
35
+ \`\`\`
36
+
37
+ ### Editing Existing Widgets
38
+ \`\`\`
39
+ list_pages → get_page_by_name → (node-level fetch) → validate_yaml → update_project_yaml
40
+ \`\`\`
41
+
42
+ ### Adding New Widgets
43
+ \`\`\`
44
+ list_pages → get_page_by_name → update widget-tree-outline + push individual node files → validate_yaml → update_project_yaml
45
+ \`\`\`
46
+
47
+ ## Critical YAML Rules
48
+
49
+ 1. **Always update both \`inputValue\` AND \`mostRecentInputValue\`** — they must stay in sync.
50
+ - **Exceptions:** \`fontWeightValue\` and \`fontSizeValue\` only accept \`inputValue\`.
51
+ 2. **Use node-level file keys** for targeted edits, not the full page YAML.
52
+ 3. **Always validate before pushing** — call \`validate_yaml\` first.
53
+ 4. **Adding widgets requires node-level files** — push the tree outline + individual nodes together.
54
+ 5. **Column has no \`mainAxisSize\`** — use \`minSizeValue: { inputValue: true }\` instead.
55
+ 6. **AppBar \`templateType\`** — only \`LARGE_HEADER\` is valid. Control height via \`toolbarHeight\`.
56
+ 7. **TextField keyboard types** — use \`EMAIL_ADDRESS\`, not \`EMAIL\`.
57
+
58
+ ## Anti-Patterns
59
+
60
+ - Do NOT call \`list_project_files\` to find pages — use \`list_pages\` instead.
61
+ - Do NOT fetch pages one-by-one — use \`get_page_by_name\`.
62
+ - Do NOT edit full page YAML for a single widget — use node-level file keys.
63
+ - Do NOT guess YAML field names — use \`get_yaml_docs\` to look them up.`,
64
+ },
65
+ },
66
+ ],
67
+ }));
68
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGeneratePagePrompt(server: McpServer): void;
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ export function registerGeneratePagePrompt(server) {
3
+ server.prompt("generate-page", "Generate a new FlutterFlow page from a natural language description", {
4
+ projectId: z.string().describe("The FlutterFlow project ID"),
5
+ description: z
6
+ .string()
7
+ .describe("Natural language description of the page to create"),
8
+ }, ({ projectId, description }) => ({
9
+ messages: [
10
+ {
11
+ role: "user",
12
+ content: {
13
+ type: "text",
14
+ text: `You are an expert FlutterFlow developer. Generate a new page for the FlutterFlow project.
15
+
16
+ ## Instructions
17
+
18
+ 1. First, use the list_project_files tool with projectId "${projectId}" to understand the existing project structure.
19
+ 2. Then, use get_project_yaml to read a few existing pages to understand the YAML schema and conventions used in this project.
20
+ 3. Based on the following description, generate valid FlutterFlow YAML for a new page:
21
+
22
+ **Page Description:** ${description}
23
+
24
+ 4. Use validate_yaml to check your generated YAML is valid.
25
+ 5. If validation passes, use update_project_yaml to push the new page to the project.
26
+ 6. If validation fails, fix the errors and try again.
27
+
28
+ ## Important
29
+ - Follow the exact YAML structure you observed in existing pages.
30
+ - Use consistent naming conventions matching the project.
31
+ - YAML content must be single-line strings with escaped newlines (\\n).`,
32
+ },
33
+ },
34
+ ],
35
+ }));
36
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerInspectProjectPrompt(server: McpServer): void;
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ export function registerInspectProjectPrompt(server) {
3
+ server.prompt("inspect-project", "Read and summarize a FlutterFlow project's structure, pages, and components", {
4
+ projectId: z.string().describe("The FlutterFlow project ID"),
5
+ }, ({ projectId }) => ({
6
+ messages: [
7
+ {
8
+ role: "user",
9
+ content: {
10
+ type: "text",
11
+ text: `You are an expert FlutterFlow developer. Inspect and summarize the FlutterFlow project.
12
+
13
+ ## Instructions
14
+
15
+ 1. Use list_project_files with projectId "${projectId}" to get all files.
16
+ 2. Use get_project_yaml to read key files like "app-details", "folders", and "authentication".
17
+ 3. Read a sample of page and component files to understand the app structure.
18
+ 4. Provide a clear summary including:
19
+ - App name and details
20
+ - Number of pages and their names
21
+ - Number of components and their names
22
+ - Data models / collections
23
+ - Authentication setup
24
+ - Any custom code files
25
+ - Overall architecture observations`,
26
+ },
27
+ },
28
+ ],
29
+ }));
30
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerModifyComponentPrompt(server: McpServer): void;
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ export function registerModifyComponentPrompt(server) {
3
+ server.prompt("modify-component", "Read an existing FlutterFlow component and modify it based on instructions", {
4
+ projectId: z.string().describe("The FlutterFlow project ID"),
5
+ fileName: z
6
+ .string()
7
+ .describe("The YAML file name of the component to modify"),
8
+ changes: z
9
+ .string()
10
+ .describe("Description of changes to make to the component"),
11
+ }, ({ projectId, fileName, changes }) => ({
12
+ messages: [
13
+ {
14
+ role: "user",
15
+ content: {
16
+ type: "text",
17
+ text: `You are an expert FlutterFlow developer. Modify an existing component in the FlutterFlow project.
18
+
19
+ ## Instructions
20
+
21
+ 1. Use get_project_yaml with projectId "${projectId}" and fileName "${fileName}" to read the current component YAML.
22
+ 2. Understand the current structure and widget tree.
23
+ 3. Apply the following changes:
24
+
25
+ **Requested Changes:** ${changes}
26
+
27
+ 4. Use validate_yaml to verify your modified YAML is valid.
28
+ 5. If validation passes, use update_project_yaml to push the changes.
29
+ 6. If validation fails, fix the errors and try again.
30
+
31
+ ## Important
32
+ - Preserve all existing structure you are not modifying.
33
+ - YAML content must be single-line strings with escaped newlines (\\n).
34
+ - Only change what was requested — do not refactor or reorganize.`,
35
+ },
36
+ },
37
+ ],
38
+ }));
39
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerDocsResources(server: McpServer): void;
@@ -0,0 +1,76 @@
1
+ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const DOCS_DIR = path.resolve(__dirname, "../../docs/ff-yaml");
8
+ /** Recursively list all .md files under a directory, returning relative paths. */
9
+ function listDocFiles(dir, prefix = "") {
10
+ const results = [];
11
+ if (!fs.existsSync(dir))
12
+ return results;
13
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
14
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
15
+ if (entry.isDirectory()) {
16
+ results.push(...listDocFiles(path.join(dir, entry.name), relPath));
17
+ }
18
+ else if (entry.name.endsWith(".md")) {
19
+ results.push(relPath);
20
+ }
21
+ }
22
+ return results;
23
+ }
24
+ export function registerDocsResources(server) {
25
+ // Static resource: list all available docs
26
+ server.resource("docs-index", "ff://docs", {
27
+ description: "FlutterFlow YAML reference catalog — list all available documentation files",
28
+ mimeType: "application/json",
29
+ }, async (uri) => {
30
+ const files = listDocFiles(DOCS_DIR);
31
+ const index = files.map((f) => ({
32
+ file: f.replace(/\.md$/, ""),
33
+ uri: `ff://docs/${f.replace(/\.md$/, "")}`,
34
+ }));
35
+ return {
36
+ contents: [
37
+ {
38
+ uri: uri.href,
39
+ text: JSON.stringify(index, null, 2),
40
+ mimeType: "application/json",
41
+ },
42
+ ],
43
+ };
44
+ });
45
+ // Dynamic resource: get a specific doc file
46
+ server.resource("docs-file", new ResourceTemplate("ff://docs/{+path}", { list: undefined }), {
47
+ description: "Read a specific FlutterFlow YAML reference doc (e.g. ff://docs/04-widgets/button)",
48
+ mimeType: "text/markdown",
49
+ }, async (uri, { path: docPath }) => {
50
+ const filePath = path.join(DOCS_DIR, `${docPath}.md`);
51
+ if (!fs.existsSync(filePath)) {
52
+ const available = listDocFiles(DOCS_DIR)
53
+ .map((f) => f.replace(/\.md$/, ""))
54
+ .join("\n ");
55
+ return {
56
+ contents: [
57
+ {
58
+ uri: uri.href,
59
+ text: `Doc not found: ${docPath}\n\nAvailable docs:\n ${available}`,
60
+ mimeType: "text/plain",
61
+ },
62
+ ],
63
+ };
64
+ }
65
+ const content = fs.readFileSync(filePath, "utf-8");
66
+ return {
67
+ contents: [
68
+ {
69
+ uri: uri.href,
70
+ text: content,
71
+ mimeType: "text/markdown",
72
+ },
73
+ ],
74
+ };
75
+ });
76
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { FlutterFlowClient } from "../api/flutterflow.js";
3
+ export declare function registerResources(server: McpServer, client: FlutterFlowClient): void;
@@ -0,0 +1,60 @@
1
+ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { decodeProjectYamlResponse } from "../utils/decode-yaml.js";
3
+ export function registerResources(server, client) {
4
+ // Static resource: list all projects
5
+ server.resource("projects", "ff://projects", {
6
+ description: "List all FlutterFlow projects",
7
+ mimeType: "application/json",
8
+ }, async (uri) => {
9
+ const projects = await client.listProjects();
10
+ return {
11
+ contents: [
12
+ {
13
+ uri: uri.href,
14
+ text: JSON.stringify(projects, null, 2),
15
+ mimeType: "application/json",
16
+ },
17
+ ],
18
+ };
19
+ });
20
+ // Dynamic resource: list files in a project
21
+ server.resource("project-files", new ResourceTemplate("ff://projects/{projectId}/files", {
22
+ list: undefined,
23
+ }), {
24
+ description: "List YAML file names in a FlutterFlow project",
25
+ mimeType: "application/json",
26
+ }, async (uri, { projectId }) => {
27
+ const files = await client.listPartitionedFileNames(projectId);
28
+ return {
29
+ contents: [
30
+ {
31
+ uri: uri.href,
32
+ text: JSON.stringify(files, null, 2),
33
+ mimeType: "application/json",
34
+ },
35
+ ],
36
+ };
37
+ });
38
+ // Dynamic resource: get specific YAML file
39
+ server.resource("project-yaml", new ResourceTemplate("ff://projects/{projectId}/yaml/{+fileName}", {
40
+ list: undefined,
41
+ }), {
42
+ description: "Get a specific YAML file from a FlutterFlow project",
43
+ mimeType: "text/yaml",
44
+ }, async (uri, { projectId, fileName }) => {
45
+ const raw = await client.getProjectYamls(projectId, fileName);
46
+ const decoded = decodeProjectYamlResponse(raw);
47
+ const yamlText = Object.entries(decoded)
48
+ .map(([name, content]) => `# ${name}\n${content}`)
49
+ .join("\n");
50
+ return {
51
+ contents: [
52
+ {
53
+ uri: uri.href,
54
+ text: yamlText,
55
+ mimeType: "text/yaml",
56
+ },
57
+ ],
58
+ };
59
+ });
60
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * find_component_usages tool — scans cached node files to find all pages and
3
+ * components where a given component is instantiated.
4
+ * Zero API calls: everything comes from the local .ff-cache.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ export declare function registerFindComponentUsagesTool(server: McpServer): void;