flutterflow-mcp 0.2.3 → 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 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);
@@ -2,19 +2,10 @@ 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
  */
@@ -2,18 +2,10 @@ 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"
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetApiEndpointsTool(server: McpServer): void;
@@ -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,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetAppStateTool(server: McpServer): void;
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetCustomCodeTool(server: McpServer): void;
@@ -0,0 +1,284 @@
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
+ import { resolveDataType } from "../utils/resolve-data-type.js";
6
+ // ---------------------------------------------------------------------------
7
+ // Helpers
8
+ // ---------------------------------------------------------------------------
9
+ function formatArg(arg) {
10
+ return arg.required ? `${arg.name}: ${arg.type} (required)` : `${arg.name}: ${arg.type}?`;
11
+ }
12
+ function formatAction(action) {
13
+ const lines = [];
14
+ lines.push(`### ${action.name}`);
15
+ lines.push(`Key: ${action.key} | File: \`${action.fileKey}\``);
16
+ if (action.args.length === 0) {
17
+ lines.push("Args: (none)");
18
+ }
19
+ else {
20
+ lines.push(`Args: ${action.args.map(formatArg).join(", ")}`);
21
+ }
22
+ lines.push(`Returns: ${action.returnType ?? "void"}`);
23
+ lines.push(`Context: ${action.includeContext ? "Yes" : "No"}`);
24
+ if (action.code !== undefined) {
25
+ lines.push("");
26
+ lines.push("```dart");
27
+ lines.push(action.code);
28
+ lines.push("```");
29
+ }
30
+ return lines.join("\n");
31
+ }
32
+ function formatFunction(fn) {
33
+ const lines = [];
34
+ lines.push(`### ${fn.name}`);
35
+ lines.push(`Key: ${fn.key} | File: \`${fn.fileKey}\``);
36
+ if (fn.args.length === 0) {
37
+ lines.push("Args: (none)");
38
+ }
39
+ else {
40
+ lines.push(`Args: ${fn.args.map(formatArg).join(", ")}`);
41
+ }
42
+ lines.push(`Returns: ${fn.returnType ?? "void"}`);
43
+ if (fn.code !== undefined) {
44
+ lines.push("");
45
+ lines.push("```dart");
46
+ lines.push(fn.code);
47
+ lines.push("```");
48
+ }
49
+ return lines.join("\n");
50
+ }
51
+ function formatWidget(widget) {
52
+ const lines = [];
53
+ lines.push(`### ${widget.name}`);
54
+ lines.push(`Key: ${widget.key} | File: \`${widget.fileKey}\``);
55
+ if (widget.params.length === 0) {
56
+ lines.push("Params: (none)");
57
+ }
58
+ else {
59
+ lines.push("Params:");
60
+ for (const p of widget.params) {
61
+ const suffix = p.required ? " (required)" : "";
62
+ lines.push(` - ${p.name}: ${p.type}${suffix}`);
63
+ }
64
+ }
65
+ if (widget.code !== undefined) {
66
+ lines.push("");
67
+ lines.push("```dart");
68
+ lines.push(widget.code);
69
+ lines.push("```");
70
+ }
71
+ return lines.join("\n");
72
+ }
73
+ function formatAgent(agent) {
74
+ const lines = [];
75
+ lines.push(`### ${agent.displayName} [${agent.status}]`);
76
+ lines.push(`Key: ${agent.key} | File: \`${agent.fileKey}\``);
77
+ lines.push(`Provider: ${agent.provider} (${agent.model})`);
78
+ lines.push(`Input: ${agent.requestTypes.join(", ")}`);
79
+ lines.push(`Output: ${agent.responseType}`);
80
+ if (agent.description) {
81
+ lines.push(`Description: ${agent.description}`);
82
+ }
83
+ return lines.join("\n");
84
+ }
85
+ function resolveReturnType(returnParam) {
86
+ if (!returnParam)
87
+ return null;
88
+ const dt = returnParam.dataType;
89
+ if (!dt)
90
+ return null;
91
+ const resolved = resolveDataType(dt);
92
+ const nonNullable = dt.nonNullable ?? false;
93
+ return nonNullable ? `${resolved} (required)` : `${resolved}?`;
94
+ }
95
+ function parseArgs(rawArgs) {
96
+ if (!Array.isArray(rawArgs))
97
+ return [];
98
+ return rawArgs.map((arg) => {
99
+ const id = arg.identifier;
100
+ const name = id?.name || "unknown";
101
+ const dt = arg.dataType || {};
102
+ const type = resolveDataType(dt);
103
+ const required = dt.nonNullable ?? false;
104
+ return { name, type, required };
105
+ });
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Category processors
109
+ // ---------------------------------------------------------------------------
110
+ async function processActions(projectId, nameFilter, includeCode) {
111
+ const allKeys = await listCachedKeys(projectId, "custom-actions/id-");
112
+ const topKeys = allKeys.filter((k) => /^custom-actions\/id-[a-z0-9]+$/i.test(k));
113
+ return batchProcess(topKeys, 10, async (fileKey) => {
114
+ const content = await cacheRead(projectId, fileKey);
115
+ if (!content)
116
+ return null;
117
+ const doc = YAML.parse(content);
118
+ const id = doc.identifier;
119
+ const name = id?.name || "unknown";
120
+ if (nameFilter && name.toLowerCase() !== nameFilter.toLowerCase())
121
+ return null;
122
+ const idKey = id?.key || fileKey.match(/id-([a-z0-9]+)$/i)?.[1] || "unknown";
123
+ const args = parseArgs(doc.arguments);
124
+ const returnType = resolveReturnType(doc.returnParameter);
125
+ const includeContext = doc.includeContext ?? false;
126
+ let code;
127
+ if (includeCode) {
128
+ const codeContent = await cacheRead(projectId, `custom-actions/id-${idKey}/action-code.dart`);
129
+ if (codeContent)
130
+ code = codeContent;
131
+ }
132
+ return { name, key: idKey, fileKey, args, returnType, includeContext, code };
133
+ }).then((results) => results.filter((r) => r !== null));
134
+ }
135
+ async function processFunctions(projectId, nameFilter, includeCode) {
136
+ const allKeys = await listCachedKeys(projectId, "custom-functions/id-");
137
+ const topKeys = allKeys.filter((k) => /^custom-functions\/id-[a-z0-9]+$/i.test(k));
138
+ return batchProcess(topKeys, 10, async (fileKey) => {
139
+ const content = await cacheRead(projectId, fileKey);
140
+ if (!content)
141
+ return null;
142
+ const doc = YAML.parse(content);
143
+ const id = doc.identifier;
144
+ const name = id?.name || "unknown";
145
+ if (nameFilter && name.toLowerCase() !== nameFilter.toLowerCase())
146
+ return null;
147
+ const idKey = id?.key || fileKey.match(/id-([a-z0-9]+)$/i)?.[1] || "unknown";
148
+ const args = parseArgs(doc.arguments);
149
+ const returnType = resolveReturnType(doc.returnParameter);
150
+ let code;
151
+ if (includeCode) {
152
+ const codeContent = await cacheRead(projectId, `custom-functions/id-${idKey}/function-code.dart`);
153
+ if (codeContent)
154
+ code = codeContent;
155
+ }
156
+ return { name, key: idKey, fileKey, args, returnType, code };
157
+ }).then((results) => results.filter((r) => r !== null));
158
+ }
159
+ async function processWidgets(projectId, nameFilter, includeCode) {
160
+ const allKeys = await listCachedKeys(projectId, "custom-widgets/id-");
161
+ const topKeys = allKeys.filter((k) => /^custom-widgets\/id-[a-z0-9]+$/i.test(k));
162
+ return batchProcess(topKeys, 10, async (fileKey) => {
163
+ const content = await cacheRead(projectId, fileKey);
164
+ if (!content)
165
+ return null;
166
+ const doc = YAML.parse(content);
167
+ const id = doc.identifier;
168
+ const name = id?.name || "unknown";
169
+ if (nameFilter && name.toLowerCase() !== nameFilter.toLowerCase())
170
+ return null;
171
+ const idKey = id?.key || fileKey.match(/id-([a-z0-9]+)$/i)?.[1] || "unknown";
172
+ const rawParams = doc.parameters;
173
+ const params = parseArgs(rawParams);
174
+ const description = doc.description || "";
175
+ let code;
176
+ if (includeCode) {
177
+ const codeContent = await cacheRead(projectId, `custom-widgets/id-${idKey}/widget-code.dart`);
178
+ if (codeContent)
179
+ code = codeContent;
180
+ }
181
+ return { name, key: idKey, fileKey, params, description, code };
182
+ }).then((results) => results.filter((r) => r !== null));
183
+ }
184
+ async function processAgents(projectId, nameFilter) {
185
+ const allKeys = await listCachedKeys(projectId, "agent/id-");
186
+ const topKeys = allKeys.filter((k) => /^agent\/id-[a-z0-9]+$/i.test(k));
187
+ return batchProcess(topKeys, 10, async (fileKey) => {
188
+ const content = await cacheRead(projectId, fileKey);
189
+ if (!content)
190
+ return null;
191
+ const doc = YAML.parse(content);
192
+ const id = doc.identifier;
193
+ const identifierName = id?.name || "unknown";
194
+ if (nameFilter && identifierName.toLowerCase() !== nameFilter.toLowerCase())
195
+ return null;
196
+ const idKey = id?.key || fileKey.match(/id-([a-z0-9]+)$/i)?.[1] || "unknown";
197
+ const displayName = doc.name || identifierName;
198
+ const status = doc.status || "UNKNOWN";
199
+ const aiModel = doc.aiModel;
200
+ const provider = aiModel?.provider || "UNKNOWN";
201
+ const model = aiModel?.model || "unknown";
202
+ const reqOpts = doc.requestOptions;
203
+ const requestTypes = reqOpts?.requestTypes || [];
204
+ const resOpts = doc.responseOptions;
205
+ const responseType = resOpts?.responseType || "UNKNOWN";
206
+ const description = doc.description || "";
207
+ return { name: identifierName, key: idKey, fileKey, displayName, status, provider, model, requestTypes, responseType, description };
208
+ }).then((results) => results.filter((r) => r !== null));
209
+ }
210
+ // ---------------------------------------------------------------------------
211
+ // Tool registration
212
+ // ---------------------------------------------------------------------------
213
+ export function registerGetCustomCodeTool(server) {
214
+ server.tool("get_custom_code", "Get custom actions, functions, widgets, and AI agents from local cache — signatures, arguments, return types, and optionally Dart source code. No API calls. Run sync_project first if not cached.", {
215
+ projectId: z.string().describe("The FlutterFlow project ID"),
216
+ type: z
217
+ .enum(["actions", "functions", "widgets", "agents", "all"])
218
+ .optional()
219
+ .default("all")
220
+ .describe("Type of custom code to retrieve"),
221
+ name: z
222
+ .string()
223
+ .optional()
224
+ .describe("Case-insensitive filter on identifier name"),
225
+ includeCode: z
226
+ .boolean()
227
+ .optional()
228
+ .default(false)
229
+ .describe("Include Dart source code in output"),
230
+ }, async ({ projectId, type, name, includeCode }) => {
231
+ const meta = await cacheMeta(projectId);
232
+ if (!meta) {
233
+ return {
234
+ content: [
235
+ {
236
+ type: "text",
237
+ text: `No cache found for project "${projectId}". Run sync_project first to download the project YAML files.`,
238
+ },
239
+ ],
240
+ };
241
+ }
242
+ const categories = type === "all"
243
+ ? ["actions", "functions", "widgets", "agents"]
244
+ : [type];
245
+ const sections = [];
246
+ for (const cat of categories) {
247
+ if (cat === "actions") {
248
+ const items = await processActions(projectId, name, includeCode);
249
+ if (items.length > 0) {
250
+ sections.push(`## Custom Actions (${items.length})\n\n${items.map(formatAction).join("\n\n")}`);
251
+ }
252
+ }
253
+ else if (cat === "functions") {
254
+ const items = await processFunctions(projectId, name, includeCode);
255
+ if (items.length > 0) {
256
+ sections.push(`## Custom Functions (${items.length})\n\n${items.map(formatFunction).join("\n\n")}`);
257
+ }
258
+ }
259
+ else if (cat === "widgets") {
260
+ const items = await processWidgets(projectId, name, includeCode);
261
+ if (items.length > 0) {
262
+ sections.push(`## Custom Widgets (${items.length})\n\n${items.map(formatWidget).join("\n\n")}`);
263
+ }
264
+ }
265
+ else if (cat === "agents") {
266
+ const items = await processAgents(projectId, name);
267
+ if (items.length > 0) {
268
+ sections.push(`## AI Agents (${items.length})\n\n${items.map(formatAgent).join("\n\n")}`);
269
+ }
270
+ }
271
+ }
272
+ if (sections.length === 0) {
273
+ return {
274
+ content: [
275
+ { type: "text", text: "No custom code found in cache." },
276
+ ],
277
+ };
278
+ }
279
+ const output = `# Custom Code\n\n${sections.join("\n\n")}`;
280
+ return {
281
+ content: [{ type: "text", text: output }],
282
+ };
283
+ });
284
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetDataModelsTool(server: McpServer): void;