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.
- package/README.md +124 -0
- package/build/api/flutterflow.d.ts +11 -0
- package/build/api/flutterflow.js +61 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +54 -0
- package/build/prompts/dev-workflow.d.ts +2 -0
- package/build/prompts/dev-workflow.js +68 -0
- package/build/prompts/generate-page.d.ts +2 -0
- package/build/prompts/generate-page.js +36 -0
- package/build/prompts/inspect-project.d.ts +2 -0
- package/build/prompts/inspect-project.js +30 -0
- package/build/prompts/modify-component.d.ts +2 -0
- package/build/prompts/modify-component.js +39 -0
- package/build/resources/docs.d.ts +2 -0
- package/build/resources/docs.js +76 -0
- package/build/resources/projects.d.ts +3 -0
- package/build/resources/projects.js +60 -0
- package/build/tools/find-component-usages.d.ts +7 -0
- package/build/tools/find-component-usages.js +225 -0
- package/build/tools/find-page-navigations.d.ts +7 -0
- package/build/tools/find-page-navigations.js +228 -0
- package/build/tools/get-component-summary.d.ts +22 -0
- package/build/tools/get-component-summary.js +193 -0
- package/build/tools/get-page-by-name.d.ts +3 -0
- package/build/tools/get-page-by-name.js +56 -0
- package/build/tools/get-page-summary.d.ts +22 -0
- package/build/tools/get-page-summary.js +220 -0
- package/build/tools/get-yaml-docs.d.ts +6 -0
- package/build/tools/get-yaml-docs.js +217 -0
- package/build/tools/get-yaml.d.ts +3 -0
- package/build/tools/get-yaml.js +47 -0
- package/build/tools/list-files.d.ts +3 -0
- package/build/tools/list-files.js +30 -0
- package/build/tools/list-pages.d.ts +25 -0
- package/build/tools/list-pages.js +101 -0
- package/build/tools/list-projects.d.ts +3 -0
- package/build/tools/list-projects.js +19 -0
- package/build/tools/sync-project.d.ts +3 -0
- package/build/tools/sync-project.js +144 -0
- package/build/tools/update-yaml.d.ts +3 -0
- package/build/tools/update-yaml.js +24 -0
- package/build/tools/validate-yaml.d.ts +3 -0
- package/build/tools/validate-yaml.js +22 -0
- package/build/utils/cache.d.ts +48 -0
- package/build/utils/cache.js +162 -0
- package/build/utils/decode-yaml.d.ts +7 -0
- package/build/utils/decode-yaml.js +31 -0
- package/build/utils/page-summary/action-summarizer.d.ts +9 -0
- package/build/utils/page-summary/action-summarizer.js +291 -0
- package/build/utils/page-summary/formatter.d.ts +13 -0
- package/build/utils/page-summary/formatter.js +121 -0
- package/build/utils/page-summary/node-extractor.d.ts +17 -0
- package/build/utils/page-summary/node-extractor.js +207 -0
- package/build/utils/page-summary/tree-walker.d.ts +6 -0
- package/build/utils/page-summary/tree-walker.js +55 -0
- package/build/utils/page-summary/types.d.ts +56 -0
- package/build/utils/page-summary/types.js +4 -0
- package/build/utils/parse-folders.d.ts +9 -0
- package/build/utils/parse-folders.js +29 -0
- package/docs/ff-yaml/00-overview.md +137 -0
- package/docs/ff-yaml/01-project-files.md +513 -0
- package/docs/ff-yaml/02-pages.md +572 -0
- package/docs/ff-yaml/03-components.md +413 -0
- package/docs/ff-yaml/04-widgets/README.md +122 -0
- package/docs/ff-yaml/04-widgets/button.md +444 -0
- package/docs/ff-yaml/04-widgets/container.md +358 -0
- package/docs/ff-yaml/04-widgets/dropdown.md +579 -0
- package/docs/ff-yaml/04-widgets/form.md +256 -0
- package/docs/ff-yaml/04-widgets/image.md +276 -0
- package/docs/ff-yaml/04-widgets/layout.md +355 -0
- package/docs/ff-yaml/04-widgets/misc.md +553 -0
- package/docs/ff-yaml/04-widgets/text-field.md +326 -0
- package/docs/ff-yaml/04-widgets/text.md +302 -0
- package/docs/ff-yaml/05-actions.md +843 -0
- package/docs/ff-yaml/06-variables.md +834 -0
- package/docs/ff-yaml/07-data.md +591 -0
- package/docs/ff-yaml/08-custom-code.md +715 -0
- package/docs/ff-yaml/09-theming.md +592 -0
- package/docs/ff-yaml/10-editing-guide.md +454 -0
- package/docs/ff-yaml/README.md +105 -0
- package/package.json +55 -0
- package/skills/ff-widget-patterns.md +141 -0
- 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
|
+
}
|
package/build/index.d.ts
ADDED
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,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,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,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,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,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,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;
|