flutterflow-mcp 0.3.0 → 0.3.1

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.
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys, } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys, } from "../utils/cache.js";
4
4
  import { resolveComponent } from "./get-component-summary.js";
5
5
  import { batchProcess } from "../utils/batch-process.js";
6
6
  // ---------------------------------------------------------------------------
@@ -210,7 +210,7 @@ export function registerFindComponentUsagesTool(server) {
210
210
  }
211
211
  }
212
212
  return {
213
- content: [{ type: "text", text: lines.join("\n") }],
213
+ content: [{ type: "text", text: lines.join("\n") + cacheAgeFooter(meta) }],
214
214
  };
215
215
  });
216
216
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys, } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys, } from "../utils/cache.js";
4
4
  import { resolvePage } from "./get-page-summary.js";
5
5
  import { batchProcess } from "../utils/batch-process.js";
6
6
  // ---------------------------------------------------------------------------
@@ -214,7 +214,7 @@ export function registerFindPageNavigationsTool(server) {
214
214
  }
215
215
  }
216
216
  return {
217
- content: [{ type: "text", text: lines.join("\n") }],
217
+ content: [{ type: "text", text: lines.join("\n") + cacheAgeFooter(meta) }],
218
218
  };
219
219
  });
220
220
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys } from "../utils/cache.js";
4
4
  import { batchProcess } from "../utils/batch-process.js";
5
5
  function parseEndpoint(content) {
6
6
  const doc = YAML.parse(content);
@@ -120,7 +120,7 @@ export function registerGetApiEndpointsTool(server) {
120
120
  endpoints = filtered;
121
121
  }
122
122
  return {
123
- content: [{ type: "text", text: formatEndpoints(endpoints) }],
123
+ content: [{ type: "text", text: formatEndpoints(endpoints) + cacheAgeFooter(meta) }],
124
124
  };
125
125
  });
126
126
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  async function resolvePageName(projectId, scaffoldId) {
5
5
  const content = await cacheRead(projectId, `page/id-${scaffoldId}`);
6
6
  if (!content)
@@ -163,7 +163,7 @@ export function registerGetAppSettingsTool(server) {
163
163
  sections.push(`(not configured)`);
164
164
  }
165
165
  return {
166
- content: [{ type: "text", text: sections.join("\n") }],
166
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
167
167
  };
168
168
  });
169
169
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  import { resolveDataType } from "../utils/resolve-data-type.js";
5
5
  export function registerGetAppStateTool(server) {
6
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.", {
@@ -90,7 +90,7 @@ export function registerGetAppStateTool(server) {
90
90
  }
91
91
  }
92
92
  return {
93
- content: [{ type: "text", text: sections.join("\n") }],
93
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
94
94
  };
95
95
  });
96
96
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys, } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys, } from "../utils/cache.js";
4
4
  import { parseTreeOutline } from "../utils/page-summary/tree-walker.js";
5
5
  import { extractNodeInfo } from "../utils/page-summary/node-extractor.js";
6
6
  import { summarizeTriggers } from "../utils/page-summary/action-summarizer.js";
@@ -189,7 +189,7 @@ export function registerGetComponentSummaryTool(server) {
189
189
  // Format output
190
190
  const summary = formatComponentSummary(componentMeta, enrichedTree);
191
191
  return {
192
- content: [{ type: "text", text: summary }],
192
+ content: [{ type: "text", text: summary + cacheAgeFooter(meta) }],
193
193
  };
194
194
  });
195
195
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys } from "../utils/cache.js";
4
4
  import { batchProcess } from "../utils/batch-process.js";
5
5
  import { resolveDataType } from "../utils/resolve-data-type.js";
6
6
  // ---------------------------------------------------------------------------
@@ -374,7 +374,7 @@ export function registerGetCustomCodeTool(server) {
374
374
  }
375
375
  const output = `# Custom Code\n\n${sections.join("\n\n")}`;
376
376
  return {
377
- content: [{ type: "text", text: output }],
377
+ content: [{ type: "text", text: output + cacheAgeFooter(meta) }],
378
378
  };
379
379
  });
