flutterflow-mcp 0.2.2 → 0.2.5
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/build/index.js +12 -0
- package/build/tools/find-component-usages.d.ts +14 -0
- package/build/tools/find-component-usages.js +3 -12
- package/build/tools/find-page-navigations.d.ts +19 -0
- package/build/tools/find-page-navigations.js +3 -11
- package/build/tools/get-api-endpoints.d.ts +2 -0
- package/build/tools/get-api-endpoints.js +126 -0
- package/build/tools/get-app-state.d.ts +2 -0
- package/build/tools/get-app-state.js +96 -0
- package/build/tools/get-component-summary.js +3 -1
- package/build/tools/get-custom-code.d.ts +2 -0
- package/build/tools/get-custom-code.js +284 -0
- package/build/tools/get-data-models.d.ts +2 -0
- package/build/tools/get-data-models.js +266 -0
- package/build/tools/get-page-summary.js +4 -19
- package/build/tools/get-project-config.d.ts +2 -0
- package/build/tools/get-project-config.js +160 -0
- package/build/tools/get-theme.d.ts +2 -0
- package/build/tools/get-theme.js +199 -0
- package/build/tools/sync-project.js +4 -1
- package/build/utils/batch-process.d.ts +2 -0
- package/build/utils/batch-process.js +10 -0
- package/build/utils/cache.d.ts +5 -0
- package/build/utils/cache.js +16 -1
- package/build/utils/page-summary/action-summarizer.d.ts +16 -1
- package/build/utils/page-summary/action-summarizer.js +3 -3
- package/build/utils/page-summary/formatter.js +9 -1
- package/build/utils/page-summary/node-extractor.d.ts +7 -0
- package/build/utils/page-summary/node-extractor.js +22 -2
- package/build/utils/page-summary/types.d.ts +2 -0
- package/build/utils/resolve-data-type.d.ts +2 -0
- package/build/utils/resolve-data-type.js +18 -0
- package/docs/ff-yaml/00-overview.md +30 -1
- package/docs/ff-yaml/01-project-files.md +1380 -4
- package/docs/ff-yaml/03-components.md +62 -2
- package/docs/ff-yaml/08-custom-code.md +77 -1
- package/docs/ff-yaml/09-theming.md +46 -0
- package/package.json +6 -2
package/build/index.js
CHANGED
|
@@ -21,6 +21,12 @@ import { registerModifyComponentPrompt } from "./prompts/modify-component.js";
|
|
|
21
21
|
import { registerInspectProjectPrompt } from "./prompts/inspect-project.js";
|
|
22
22
|
import { registerDevWorkflowPrompt } from "./prompts/dev-workflow.js";
|
|
23
23
|
import { registerGetYamlDocsTool } from "./tools/get-yaml-docs.js";
|
|
24
|
+
import { registerGetAppStateTool } from "./tools/get-app-state.js";
|
|
25
|
+
import { registerGetApiEndpointsTool } from "./tools/get-api-endpoints.js";
|
|
26
|
+
import { registerGetDataModelsTool } from "./tools/get-data-models.js";
|
|
27
|
+
import { registerGetCustomCodeTool } from "./tools/get-custom-code.js";
|
|
28
|
+
import { registerGetProjectConfigTool } from "./tools/get-project-config.js";
|
|
29
|
+
import { registerGetThemeTool } from "./tools/get-theme.js";
|
|
24
30
|
const server = new McpServer({
|
|
25
31
|
name: "ff-mcp",
|
|
26
32
|
version: "0.1.0",
|
|
@@ -40,6 +46,12 @@ registerGetComponentSummaryTool(server);
|
|
|
40
46
|
registerFindComponentUsagesTool(server);
|
|
41
47
|
registerFindPageNavigationsTool(server);
|
|
42
48
|
registerGetYamlDocsTool(server);
|
|
49
|
+
registerGetAppStateTool(server);
|
|
50
|
+
registerGetApiEndpointsTool(server);
|
|
51
|
+
registerGetDataModelsTool(server);
|
|
52
|
+
registerGetCustomCodeTool(server);
|
|
53
|
+
registerGetProjectConfigTool(server);
|
|
54
|
+
registerGetThemeTool(server);
|
|
43
55
|
// Register resources
|
|
44
56
|
registerResources(server, client);
|
|
45
57
|
registerDocsResources(server);
|
|
@@ -4,4 +4,18 @@
|
|
|
4
4
|
* Zero API calls: everything comes from the local .ff-cache.
|
|
5
5
|
*/
|
|
6
6
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the value of a parameter pass to a readable string.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveParamValue(paramObj: Record<string, unknown>): string;
|
|
11
|
+
/**
|
|
12
|
+
* Extract parent context (page or component name + ID) from a file key path.
|
|
13
|
+
* Examples:
|
|
14
|
+
* "page/id-Scaffold_xxx/page-widget-tree-outline/node/id-Widget_yyy"
|
|
15
|
+
* "component/id-Container_xxx/component-widget-tree-outline/node/id-Widget_yyy"
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseParentFromKey(fileKey: string): {
|
|
18
|
+
type: "page" | "component";
|
|
19
|
+
id: string;
|
|
20
|
+
} | null;
|
|
7
21
|
export declare function registerFindComponentUsagesTool(server: McpServer): void;
|
|
@@ -2,23 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import YAML from "yaml";
|
|
3
3
|
import { cacheRead, cacheMeta, listCachedKeys, } from "../utils/cache.js";
|
|
4
4
|
import { resolveComponent } from "./get-component-summary.js";
|
|
5
|
+
import { batchProcess } from "../utils/batch-process.js";
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// Helpers
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
|
-
/** Batch-process items in groups to avoid overwhelming the file system. */
|
|
9
|
-
async function batchProcess(items, batchSize, fn) {
|
|
10
|
-
const results = [];
|
|
11
|
-
for (let i = 0; i < items.length; i += batchSize) {
|
|
12
|
-
const batch = items.slice(i, i + batchSize);
|
|
13
|
-
const batchResults = await Promise.all(batch.map(fn));
|
|
14
|
-
results.push(...batchResults);
|
|
15
|
-
}
|
|
16
|
-
return results;
|
|
17
|
-
}
|
|
18
9
|
/**
|
|
19
10
|
* Resolve the value of a parameter pass to a readable string.
|
|
20
11
|
*/
|
|
21
|
-
function resolveParamValue(paramObj) {
|
|
12
|
+
export function resolveParamValue(paramObj) {
|
|
22
13
|
// Check for variable source (e.g. INTERNATIONALIZATION)
|
|
23
14
|
const variable = paramObj.variable;
|
|
24
15
|
if (variable) {
|
|
@@ -60,7 +51,7 @@ function resolveParamValue(paramObj) {
|
|
|
60
51
|
* "page/id-Scaffold_xxx/page-widget-tree-outline/node/id-Widget_yyy"
|
|
61
52
|
* "component/id-Container_xxx/component-widget-tree-outline/node/id-Widget_yyy"
|
|
62
53
|
*/
|
|
63
|
-
function parseParentFromKey(fileKey) {
|
|
54
|
+
export function parseParentFromKey(fileKey) {
|
|
64
55
|
const pageMatch = fileKey.match(/^page\/id-(Scaffold_\w+)\//);
|
|
65
56
|
if (pageMatch)
|
|
66
57
|
return { type: "page", id: pageMatch[1] };
|
|
@@ -4,4 +4,23 @@
|
|
|
4
4
|
* Zero API calls: everything comes from the local .ff-cache.
|
|
5
5
|
*/
|
|
6
6
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
/**
|
|
8
|
+
* Parse parent context from an action file key.
|
|
9
|
+
* Example: "page/id-Scaffold_XXX/page-widget-tree-outline/node/id-Widget_YYY/trigger_actions/id-ON_TAP/action/id-zzz"
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseActionContext(fileKey: string): {
|
|
12
|
+
parentType: "page" | "component";
|
|
13
|
+
parentId: string;
|
|
14
|
+
widgetKey: string;
|
|
15
|
+
trigger: string;
|
|
16
|
+
} | null;
|
|
17
|
+
/**
|
|
18
|
+
* Recursively search an object for a navigate action targeting the given scaffold ID.
|
|
19
|
+
* Returns navigate details if found, including whether it's inside a disableAction.
|
|
20
|
+
*/
|
|
21
|
+
export declare function findNavigateAction(obj: unknown, targetScaffoldId: string, isDisabled: boolean, depth: number): {
|
|
22
|
+
disabled: boolean;
|
|
23
|
+
allowBack: boolean;
|
|
24
|
+
passedParams: string[];
|
|
25
|
+
} | null;
|
|
7
26
|
export declare function registerFindPageNavigationsTool(server: McpServer): void;
|
|
@@ -2,23 +2,15 @@ import { z } from "zod";
|
|
|
2
2
|
import YAML from "yaml";
|
|
3
3
|
import { cacheRead, cacheMeta, listCachedKeys, } from "../utils/cache.js";
|
|
4
4
|
import { resolvePage } from "./get-page-summary.js";
|
|
5
|
+
import { batchProcess } from "../utils/batch-process.js";
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// Helpers
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
|
-
async function batchProcess(items, batchSize, fn) {
|
|
9
|
-
const results = [];
|
|
10
|
-
for (let i = 0; i < items.length; i += batchSize) {
|
|
11
|
-
const batch = items.slice(i, i + batchSize);
|
|
12
|
-
const batchResults = await Promise.all(batch.map(fn));
|
|
13
|
-
results.push(...batchResults);
|
|
14
|
-
}
|
|
15
|
-
return results;
|
|
16
|
-
}
|
|
17
9
|
/**
|
|
18
10
|
* Parse parent context from an action file key.
|
|
19
11
|
* Example: "page/id-Scaffold_XXX/page-widget-tree-outline/node/id-Widget_YYY/trigger_actions/id-ON_TAP/action/id-zzz"
|
|
20
12
|
*/
|
|
21
|
-
function parseActionContext(fileKey) {
|
|
13
|
+
export function parseActionContext(fileKey) {
|
|
22
14
|
// Page action
|
|
23
15
|
const pageMatch = fileKey.match(/^page\/id-(Scaffold_\w+)\/.*\/node\/id-(\w+)\/trigger_actions\/id-([^/]+)\/action\//);
|
|
24
16
|
if (pageMatch) {
|
|
@@ -45,7 +37,7 @@ function parseActionContext(fileKey) {
|
|
|
45
37
|
* Recursively search an object for a navigate action targeting the given scaffold ID.
|
|
46
38
|
* Returns navigate details if found, including whether it's inside a disableAction.
|
|
47
39
|
*/
|
|
48
|
-
function findNavigateAction(obj, targetScaffoldId, isDisabled, depth) {
|
|
40
|
+
export function findNavigateAction(obj, targetScaffoldId, isDisabled, depth) {
|
|
49
41
|
if (!obj || typeof obj !== "object" || depth > 12)
|
|
50
42
|
return null;
|
|
51
43
|
const o = obj;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { cacheRead, cacheMeta, listCachedKeys } from "../utils/cache.js";
|
|
4
|
+
import { batchProcess } from "../utils/batch-process.js";
|
|
5
|
+
function parseEndpoint(content) {
|
|
6
|
+
const doc = YAML.parse(content);
|
|
7
|
+
if (!doc)
|
|
8
|
+
return null;
|
|
9
|
+
const identifier = doc.identifier;
|
|
10
|
+
const name = identifier?.name || "Unknown";
|
|
11
|
+
const method = doc.callType || "GET";
|
|
12
|
+
const url = doc.url || "";
|
|
13
|
+
const bodyType = doc.bodyType;
|
|
14
|
+
const body = doc.body;
|
|
15
|
+
const variables = [];
|
|
16
|
+
const rawVars = doc.variables;
|
|
17
|
+
if (Array.isArray(rawVars)) {
|
|
18
|
+
for (const v of rawVars) {
|
|
19
|
+
const vid = v.identifier;
|
|
20
|
+
const vName = vid?.name || "unknown";
|
|
21
|
+
const vType = v.type || "String";
|
|
22
|
+
variables.push({ name: vName, type: vType });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const headers = [];
|
|
26
|
+
const rawHeaders = doc.headers;
|
|
27
|
+
if (Array.isArray(rawHeaders)) {
|
|
28
|
+
headers.push(...rawHeaders);
|
|
29
|
+
}
|
|
30
|
+
const responseFields = [];
|
|
31
|
+
const rawJsonPaths = doc.jsonPathDefinitions;
|
|
32
|
+
if (Array.isArray(rawJsonPaths)) {
|
|
33
|
+
for (const jp of rawJsonPaths) {
|
|
34
|
+
const jpId = jp.identifier;
|
|
35
|
+
const jpName = jpId?.name || "unknown";
|
|
36
|
+
const jpDef = jp.jsonPath;
|
|
37
|
+
const path = jpDef?.jsonPath || "";
|
|
38
|
+
const returnParam = jpDef?.returnParameter;
|
|
39
|
+
const dt = returnParam?.dataType;
|
|
40
|
+
const scalarType = dt?.scalarType || "String";
|
|
41
|
+
responseFields.push({ name: jpName, type: scalarType, jsonPath: path });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { name, method, url, bodyType, body, variables, headers, responseFields };
|
|
45
|
+
}
|
|
46
|
+
function formatEndpoints(endpoints) {
|
|
47
|
+
if (endpoints.length === 0)
|
|
48
|
+
return "No API endpoints found in cache.";
|
|
49
|
+
const lines = [`# API Endpoints (${endpoints.length})`];
|
|
50
|
+
for (const ep of endpoints) {
|
|
51
|
+
lines.push("");
|
|
52
|
+
lines.push(`## ${ep.name}`);
|
|
53
|
+
lines.push(`Method: ${ep.method}`);
|
|
54
|
+
lines.push(`URL: ${ep.url}`);
|
|
55
|
+
if (ep.bodyType)
|
|
56
|
+
lines.push(`Body type: ${ep.bodyType}`);
|
|
57
|
+
if (ep.variables.length > 0) {
|
|
58
|
+
lines.push("Variables:");
|
|
59
|
+
for (const v of ep.variables) {
|
|
60
|
+
lines.push(` - ${v.name}: ${v.type}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (ep.headers.length > 0) {
|
|
64
|
+
lines.push("Headers:");
|
|
65
|
+
for (const h of ep.headers) {
|
|
66
|
+
lines.push(` - ${h}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (ep.responseFields.length > 0) {
|
|
70
|
+
lines.push("Response fields:");
|
|
71
|
+
for (const rf of ep.responseFields) {
|
|
72
|
+
lines.push(` - ${rf.name}: ${rf.type} (${rf.jsonPath})`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
|
78
|
+
export function registerGetApiEndpointsTool(server) {
|
|
79
|
+
server.tool("get_api_endpoints", "Get API endpoint definitions from local cache — method, URL, variables, headers, response fields. No API calls. Run sync_project first if not cached.", {
|
|
80
|
+
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
81
|
+
name: z
|
|
82
|
+
.string()
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Case-insensitive filter on endpoint name"),
|
|
85
|
+
}, async ({ projectId, name }) => {
|
|
86
|
+
const meta = await cacheMeta(projectId);
|
|
87
|
+
if (!meta) {
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `No cache found for project "${projectId}". Run sync_project first to download the project YAML files.`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const allKeys = await listCachedKeys(projectId, "api-endpoint/id-");
|
|
98
|
+
const topLevelKeys = allKeys.filter((k) => /^api-endpoint\/id-[a-z0-9]+$/i.test(k));
|
|
99
|
+
const parsed = await batchProcess(topLevelKeys, 10, async (key) => {
|
|
100
|
+
const content = await cacheRead(projectId, key);
|
|
101
|
+
if (!content)
|
|
102
|
+
return null;
|
|
103
|
+
return parseEndpoint(content);
|
|
104
|
+
});
|
|
105
|
+
let endpoints = parsed.filter((ep) => ep !== null);
|
|
106
|
+
if (name) {
|
|
107
|
+
const lower = name.toLowerCase();
|
|
108
|
+
const filtered = endpoints.filter((ep) => ep.name.toLowerCase().includes(lower));
|
|
109
|
+
if (filtered.length === 0) {
|
|
110
|
+
const available = endpoints.map((ep) => ep.name).join(", ");
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: `No API endpoints matching '${name}' found. Available: ${available}`,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
endpoints = filtered;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: "text", text: formatEndpoints(endpoints) }],
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { cacheRead, cacheMeta } from "../utils/cache.js";
|
|
4
|
+
import { resolveDataType } from "../utils/resolve-data-type.js";
|
|
5
|
+
export function registerGetAppStateTool(server) {
|
|
6
|
+
server.tool("get_app_state", "Get app state variables, constants, and environment settings from local cache. No API calls. Run sync_project first if not cached.", {
|
|
7
|
+
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
8
|
+
}, async ({ projectId }) => {
|
|
9
|
+
const meta = await cacheMeta(projectId);
|
|
10
|
+
if (!meta) {
|
|
11
|
+
return {
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
type: "text",
|
|
15
|
+
text: `No cache found for project "${projectId}". Run sync_project first.`,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const sections = ["# App State"];
|
|
21
|
+
// --- State Variables ---
|
|
22
|
+
const appStateYaml = await cacheRead(projectId, "app-state");
|
|
23
|
+
if (appStateYaml) {
|
|
24
|
+
const doc = YAML.parse(appStateYaml);
|
|
25
|
+
const fields = doc.fields || [];
|
|
26
|
+
const lines = [];
|
|
27
|
+
for (const field of fields) {
|
|
28
|
+
const name = field.parameter?.identifier?.name || "unknown";
|
|
29
|
+
const dt = resolveDataType(field.parameter?.dataType || {});
|
|
30
|
+
const parts = [`- ${name}: ${dt}`];
|
|
31
|
+
if (field.persisted)
|
|
32
|
+
parts.push("(persisted)");
|
|
33
|
+
if (field.serializedDefaultValue && field.serializedDefaultValue.length > 0) {
|
|
34
|
+
const joined = field.serializedDefaultValue
|
|
35
|
+
.map((v) => `"${v}"`)
|
|
36
|
+
.join(", ");
|
|
37
|
+
parts.push(`[default: ${joined}]`);
|
|
38
|
+
}
|
|
39
|
+
lines.push(parts.join(" "));
|
|
40
|
+
}
|
|
41
|
+
sections.push("\n## State Variables");
|
|
42
|
+
if (lines.length > 0) {
|
|
43
|
+
sections.push(lines.join("\n"));
|
|
44
|
+
}
|
|
45
|
+
const secure = doc.securePersistedValues ?? false;
|
|
46
|
+
sections.push(`\nSecure persisted values: ${secure ? "Yes" : "No"}`);
|
|
47
|
+
}
|
|
48
|
+
// --- Constants ---
|
|
49
|
+
const constantsYaml = await cacheRead(projectId, "app-constants");
|
|
50
|
+
if (constantsYaml) {
|
|
51
|
+
const doc = YAML.parse(constantsYaml);
|
|
52
|
+
const fields = doc.fields || [];
|
|
53
|
+
const lines = [];
|
|
54
|
+
for (const field of fields) {
|
|
55
|
+
const name = field.parameter?.identifier?.name || "unknown";
|
|
56
|
+
const dt = resolveDataType(field.parameter?.dataType || {});
|
|
57
|
+
const vals = field.serializedValue || [];
|
|
58
|
+
const formatted = vals.map((v) => `"${v}"`).join(", ");
|
|
59
|
+
lines.push(`- ${name}: ${dt} = [${formatted}]`);
|
|
60
|
+
}
|
|
61
|
+
sections.push("\n## Constants");
|
|
62
|
+
if (lines.length > 0) {
|
|
63
|
+
sections.push(lines.join("\n"));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// --- Environment Settings ---
|
|
67
|
+
const envYaml = await cacheRead(projectId, "environment-settings");
|
|
68
|
+
if (envYaml) {
|
|
69
|
+
const doc = YAML.parse(envYaml);
|
|
70
|
+
const currentEnv = doc.currentEnvironment;
|
|
71
|
+
const envValues = doc.environmentValues || [];
|
|
72
|
+
sections.push("\n## Environment Settings");
|
|
73
|
+
if (currentEnv) {
|
|
74
|
+
sections.push(`Current: ${currentEnv.name || "unknown"} (${currentEnv.key || "?"})`);
|
|
75
|
+
}
|
|
76
|
+
const lines = [];
|
|
77
|
+
for (const ev of envValues) {
|
|
78
|
+
const name = ev.parameter?.identifier?.name || "unknown";
|
|
79
|
+
const dt = resolveDataType(ev.parameter?.dataType || {});
|
|
80
|
+
const privateTag = ev.isPrivate ? " (private)" : "";
|
|
81
|
+
lines.push(`- ${name}: ${dt}${privateTag}`);
|
|
82
|
+
const valuesMap = ev.valuesMap || {};
|
|
83
|
+
for (const [envKey, envVal] of Object.entries(valuesMap)) {
|
|
84
|
+
const display = ev.isPrivate ? "****" : (envVal.serializedValue ?? "");
|
|
85
|
+
lines.push(` ${envKey}: ${display}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (lines.length > 0) {
|
|
89
|
+
sections.push(lines.join("\n"));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: sections.join("\n") }],
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -110,6 +110,8 @@ outline) {
|
|
|
110
110
|
name: nodeInfo.name,
|
|
111
111
|
slot: outline.slot,
|
|
112
112
|
detail: nodeInfo.detail,
|
|
113
|
+
componentRef: nodeInfo.componentRef,
|
|
114
|
+
componentId: nodeInfo.componentId,
|
|
113
115
|
triggers,
|
|
114
116
|
children,
|
|
115
117
|
};
|
|
@@ -118,7 +120,7 @@ outline) {
|
|
|
118
120
|
// Tool registration
|
|
119
121
|
// ---------------------------------------------------------------------------
|
|
120
122
|
export function registerGetComponentSummaryTool(server) {
|
|
121
|
-
server.tool("get_component_summary", "Get a readable summary of a FlutterFlow component from local cache — widget tree, actions, params. No API calls. Run sync_project first if not cached.", {
|
|
123
|
+
server.tool("get_component_summary", "Get a readable summary of a FlutterFlow component from local cache — widget tree, actions, params. Nested component references are resolved to show [ComponentName] (ComponentId). No API calls. Run sync_project first if not cached.", {
|
|
122
124
|
projectId: z.string().describe("The FlutterFlow project ID"),
|
|
123
125
|
componentName: z
|
|
124
126
|
.string()
|