flutterflow-mcp 0.3.4 → 0.3.6

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 CHANGED
@@ -263,9 +263,19 @@ This MCP ships with a comprehensive FlutterFlow YAML reference catalog that AI m
263
263
 
264
264
  See [docs/ff-yaml/](docs/ff-yaml/) for the full catalog.
265
265
 
266
+ ## AI Agent Skill
267
+
268
+ This MCP includes a [skills.sh](https://skills.sh)-compatible skill for AI agents. Install it to teach your AI assistant how to use FlutterFlow MCP effectively:
269
+
270
+ ```bash
271
+ npx skills add mohn93/ff-mcp --skill flutterflow-mcp
272
+ ```
273
+
274
+ Compatible with Claude Code, Cursor, GitHub Copilot, Codex, Goose, Windsurf, and 12+ other AI agents. The skill provides tool orchestration workflows, critical YAML rules, and detailed reference documentation for widgets, actions, data binding, theming, and more.
275
+
266
276
  ## Claude Code Skills
267
277
 
268
- Copy skills from [`skills/`](skills/) into your project's `.claude/skills/` directory:
278
+ You can also copy Claude Code-specific skills from [`skills/`](skills/) into your project's `.claude/skills/` directory:
269
279
 
270
280
  - **`ff-yaml-dev.md`** — Core workflow: reading, editing, and creating FlutterFlow pages/components
271
281
  - **`ff-widget-patterns.md`** — Quick reference for common widget YAML patterns and snippets
@@ -84,6 +84,13 @@ export const TOPIC_MAP = {
84
84
  parametervalues: "03-components.md",
85
85
  callback: "03-components.md",
86
86
  executecallbackaction: "03-components.md",
87
+ // Internationalization
88
+ translation: "01-project-files.md",
89
+ translations: "01-project-files.md",
90
+ i18n: "01-project-files.md",
91
+ localization: "01-project-files.md",
92
+ translatabletext: "01-project-files.md",
93
+ languages: "01-project-files.md",
87
94
  // Universal patterns
88
95
  inputvalue: "README.md",
89
96
  mostrecentinputvalue: "README.md",
@@ -36,7 +36,7 @@ Every FlutterFlow project is a collection of YAML files accessed via file keys.
36
36
  | `enums/id-XXX` | Enum definition with named values |
37
37
  | `collections/id-XXX` | Firestore collection schema: fields, types, subcollections |
38
38
  | `agent/id-XXX` | AI agent configuration |
39
- | `custom-file/id-<TYPE>` | Custom file configs for native platform files. Known types: `MAIN` (main.dart startup actions), `ANDROID_MANIFEST` (XML injection hooks for AndroidManifest.xml), `PROGUARD` (ProGuard rule injection for proguard-rules.pro), `BUILD_GRADLE` (Gradle plugin/dependency/repository injection for build.gradle). Only appear after enabled in FF editor. Sub-file `custom-file/id-MAIN/custom-file-code.dart` contains generated Dart source. |
39
+ | `custom-file/id-<TYPE>` | Custom file configs for native platform files. Known types: `MAIN` (main.dart startup actions), `ANDROID_MANIFEST` (XML injection hooks for AndroidManifest.xml), `INFO_PLIST` (plist property injection for Info.plist), `ENTITLEMENTS` (iOS capability entitlements for Runner.entitlements), `APP_DELEGATE` (Swift code injection for AppDelegate.swift), `PROGUARD` (ProGuard rule injection for proguard-rules.pro), `BUILD_GRADLE` (Gradle plugin/dependency/repository injection for build.gradle). Only appear after enabled in FF editor. Sub-file `custom-file/id-<TYPE>/custom-file-code.dart` contains generated source. |
40
40
  | `environment-settings` | Per-environment configuration values (API URLs, keys) |
41
41
  | `dependencies` | FlutterFlow library package dependencies |
42
42
  | `custom-code-dependencies` | Dart/Flutter pub dependencies for custom code |
@@ -444,6 +444,69 @@ persistLanguageSelection: true # Remember user's language choice
444
444
 
445
445
  ---
446
446
 
447
+ ## languages/translation/id-{key} (Translation Files)
448
+
449
+ **Purpose:** Individual translation entries for user-defined translatable strings. Each file maps a unique key to translated text across all supported languages.
450
+
451
+ **File key pattern:** `languages/translation/id-<key>` (e.g., `languages/translation/id-ttk654j0`)
452
+
453
+ **Schema:**
454
+ ```yaml
455
+ translationIdentifier:
456
+ key: ttk654j0 # Unique translation key (8-char alphanumeric)
457
+ translations:
458
+ - language:
459
+ language: en # ISO 639-1 code (must match languages.yaml)
460
+ text: Continue # Translated text for this language
461
+ - language:
462
+ language: es
463
+ text: Continuar
464
+ isFixed: false # false = user-editable, true = system/preset string
465
+ ```
466
+
467
+ **Key fields:**
468
+
469
+ | Field | Type | Notes |
470
+ |---|---|---|
471
+ | `translationIdentifier.key` | string | Unique key matching the file key suffix. Referenced by widgets and parameter passes. |
472
+ | `translations` | list | One entry per language. Must include all languages from `languages.yaml`. |
473
+ | `translations[].language.language` | string | ISO 639-1 code (`en`, `es`, `fr`, etc.) |
474
+ | `translations[].text` | string | Translated text. Empty string `""` = untranslated (falls back to primary language). |
475
+ | `isFixed` | bool | `true` for system preset strings, `false` for user-defined translations. |
476
+
477
+ ### Where translation keys are referenced
478
+
479
+ Translation keys appear in two contexts:
480
+
481
+ **1. On Text widgets** — via `translationIdentifier` in the `text` prop:
482
+ ```yaml
483
+ # Widget-level translation (static, per-widget)
484
+ text:
485
+ translationIdentifier:
486
+ key: ttk654j0
487
+ textValue:
488
+ inputValue: Continue # English default / editor preview
489
+ ```
490
+
491
+ **2. On component parameter passes** — via `translatableText` inside `inputValue`:
492
+ ```yaml
493
+ # Parameter-level translation (per-instance, used when passing
494
+ # translated strings to component parameters)
495
+ paramIdentifier:
496
+ name: title
497
+ key: p1titl
498
+ inputValue:
499
+ translatableText:
500
+ translationIdentifier:
501
+ key: ms01ttl1
502
+ textValue:
503
+ inputValue: Histamine / Low DAO # English default
504
+ ```
505
+
506
+ > **Important:** The `translatableText` wrapper is the correct way to pass translated strings as component parameters. Do NOT use `translationIdentifier` directly on the parameterPass (it will fail validation with "Unknown field name"). Instead, nest it inside `inputValue.translatableText`.
507
+
508
+ ---
509
+
447
510
  ## app-state.yaml
448
511
 
449
512
  **Purpose:** App-level state variables (global state accessible from any page/component).
@@ -1242,6 +1305,363 @@ parameters: # Template variables referenced in ho
1242
1305
 
1243
1306
  ---
1244
1307
 
1308
+ ## custom-file/id-INFO_PLIST
1309
+
1310
+ **Purpose:** Allows injecting custom properties into the generated `Info.plist` for iOS builds, and defining template variables for dynamic values. Like `ANDROID_MANIFEST`, this is an abstraction layer — you define "hooks" (plist property injection) and "parameters" (template variables).
1311
+
1312
+ > **This file only exists when it has content.** If all hooks and parameters are removed (via UI or API), the file disappears from the server (API returns 404). It reappears when a user adds a hook or parameter in the FF editor.
1313
+
1314
+ > **WARNING: Pushing any `custom-file` deletes siblings.** The API treats all `custom-file/id-*` keys as a single collection. Pushing this file alone will delete all other custom files (MAIN, ANDROID_MANIFEST, PROGUARD, BUILD_GRADLE, etc.). **Always include all existing `custom-file` entries in the same push payload.** See [API Limitation #10](../flutterflow-api-limitations.md#10-pushing-one-custom-file-deletes-all-other-custom-file-entries).
1315
+
1316
+ > **Important — this is NOT the Info.plist itself.**
1317
+ > This is FlutterFlow's configuration that controls **property injection into the generated Info.plist** at build time. The file only appears after a user adds at least one property in the FF editor (Settings > Custom Code > Custom Files > Info.plist).
1318
+
1319
+ **Top-level keys:**
1320
+ - `type`
1321
+ - `identifier`
1322
+ - `hooks`
1323
+ - `parameters`
1324
+
1325
+ ### Hook Types
1326
+
1327
+ Only one hook type:
1328
+
1329
+ | Hook Type | Where it injects in Info.plist |
1330
+ |---|---|
1331
+ | `INFO_PLIST_PROPERTY` | Inside the root `<dict>` — use for adding `<key>`/`<value>` pairs (strings, booleans, arrays, etc.) |
1332
+
1333
+ ### Parameters (Template Variables)
1334
+
1335
+ Same pattern as ANDROID_MANIFEST — parameters are template variables that can be referenced in hook `content` using `{{variableName}}` syntax. Values can come from literal input or environment variables.
1336
+
1337
+ ### Schema
1338
+
1339
+ ```yaml
1340
+ type: INFO_PLIST
1341
+ identifier:
1342
+ name: Info.plist
1343
+
1344
+ hooks: # List of plist property injection hooks
1345
+ - type: INFO_PLIST_PROPERTY # Only hook type for Info.plist
1346
+ identifier:
1347
+ name: livetag # Human-readable hook name
1348
+ key: gh2m0b # Unique 6-char alphanumeric key
1349
+ content: "<key>live</key>\n<string>value</string>" # Raw plist XML to inject
1350
+
1351
+ parameters: # Template variables
1352
+ zini8l: # Parameter key
1353
+ parameter:
1354
+ identifier:
1355
+ name: fsdafd # Variable name — use {{fsdafd}} in hook content
1356
+ dataType:
1357
+ scalarType: String # Data type (String, Integer, etc.)
1358
+ value:
1359
+ inputValue:
1360
+ serializedValue: safdsaf # Literal value
1361
+ ```
1362
+
1363
+ **Key fields:**
1364
+
1365
+ | Field | Type | Notes |
1366
+ |---|---|---|
1367
+ | `type` | string | Always `INFO_PLIST` for this file. |
1368
+ | `identifier.name` | string | Always `Info.plist`. |
1369
+ | `hooks` | list | List of plist property injection hooks. Each hook injects a key-value pair into the root `<dict>`. |
1370
+ | `hooks[].type` | enum | Always `INFO_PLIST_PROPERTY`. |
1371
+ | `hooks[].identifier.name` | string | Human-readable hook name. |
1372
+ | `hooks[].identifier.key` | string | Unique 6-char alphanumeric key. |
1373
+ | `hooks[].content` | string | Raw plist XML to inject. Typically `<key>name</key>\n<string>value</string>` or similar plist entries. Must be properly escaped in YAML. |
1374
+ | `parameters` | map | Map of parameter key to parameter definition. |
1375
+ | `parameters.<key>.parameter.identifier.name` | string | Variable name. Referenced in hook `content` as `{{variableName}}`. |
1376
+ | `parameters.<key>.parameter.dataType.scalarType` | enum | Data type (`String`, `Integer`, `Double`, `Boolean`, etc.). |
1377
+ | `parameters.<key>.value` | object | Value source — either `inputValue.serializedValue` (literal) or `variable` (environment reference). |
1378
+
1379
+ ### Parameter value sources
1380
+
1381
+ Parameters support two value sources:
1382
+
1383
+ **Literal value:**
1384
+ ```yaml
1385
+ value:
1386
+ inputValue:
1387
+ serializedValue: my-value # Hardcoded value
1388
+ ```
1389
+
1390
+ **Environment variable reference:**
1391
+ ```yaml
1392
+ value:
1393
+ variable:
1394
+ source: DEV_ENVIRONMENT
1395
+ baseVariable:
1396
+ environmentValue:
1397
+ identifier:
1398
+ name: myEnvVar # References environment-settings.yaml
1399
+ key: eji5p6
1400
+ ```
1401
+
1402
+ ### Hook content examples
1403
+
1404
+ **Simple string property:**
1405
+ ```yaml
1406
+ content: "<key>MyCustomKey</key>\n<string>MyCustomValue</string>"
1407
+ ```
1408
+
1409
+ **Boolean property:**
1410
+ ```yaml
1411
+ content: "<key>MyFeatureFlag</key>\n<true/>"
1412
+ ```
1413
+
1414
+ **Array property:**
1415
+ ```yaml
1416
+ content: "<key>LSApplicationQueriesSchemes</key>\n<array>\n <string>myapp</string>\n <string>myapp-dev</string>\n</array>"
1417
+ ```
1418
+
1419
+ **API capabilities:**
1420
+
1421
+ | Operation | Status | Notes |
1422
+ |---|---|---|
1423
+ | Reading config | Works | Full hook and parameter data returned. |
1424
+ | Adding hooks via API | Expected to work | Same structure as ANDROID_MANIFEST hooks. |
1425
+ | Adding parameters via API | Expected to work | Same structure as other custom file parameters. |
1426
+ | Hook name validation gap | Caution | Same as ANDROID_MANIFEST — avoid hyphens in names, use camelCase or spaces. |
1427
+
1428
+ ### Code sub-file (`custom-file/id-INFO_PLIST/custom-file-code.dart`)
1429
+
1430
+ Contains the full generated `Info.plist` XML. This is the complete plist including all standard iOS keys (bundle identifiers, permissions, URL schemes, etc.) plus any injected hook content. It is **read-only** — modifications should be made through the hooks/parameters config, not by editing the XML directly.
1431
+
1432
+ ---
1433
+
1434
+ ## custom-file/id-ENTITLEMENTS
1435
+
1436
+ **Purpose:** Allows injecting custom entitlements into the generated `Runner.entitlements` file for iOS builds. Entitlements declare app capabilities such as push notifications, App Groups, iCloud, associated domains, and other iOS-specific permissions.
1437
+
1438
+ > **This file only exists when it has content.** If all hooks and parameters are removed (via UI or API), the file disappears from the server (API returns 404). It reappears when a user adds an entitlement or parameter in the FF editor.
1439
+
1440
+ > **WARNING: Pushing any `custom-file` deletes siblings.** The API treats all `custom-file/id-*` keys as a single collection. Pushing this file alone will delete all other custom files (MAIN, ANDROID_MANIFEST, INFO_PLIST, etc.). **Always include all existing `custom-file` entries in the same push payload.** See [API Limitation #10](../flutterflow-api-limitations.md#10-pushing-one-custom-file-deletes-all-other-custom-file-entries).
1441
+
1442
+ > **Important — this is an abstraction layer.**
1443
+ > This config controls **entitlement injection into the generated `Runner.entitlements`** at build time. The file only appears after a user adds at least one entitlement in the FF editor (Settings > Custom Code > Custom Files > Runner.entitlements).
1444
+
1445
+ **Top-level keys:**
1446
+ - `type`
1447
+ - `identifier`
1448
+ - `hooks`
1449
+ - `parameters`
1450
+
1451
+ ### Hook Types
1452
+
1453
+ Only one hook type:
1454
+
1455
+ | Hook Type | Where it injects in Runner.entitlements |
1456
+ |---|---|
1457
+ | `ENTITLEMENT` | Inside the root `<dict>` of the entitlements plist — use for adding capability key-value pairs |
1458
+
1459
+ ### Schema
1460
+
1461
+ ```yaml
1462
+ type: ENTITLEMENTS
1463
+ identifier:
1464
+ name: Runner.entitlements
1465
+
1466
+ hooks:
1467
+ - type: ENTITLEMENT # Only hook type for entitlements
1468
+ identifier:
1469
+ name: environemnt # Human-readable hook name
1470
+ key: 5x527x # Unique 6-char alphanumeric key
1471
+ content: "<key>aps-environment</key>\n<string>development</string>" # Plist XML to inject
1472
+
1473
+ parameters: # Template variables — same as all other custom files
1474
+ f4cahu:
1475
+ parameter:
1476
+ identifier:
1477
+ name: variable1 # Variable name — use {{variable1}} in hook content
1478
+ dataType:
1479
+ scalarType: String
1480
+ value:
1481
+ inputValue:
1482
+ serializedValue: clear value # Default value
1483
+ ```
1484
+
1485
+ **Key fields:**
1486
+
1487
+ | Field | Type | Notes |
1488
+ |---|---|---|
1489
+ | `type` | string | Always `ENTITLEMENTS` for this file. |
1490
+ | `identifier.name` | string | Always `Runner.entitlements`. |
1491
+ | `hooks` | list | List of entitlement injection hooks. Each hook injects a capability key-value pair into the entitlements plist. |
1492
+ | `hooks[].type` | enum | Always `ENTITLEMENT`. |
1493
+ | `hooks[].identifier.name` | string | Human-readable name for the entitlement. |
1494
+ | `hooks[].identifier.key` | string | Unique 6-char alphanumeric key. |
1495
+ | `hooks[].content` | string | Raw plist XML to inject. Typically `<key>capability-name</key>` followed by a value (`<string>`, `<true/>`, `<array>`, etc.). Must be properly escaped in YAML. |
1496
+ | `parameters` | map | Template variables keyed by parameter ID. Use `{{variableName}}` in `content` to reference them. Same structure as all other custom files. |
1497
+
1498
+ ### Common entitlement content examples
1499
+
1500
+ **Push notifications (development):**
1501
+ ```yaml
1502
+ content: "<key>aps-environment</key>\n<string>development</string>"
1503
+ ```
1504
+
1505
+ **Push notifications (production):**
1506
+ ```yaml
1507
+ content: "<key>aps-environment</key>\n<string>production</string>"
1508
+ ```
1509
+
1510
+ **Associated domains (for universal links):**
1511
+ ```yaml
1512
+ content: "<key>com.apple.developer.associated-domains</key>\n<array>\n <string>applinks:example.com</string>\n</array>"
1513
+ ```
1514
+
1515
+ **App Groups:**
1516
+ ```yaml
1517
+ content: "<key>com.apple.security.application-groups</key>\n<array>\n <string>group.com.example.myapp</string>\n</array>"
1518
+ ```
1519
+
1520
+ ### API capabilities
1521
+
1522
+ | Operation | Status | Notes |
1523
+ |---|---|---|
1524
+ | Reading config | Works | Full hook and parameter data returned. |
1525
+ | Adding hooks via API | Expected to work | Same structure as other custom file hooks. |
1526
+ | Adding parameters via API | Expected to work | Same structure as other custom file parameters. |
1527
+ | Hook name validation gap | Caution | Same as other custom files — avoid hyphens in names, use camelCase or spaces. |
1528
+
1529
+ ---
1530
+
1531
+ ## custom-file/id-APP_DELEGATE
1532
+
1533
+ **Purpose:** Allows injecting custom Swift code into the generated `AppDelegate.swift` for iOS builds. Supports import statements and initialization code that runs during the iOS app launch sequence.
1534
+
1535
+ > **This file only exists when it has content.** If all hooks and parameters are removed (via UI or API), the file disappears from the server (API returns 404). It reappears when a user adds a hook or parameter in the FF editor.
1536
+
1537
+ > **WARNING: Pushing any `custom-file` deletes siblings.** The API treats all `custom-file/id-*` keys as a single collection. Pushing this file alone will delete all other custom files (MAIN, ANDROID_MANIFEST, INFO_PLIST, etc.). **Always include all existing `custom-file` entries in the same push payload.** See [API Limitation #10](../flutterflow-api-limitations.md#10-pushing-one-custom-file-deletes-all-other-custom-file-entries).
1538
+
1539
+ > **Important — this is an abstraction layer.**
1540
+ > This config controls **Swift code injection into the generated `AppDelegate.swift`** at build time. The file only appears after a user adds at least one hook in the FF editor (Settings > Custom Code > Custom Files > AppDelegate.swift).
1541
+
1542
+ **Top-level keys:**
1543
+ - `type`
1544
+ - `identifier`
1545
+ - `hooks`
1546
+ - `parameters`
1547
+
1548
+ ### Hook Types
1549
+
1550
+ Two hook types control where Swift code is injected in the generated `AppDelegate.swift`:
1551
+
1552
+ | Hook Type | Where it injects in AppDelegate.swift |
1553
+ |---|---|
1554
+ | `APP_DELEGATE_IMPORT_HOOK` | At the top of the file — use for `import` statements |
1555
+ | `APP_DELEGATE_INITIALIZATION_HOOK` | Inside `application(_:didFinishLaunchingWithOptions:)` — use for SDK init calls and setup code |
1556
+
1557
+ ### Schema
1558
+
1559
+ ```yaml
1560
+ type: APP_DELEGATE
1561
+ identifier:
1562
+ name: AppDelegate.swift
1563
+
1564
+ hooks:
1565
+ - type: APP_DELEGATE_IMPORT_HOOK # Injects at the top of the file (imports)
1566
+ identifier:
1567
+ name: UIkitimprot # Human-readable hook name
1568
+ key: rtmoen # Unique 6-char alphanumeric key
1569
+ content: import UIKit # Swift import statement
1570
+
1571
+ - type: APP_DELEGATE_INITIALIZATION_HOOK # Injects inside didFinishLaunchingWithOptions
1572
+ identifier:
1573
+ name: logsinit # Human-readable hook name
1574
+ key: 2b582i # Unique 6-char alphanumeric key
1575
+ content: Logs.init() # Swift initialization code
1576
+
1577
+ parameters: # Template variables — same as all other custom files
1578
+ 74q7xg:
1579
+ parameter:
1580
+ identifier:
1581
+ name: vars1 # Variable name — use {{vars1}} in hook content
1582
+ dataType:
1583
+ scalarType: String
1584
+ value:
1585
+ inputValue:
1586
+ serializedValue: var val # Default value
1587
+ ```
1588
+
1589
+ **Key fields:**
1590
+
1591
+ | Field | Type | Notes |
1592
+ |---|---|---|
1593
+ | `type` | string | Always `APP_DELEGATE` for this file. |
1594
+ | `identifier.name` | string | Always `AppDelegate.swift`. |
1595
+ | `hooks` | list | List of Swift code injection hooks. |
1596
+ | `hooks[].type` | enum | `APP_DELEGATE_IMPORT_HOOK` (imports) or `APP_DELEGATE_INITIALIZATION_HOOK` (init code). |
1597
+ | `hooks[].identifier.name` | string | Human-readable name for the hook. |
1598
+ | `hooks[].identifier.key` | string | Unique 6-char alphanumeric key. |
1599
+ | `hooks[].content` | string | Raw Swift code to inject. For imports: a single `import` statement. For initialization: Swift code that runs during app launch. |
1600
+ | `parameters` | map | Template variables keyed by parameter ID. Use `{{variableName}}` in `content` to reference them. Same structure as all other custom files. |
1601
+
1602
+ ### Generated AppDelegate execution order
1603
+
1604
+ The generated `AppDelegate.swift` follows this structure:
1605
+
1606
+ ```
1607
+ 1. import Flutter ← Standard Flutter import
1608
+ 2. import UIKit ← APP_DELEGATE_IMPORT_HOOK entries
1609
+ 3. import MySDK ← APP_DELEGATE_IMPORT_HOOK entries
1610
+ 4.
1611
+ 5. @UIApplicationMain
1612
+ 6. class AppDelegate: FlutterAppDelegate {
1613
+ 7. override func application(...) -> Bool {
1614
+ 8. GeneratedPluginRegistrant.register(with: self)
1615
+ 9. Logs.init() ← APP_DELEGATE_INITIALIZATION_HOOK entries
1616
+ 10. MySDK.configure() ← APP_DELEGATE_INITIALIZATION_HOOK entries
1617
+ 11. return super.application(...)
1618
+ 12. }
1619
+ 13. }
1620
+ ```
1621
+
1622
+ - **Import hooks** are placed at the top of the file alongside the standard Flutter/UIKit imports.
1623
+ - **Initialization hooks** are placed inside `application(_:didFinishLaunchingWithOptions:)` after plugin registration but before the `return` statement.
1624
+
1625
+ ### Common hook content examples
1626
+
1627
+ **Import a framework:**
1628
+ ```yaml
1629
+ - type: APP_DELEGATE_IMPORT_HOOK
1630
+ identifier:
1631
+ name: firebaseImport
1632
+ key: abc123
1633
+ content: import Firebase
1634
+ ```
1635
+
1636
+ **Initialize an SDK:**
1637
+ ```yaml
1638
+ - type: APP_DELEGATE_INITIALIZATION_HOOK
1639
+ identifier:
1640
+ name: firebaseInit
1641
+ key: def456
1642
+ content: FirebaseApp.configure()
1643
+ ```
1644
+
1645
+ **Multi-line initialization:**
1646
+ ```yaml
1647
+ - type: APP_DELEGATE_INITIALIZATION_HOOK
1648
+ identifier:
1649
+ name: oneSignalSetup
1650
+ key: ghi789
1651
+ content: "OneSignal.initialize(\"{{appId}}\")\nOneSignal.Notifications.requestPermission({ accepted in })"
1652
+ ```
1653
+
1654
+ ### API capabilities
1655
+
1656
+ | Operation | Status | Notes |
1657
+ |---|---|---|
1658
+ | Reading config | Works | Full hook and parameter data returned. |
1659
+ | Adding hooks via API | Expected to work | Same structure as other custom file hooks. |
1660
+ | Adding parameters via API | Expected to work | Same structure as other custom file parameters. |
1661
+ | Hook name validation gap | Caution | Same as other custom files — avoid hyphens in names, use camelCase or spaces. |
1662
+
1663
+ ---
1664
+
1245
1665
  ## custom-file/id-PROGUARD
1246
1666
 
1247
1667
  **Purpose:** Allows injecting custom ProGuard rules into the generated `proguard-rules.pro` file for Android builds. Controls code shrinking, obfuscation, and optimization rules.
@@ -335,6 +335,26 @@ widgetProperty:
335
335
  inputValue: 460
336
336
  ```
337
337
 
338
+ **Translatable text (i18n):**
339
+
340
+ Use `translatableText` inside `inputValue` to pass a string that should be translated at runtime based on the app's current locale. This is the correct way to make component parameter text translatable.
341
+
342
+ ```yaml
343
+ paramIdentifier:
344
+ name: title
345
+ key: p1titl
346
+ inputValue:
347
+ translatableText:
348
+ translationIdentifier:
349
+ key: ms01ttl1 # References languages/translation/id-ms01ttl1
350
+ textValue:
351
+ inputValue: Histamine / Low DAO # English default / editor preview
352
+ ```
353
+
354
+ Each `translationIdentifier.key` must have a corresponding `languages/translation/id-<key>` file with translations for all supported languages (see `01-project-files.md` for the translation file schema).
355
+
356
+ > **Note:** `translationIdentifier` is NOT valid as a direct sibling of `paramIdentifier` on a parameterPass — it must be nested inside `inputValue.translatableText`. Placing it at the wrong level causes a validation error.
357
+
338
358
  ---
339
359
 
340
360
  ## 7. isDummyRoot
@@ -831,4 +831,19 @@ inputValue:
831
831
  value: "4294967295" # ARGB integer as string
832
832
  ```
833
833
 
834
+ **Translatable text (i18n):**
835
+
836
+ Used when passing a string that should be translated at runtime based on the app's locale. Wraps `translationIdentifier` and `textValue` inside `inputValue.translatableText`. Commonly used in component parameter passes to make per-instance text translatable.
837
+
838
+ ```yaml
839
+ inputValue:
840
+ translatableText:
841
+ translationIdentifier:
842
+ key: ms01ttl1 # References languages/translation/id-ms01ttl1
843
+ textValue:
844
+ inputValue: Histamine / Low DAO # English default
845
+ ```
846
+
847
+ Each key must have a corresponding translation file at `languages/translation/id-<key>` with entries for all supported languages. See `01-project-files.md` for the translation file schema.
848
+
834
849
  The `mostRecentInputValue` field must always stay in sync with `inputValue` when both are present.
@@ -1,6 +1,6 @@
1
1
  # Custom Code
2
2
 
3
- FlutterFlow supports four types of custom code: **Custom Actions** (async Dart functions with side effects), **Custom Functions** (pure synchronous Dart functions), **Custom Widgets** (Flutter widgets with parameters), and **AI Agents** (LLM-powered processing pipelines). Additionally, **App Action Components** provide reusable action chains that can be invoked from any page, and **Custom Files** allow overriding special project-level files like `main.dart` and `AndroidManifest.xml`.
3
+ FlutterFlow supports four types of custom code: **Custom Actions** (async Dart functions with side effects), **Custom Functions** (pure synchronous Dart functions), **Custom Widgets** (Flutter widgets with parameters), and **AI Agents** (LLM-powered processing pipelines). Additionally, **App Action Components** provide reusable action chains that can be invoked from any page, and **Custom Files** allow overriding special project-level files like `main.dart`, `AndroidManifest.xml`, `Info.plist`, `proguard-rules.pro`, and `build.gradle`.
4
4
 
5
5
  ---
6
6
 
@@ -706,76 +706,21 @@ variable:
706
706
 
707
707
  ## Custom Files
708
708
 
709
- Custom Files are special project-level files such as `main.dart` (the app entry point) and `AndroidManifest.xml`. Each has a metadata YAML file and a companion code file. They live under `custom-file/`.
709
+ Custom Files are special project-level files that override platform-specific configuration (e.g., `main.dart`, `AndroidManifest.xml`, `Info.plist`, `proguard-rules.pro`, `build.gradle`). Each has a metadata YAML file and a companion code file under `custom-file/`.
710
710
 
711
- ### File layout
712
-
713
- ```
714
- custom-file/
715
- id-MAIN.yaml # Metadata for main.dart
716
- id-MAIN/custom-file-code.dart.yaml # main.dart Dart code
717
- id-ANDROID_MANIFEST.yaml # Metadata for AndroidManifest.xml
718
- id-ANDROID_MANIFEST/custom-file-code.dart.yaml # AndroidManifest.xml content
719
- ```
720
-
721
- ### MAIN metadata (id-MAIN.yaml)
722
-
723
- ```yaml
724
- type: MAIN
725
- isUnlocked: false
726
- actions:
727
- - type: FINAL_ACTION
728
- identifier:
729
- name: initializeUlink
730
- key: t6snt
731
- projectId: ulink-21sajt
732
- description: ""
733
- ```
734
-
735
- ### ANDROID_MANIFEST metadata (id-ANDROID_MANIFEST.yaml)
736
-
737
- ```yaml
738
- type: ANDROID_MANIFEST
739
- identifier:
740
- name: AndroidManifest.xml
741
- isUnlocked: true
742
- parameters:
743
- 29c3b7b9-afe7-4429-9d20-abb2e09f7a40:
744
- parameter:
745
- identifier:
746
- name: ulinkDomain
747
- dataType:
748
- scalarType: String
749
- value:
750
- variable:
751
- source: DEV_ENVIRONMENT
752
- baseVariable:
753
- environmentValue:
754
- identifier:
755
- name: ulinkDomain
756
- key: eji5p6
757
- description: ""
758
- ```
759
-
760
- ### Definition fields
761
-
762
- | Field | Required | Description |
763
- |-------|----------|-------------|
764
- | `type` | Yes | File type: `MAIN` or `ANDROID_MANIFEST` |
765
- | `identifier` | For ANDROID_MANIFEST | `name` of the file |
766
- | `isUnlocked` | No | Whether the file is editable (default: false) |
767
- | `actions` | No | List of final actions (library initializers) that run at startup |
768
- | `parameters` | No | Template parameter substitutions for the code file |
769
- | `description` | No | Human-readable description |
770
-
771
- ### Code files
772
-
773
- - **MAIN:** `custom-file/id-MAIN/custom-file-code.dart.yaml` contains the full `main.dart` with imports, `main()` function, Firebase/Supabase initialization, and library init calls.
774
- - **ANDROID_MANIFEST:** `custom-file/id-ANDROID_MANIFEST/custom-file-code.dart.yaml` contains the XML manifest with template placeholders (e.g., `{{unlinkDomain}}`, `{{ulink-21sajt.ulinkSchema}}`). Parameters in metadata map to these placeholders.
711
+ > **Full documentation:** Custom Files are documented in detail in [01-project-files.md](01-project-files.md) under the `custom-file/id-*` sections, since they are project-level configuration files. See those sections for complete schemas, hook types, parameters, API capabilities, and warnings.
775
712
 
776
- ### Parameter substitution
713
+ ### Quick reference
777
714
 
778
- Parameters in ANDROID_MANIFEST metadata define template variables. Each key is a UUID, and the value specifies where the parameter's value comes from (typically `DEV_ENVIRONMENT`). These get substituted into the code file's template placeholders at build time.
715
+ | Type | File key | Description |
716
+ |------|----------|-------------|
717
+ | `MAIN` | `custom-file/id-MAIN` | App entry point (`main.dart`) — lifecycle actions (INITIAL/FINAL) |
718
+ | `ANDROID_MANIFEST` | `custom-file/id-ANDROID_MANIFEST` | Android manifest — XML injection hooks, template parameters |
719
+ | `INFO_PLIST` | `custom-file/id-INFO_PLIST` | iOS Info.plist — property injection hooks, template parameters |
720
+ | `ENTITLEMENTS` | `custom-file/id-ENTITLEMENTS` | iOS Runner.entitlements — capability entitlement injection |
721
+ | `APP_DELEGATE` | `custom-file/id-APP_DELEGATE` | iOS AppDelegate.swift — import and initialization code injection |
722
+ | `PROGUARD` | `custom-file/id-PROGUARD` | ProGuard rules — rule injection hooks |
723
+ | `BUILD_GRADLE` | `custom-file/id-BUILD_GRADLE` | Gradle config — plugin/dependency/repository injection hooks |
779
724
 
780
725
  ---
781
726
 
@@ -788,4 +733,4 @@ Parameters in ANDROID_MANIFEST metadata define template variables. Each key is a
788
733
  | Custom Widget | `custom-widgets/id-<key>.yaml` | (code embedded or separate) | `custom-widgets/` |
789
734
  | AI Agent | `agent/id-<key>.yaml` | N/A (no code file) | `agent/` |
790
735
  | App Action Component | `app-action-components/id-<key>.yaml` | N/A (actions are inline) | `app-action-components/` |
791
- | Custom File | `custom-file/id-<TYPE>.yaml` | `custom-file/id-<TYPE>/custom-file-code.dart.yaml` | `custom-file/` |
736
+ | Custom File | `custom-file/id-<TYPE>` | `custom-file/id-<TYPE>/custom-file-code.dart` | `custom-file/` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flutterflow-mcp",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
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",
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: flutterflow-mcp
3
+ description: >
4
+ Teaches AI assistants how to develop FlutterFlow apps using MCP tools.
5
+ Use this skill when working with FlutterFlow projects, editing FF YAML,
6
+ creating or inspecting pages and components, reading project configuration,
7
+ or navigating FlutterFlow widget trees. It covers all 25 MCP tools for
8
+ discovery, reading, editing, and settings. Triggers on: FlutterFlow,
9
+ FF YAML, FF page, FF component, FF widget, FF theme, FF project.
10
+ license: MIT
11
+ compatibility: Requires the flutterflow-mcp MCP server to be connected and a valid FLUTTERFLOW_API_TOKEN environment variable.
12
+ metadata:
13
+ author: mohn93
14
+ version: "1.0"
15
+ ---
16
+
17
+ ## Prerequisites
18
+
19
+ This skill requires the **flutterflow-mcp** MCP server to be installed and connected. Before proceeding, check if the `list_projects` tool is available. If not, the user needs to set up the MCP server first:
20
+
21
+ 1. Get a FlutterFlow API token from **FlutterFlow > Profile > Account Settings > API Token** (requires a paid FlutterFlow subscription)
22
+ 2. Add the MCP server to your AI client:
23
+ ```bash
24
+ # Claude Code
25
+ claude mcp add flutterflow -e FLUTTERFLOW_API_TOKEN=<token> -- npx -y flutterflow-mcp
26
+
27
+ # Other clients (Claude Desktop, Cursor, Windsurf) — add to MCP config:
28
+ # { "command": "npx", "args": ["-y", "flutterflow-mcp"], "env": { "FLUTTERFLOW_API_TOKEN": "<token>" } }
29
+ ```
30
+ 3. Restart your AI client, then verify by calling `list_projects`
31
+
32
+ ## Overview
33
+
34
+ The FlutterFlow MCP provides 25 tools for reading, inspecting, and editing FlutterFlow projects through YAML. It connects AI assistants to the FlutterFlow Project API, enabling programmatic access to pages, components, themes, actions, data models, and settings. All project data is represented as YAML files that can be fetched, cached locally, validated, and pushed back.
35
+
36
+ ## Tool Catalog
37
+
38
+ ### Discovery & Exploration
39
+
40
+ | Tool | Purpose |
41
+ |------|---------|
42
+ | `list_projects` | List all FF projects accessible to your API token |
43
+ | `list_project_files` | List YAML file keys in a project (supports prefix filter) |
44
+ | `list_pages` | List pages with human-readable names, scaffold IDs, and folders |
45
+ | `search_project_files` | Search file keys by keyword, prefix, or regex |
46
+ | `sync_project` | Download all project YAML to local cache for fast reads |
47
+
48
+ ### Reading & Understanding
49
+
50
+ | Tool | Purpose |
51
+ |------|---------|
52
+ | `get_page_by_name` | Fetch full page YAML by human-readable name |
53
+ | `get_project_yaml` | Fetch specific YAML file(s) by file key |
54
+ | `get_page_summary` | Quick page overview: widget tree, actions, params (cache-based) |
55
+ | `get_component_summary` | Quick component overview: widget tree, params (cache-based) |
56
+ | `find_component_usages` | Find all pages/components that use a given component |
57
+ | `find_page_navigations` | Find all actions that navigate to a given page |
58
+
59
+ ### Configuration & Settings
60
+
61
+ | Tool | Purpose |
62
+ |------|---------|
63
+ | `get_theme` | Theme colors, typography, breakpoints, widget defaults |
64
+ | `get_app_state` | App state variables, constants, environment settings |
65
+ | `get_api_endpoints` | API endpoint definitions (method, URL, headers, response) |
66
+ | `get_data_models` | Data structs, enums, Firestore collections, Supabase tables |
67
+ | `get_custom_code` | Custom actions, functions, widgets, AI agents (read-only) |
68
+ | `get_general_settings` | App Details, App Assets, Nav Bar & App Bar |
69
+ | `get_project_setup` | Firebase, Languages, Platforms, Permissions, Dependencies |
70
+ | `get_app_settings` | Authentication, Push Notifications, Deployment settings |
71
+ | `get_in_app_purchases` | Stripe, Braintree, RevenueCat, Razorpay config |
72
+ | `get_integrations` | Supabase, SQLite, GitHub, Algolia, Google Maps, AdMob, etc. |
73
+
74
+ ### Editing & Documentation
75
+
76
+ | Tool | Purpose |
77
+ |------|---------|
78
+ | `get_editing_guide` | Get recommended workflow and docs for a specific editing task |
79
+ | `get_yaml_docs` | Search/retrieve YAML reference docs by topic or file path |
80
+ | `validate_yaml` | Validate YAML content before pushing changes |
81
+ | `update_project_yaml` | Push validated YAML changes to the FF project |
82
+
83
+ ## Core Workflows
84
+
85
+ ### Discover & Explore a Project
86
+
87
+ Use this workflow when first connecting to a FlutterFlow project or when you need to understand its structure.
88
+
89
+ ```
90
+ 1. list_projects → pick the target projectId
91
+ 2. sync_project(projectId) → cache all YAML locally for fast reads
92
+ 3. list_pages(projectId) → see all pages with human-readable names, scaffold IDs, folders
93
+ 4. get_page_summary(projectId, pageName) → widget tree overview for any page of interest
94
+ ```
95
+
96
+ After syncing, all cache-based tools (`get_page_summary`, `get_component_summary`, `get_theme`, `get_app_state`, etc.) work without additional API calls.
97
+
98
+ ### Read / Inspect a Page or Component
99
+
100
+ Use this workflow to understand what a page contains, how it is structured, and how it connects to the rest of the app.
101
+
102
+ ```
103
+ 1. get_page_summary(projectId, pageName) → quick overview of widget tree, actions, params
104
+ 2. get_page_by_name(projectId, pageName) → full YAML if you need complete details
105
+ 3. find_page_navigations(projectId, pageName) → discover what actions navigate here
106
+ 4. find_component_usages(projectId, componentName) → find everywhere a component is used
107
+ ```
108
+
109
+ For components, use `get_component_summary` instead of `get_page_summary`. Component summaries resolve nested component references so you can see the full hierarchy.
110
+
111
+ ### Edit an Existing Widget
112
+
113
+ Use this workflow to modify a specific widget on a page without affecting the rest of the page.
114
+
115
+ ```
116
+ 1. get_page_by_name(projectId, pageName) → get the full page YAML
117
+ 2. Identify the node file key for the target widget (format: page/id-Scaffold_XXX/page-widget-tree-outline/node/id-Widget_YYY)
118
+ 3. get_project_yaml(projectId, fileName: "page/id-.../node/id-Widget_XXX") → fetch the individual node YAML
119
+ 4. Modify the YAML — keep inputValue and mostRecentInputValue in sync
120
+ 5. validate_yaml(projectId, fileKey, content) → check for errors before pushing
121
+ 6. update_project_yaml(projectId, {fileKey: content}) → push changes
122
+ ```
123
+
124
+ Always edit at the node level. Editing the full page YAML for a single widget change risks overwriting unrelated content and is more error-prone.
125
+
126
+ ### Add a New Widget to a Page
127
+
128
+ Use this workflow when you need to add new widgets to an existing page.
129
+
130
+ ```
131
+ 1. get_page_by_name(projectId, pageName) → understand the current widget tree structure
132
+ 2. get_yaml_docs(topic: "WidgetType") → look up the YAML schema for the widget you want to add
133
+ 3. Update the page-widget-tree-outline to include a reference to the new widget key
134
+ 4. Create individual node files for each new widget (one file per widget)
135
+ 5. validate_yaml → validate the tree outline first, then each node file
136
+ 6. update_project_yaml → push tree outline + all node files together in one call
137
+ ```
138
+
139
+ Pushing the tree outline and node files in a single call is critical. The server strips inline widget children from the tree outline, so nodes must be separate files.
140
+
141
+ ### Create a Reusable Component
142
+
143
+ Use this workflow to build a new component from scratch.
144
+
145
+ ```
146
+ 1. get_yaml_docs(topic: "create component") → get the full walkthrough and required file structure
147
+ 2. Design component parameters: name, dataType, isNullable for each param
148
+ 3. Create these files: component metadata, widget-tree-outline, root node (with isDummyRoot: true), child nodes
149
+ 4. validate_yaml → validate all files before pushing
150
+ 5. update_project_yaml → push all component files in one call
151
+ ```
152
+
153
+ Remember: the root node of a component must have `isDummyRoot: true`. Callback triggers use `triggerType: CALLBACK` with a separate `parameterIdentifier` field. WidgetProperty params use `widgetProperty` in parameterPasses, not `inputValue`.
154
+
155
+ ## Critical YAML Rules
156
+
157
+ 1. **Sync inputValue and mostRecentInputValue** -- Both fields must always contain the same value when you set or update them. If you change one, change both. Exceptions: `fontWeightValue` and `fontSizeValue` only accept `inputValue` (they have no `mostRecentInputValue` field).
158
+
159
+ 2. **Use node-level file keys for edits** -- Target `page/id-Scaffold_XXX/page-widget-tree-outline/node/id-Widget_YYY` for individual widget edits. Never edit the full page YAML (`page/id-Scaffold_XXX`) just to change a single widget. Full-page edits risk overwriting unrelated content and are harder to validate.
160
+
161
+ 3. **Always validate before pushing** -- Call `validate_yaml` before every `update_project_yaml` call. Validation catches missing node files, unknown fields, invalid enum values, and structural problems. Skipping validation risks corrupting the project.
162
+
163
+ 4. **Push tree outline and node files together** -- When adding new widgets, include the updated `page-widget-tree-outline` AND all individual node files in a single `update_project_yaml` call. Widget children embedded inline in the tree outline will be silently stripped by the FlutterFlow server.
164
+
165
+ 5. **Column has no mainAxisSize field** -- To achieve shrink-to-content behavior (equivalent to `MainAxisSize.min` in Flutter), use `minSizeValue: { inputValue: true }` on the Column widget instead.
166
+
167
+ 6. **AppBar templateType** -- Only `LARGE_HEADER` is a confirmed valid value. Do not use `STANDARD` as it may cause unexpected behavior. Control the AppBar height through the `toolbarHeight` property instead.
168
+
169
+ 7. **Custom code is read-only** -- Custom actions, functions, widgets, and AI agents cannot be created or edited through the MCP API. Use `get_custom_code` to read their signatures and source code, but any modifications must be made directly in the FlutterFlow UI. Attempting to push custom code changes will silently corrupt or be ignored.
170
+
171
+ ## Anti-Patterns
172
+
173
+ - **Don't use `list_project_files` to find pages** -- It returns raw file keys without human-readable names. Use `list_pages` instead, which gives you page names, scaffold IDs, and folder assignments.
174
+
175
+ - **Don't fetch pages one-by-one to browse a project** -- This is slow and wastes API calls. Use `sync_project` to cache everything locally first, then use `get_page_summary` for quick overviews of any page.
176
+
177
+ - **Don't edit full page YAML for a single widget change** -- Full-page edits can overwrite other widgets, actions, or parameters. Always use node-level file keys for targeted, safe edits.
178
+
179
+ - **Don't guess YAML field names or enum values** -- FlutterFlow YAML has specific field names and valid values that are not always intuitive. Use `get_yaml_docs(topic)` or `get_editing_guide(task)` to look up the correct schema before writing YAML.
180
+
181
+ - **Don't embed widget children inline in the tree outline** -- The FlutterFlow server silently strips inline children from the `page-widget-tree-outline` file. Always create separate node files for each widget.
182
+
183
+ - **Don't push custom code changes through the API** -- The API silently corrupts or ignores Dart code edits for custom actions, functions, and widgets. These must be edited in the FlutterFlow UI.
184
+
185
+ ## Documentation Lookup
186
+
187
+ The MCP server ships with 21 built-in reference documents. Use these tools to look up schemas, patterns, and conventions **before** writing YAML:
188
+
189
+ | When you need... | Call |
190
+ |------------------|------|
191
+ | Widget schema (Button, Text, Container, etc.) | `get_yaml_docs(topic: "Button")` |
192
+ | Action chains, triggers, navigation | `get_yaml_docs(topic: "actions")` |
193
+ | Data binding, variable sources | `get_yaml_docs(topic: "variables")` |
194
+ | Colors, typography, dimensions | `get_yaml_docs(topic: "theming")` |
195
+ | Creating components from scratch | `get_yaml_docs(topic: "create component")` |
196
+ | Editing workflows and anti-patterns | `get_yaml_docs(topic: "editing")` |
197
+ | Data structs, enums, collections | `get_yaml_docs(topic: "data")` |
198
+ | Full docs index | `get_yaml_docs()` |
199
+ | Guided workflow for a specific task | `get_editing_guide(task: "change the button color")` |
200
+
201
+ Always consult the docs before writing YAML. They contain validated schemas, field references, enum values, and real examples.