380
380
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys } from "../utils/cache.js";
4
4
  import { batchProcess } from "../utils/batch-process.js";
5
5
  import { resolveDataType } from "../utils/resolve-data-type.js";
6
6
  async function readStructs(projectId, nameFilter) {
@@ -260,7 +260,7 @@ export function registerGetDataModelsTool(server) {
260
260
  }
261
261
  const output = formatOutput(structs, enums, collections, supabaseTables, name);
262
262
  return {
263
- content: [{ type: "text", text: output }],
263
+ content: [{ type: "text", text: output + cacheAgeFooter(meta) }],
264
264
  };
265
265
  });
266
266
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  async function resolvePageName(projectId, scaffoldId) {
5
5
  const content = await cacheRead(projectId, `page/id-${scaffoldId}`);
6
6
  if (!content)
@@ -110,7 +110,7 @@ export function registerGetGeneralSettingsTool(server) {
110
110
  sections.push(`\n## Nav Bar & App Bar`, `(not configured)`);
111
111
  }
112
112
  return {
113
- content: [{ type: "text", text: sections.join("\n") }],
113
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
114
114
  };
115
115
  });
116
116
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  const PROVIDERS = [
5
5
  { key: "stripe", label: "Stripe" },
6
6
  { key: "braintree", label: "Braintree" },
@@ -45,7 +45,7 @@ export function registerGetInAppPurchasesTool(server) {
45
45
  }
46
46
  }
47
47
  return {
48
- content: [{ type: "text", text: sections.join("\n") }],
48
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
49
49
  };
50
50
  });
51
51
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  /**
5
5
  * Fields that should never be output — API keys, secrets, tokens, etc.
6
6
  * Only IDs and non-sensitive configuration are shown.
@@ -131,7 +131,7 @@ export function registerGetIntegrationsTool(server) {
131
131
  sections.push(lines.join("\n"));
132
132
  }
133
133
  return {
134
- content: [{ type: "text", text: sections.join("\n") }],
134
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
135
135
  };
136
136
  });
137
137
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta, listCachedKeys, } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter, listCachedKeys, } from "../utils/cache.js";
4
4
  import { parseFolderMapping } from "../utils/parse-folders.js";
5
5
  import { parseTreeOutline } from "../utils/page-summary/tree-walker.js";
6
6
  import { extractNodeInfo } from "../utils/page-summary/node-extractor.js";
@@ -199,7 +199,7 @@ export function registerGetPageSummaryTool(server) {
199
199
  // Format output
200
200
  const summary = formatPageSummary(pageMeta, enrichedTree);
201
201
  return {
202
- content: [{ type: "text", text: summary }],
202
+ content: [{ type: "text", text: summary + cacheAgeFooter(meta) }],
203
203
  };
204
204
  });
205
205
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  import { resolveDataType } from "../utils/resolve-data-type.js";
5
5
  export function registerGetProjectSetupTool(server) {
6
6
  server.tool("get_project_setup", "Get Project Setup settings — Firebase services, Languages, Platforms, Permissions, Project Dependencies, Dev Environments. Mirrors the FlutterFlow 'Project Setup' settings section. Cache-based, no API calls. Run sync_project first.", {
@@ -206,7 +206,7 @@ export function registerGetProjectSetupTool(server) {
206
206
  sections.push(`(not configured)`);
207
207
  }
208
208
  return {
209
- content: [{ type: "text", text: sections.join("\n") }],
209
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
210
210
  };
211
211
  });
212
212
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import YAML from "yaml";
3
- import { cacheRead, cacheMeta } from "../utils/cache.js";
3
+ import { cacheRead, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
4
4
  function argbToHex(argbStr) {
5
5
  const n = parseInt(argbStr, 10);
6
6
  if (isNaN(n))
@@ -122,7 +122,7 @@ export function registerGetThemeTool(server) {
122
122
  }
123
123
  }
124
124
  return {
125
- content: [{ type: "text", text: sections.join("\n") }],
125
+ content: [{ type: "text", text: sections.join("\n") + cacheAgeFooter(meta) }],
126
126
  };
127
127
  });
128
128
  }
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { listCachedKeys, cacheMeta } from "../utils/cache.js";
2
+ import { listCachedKeys, cacheMeta, cacheAgeFooter } from "../utils/cache.js";
3
3
  export function registerListFilesTool(server, client) {
4
4
  server.tool("list_project_files", "List all YAML file names in a FlutterFlow project. Supports optional prefix filter (e.g. 'page/', 'component/') to narrow results.", {
5
5
  projectId: z.string().describe("The FlutterFlow project ID"),
@@ -15,7 +15,7 @@ export function registerListFilesTool(server, client) {
15
15
  content: [
16
16
  {
17
17
  type: "text",
18
- text: JSON.stringify({ value: { file_names: keys }, source: "cache", syncedAt: meta.lastSyncedAt }, null, 2),
18
+ text: JSON.stringify({ value: { file_names: keys }, source: "cache", syncedAt: meta.lastSyncedAt }, null, 2) + cacheAgeFooter(meta),
19
19
  },
20
20
  ],
21
21
  };
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { cacheMeta, listCachedKeys } from "../utils/cache.js";
2
+ import { cacheMeta, listCachedKeys, cacheAgeFooter } from "../utils/cache.js";
3
3
  const MAX_RESULTS = 100;
4
4
  export function registerSearchProjectFilesTool(server) {
5
5
  server.tool("search_project_files", "Search cached project file keys by keyword, prefix, or regex. Returns matching file keys for use with get_project_yaml. Cache-only, no API calls. Run sync_project first.", {
@@ -63,7 +63,7 @@ export function registerSearchProjectFilesTool(server) {
63
63
  lines.push(`- ${key}`);
64
64
  }
65
65
  return {
66
- content: [{ type: "text", text: lines.join("\n") }],
66
+ content: [{ type: "text", text: lines.join("\n") + cacheAgeFooter(meta) }],
67
67
  };
68
68
  });
69
69
  }
@@ -51,3 +51,8 @@ export declare function cacheWriteMeta(projectId: string, meta: CacheMeta): Prom
51
51
  * the path relative to the project cache dir.
52
52
  */
53
53
  export declare function listCachedKeys(projectId: string, prefix?: string): Promise<string[]>;
54
+ /**
55
+ * Format a human-readable footer indicating cache age.
56
+ * Appended to cache-based tool responses so the AI can judge staleness.
57
+ */
58
+ export declare function cacheAgeFooter(meta: CacheMeta): string;
@@ -148,6 +148,28 @@ export async function listCachedKeys(projectId, prefix) {
148
148
  return yamlKeys;
149
149
  }
150
150
  // ---------------------------------------------------------------------------
151
+ // Cache age footer
152
+ // ---------------------------------------------------------------------------
153
+ /**
154
+ * Format a human-readable footer indicating cache age.
155
+ * Appended to cache-based tool responses so the AI can judge staleness.
156
+ */
157
+ export function cacheAgeFooter(meta) {
158
+ const syncedAt = new Date(meta.lastSyncedAt);
159
+ const diffMs = Date.now() - syncedAt.getTime();
160
+ const diffMin = Math.floor(diffMs / 60_000);
161
+ let ago;
162
+ if (diffMin < 1)
163
+ ago = "just now";
164
+ else if (diffMin < 60)
165
+ ago = `${diffMin} min ago`;
166
+ else if (diffMin < 1440)
167
+ ago = `${Math.floor(diffMin / 60)}h ${diffMin % 60}m ago`;
168
+ else
169
+ ago = `${Math.floor(diffMin / 1440)}d ago`;
170
+ return `\n\n---\n_Synced: ${meta.lastSyncedAt} (${ago}). Call sync_project to refresh._`;
171
+ }
172
+ // ---------------------------------------------------------------------------
151
173
  // Internal helpers
152
174
  // ---------------------------------------------------------------------------
153
175
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flutterflow-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "MCP server for the FlutterFlow Project API — AI-assisted FlutterFlow development through Claude and other MCP-compatible clients",
5
5
  "type": "module",
6
6
  "main": "build/index.js",