Bring AI to your FileMaker scripts without handing over your file.
Copy steps out as readable text, flag secrets for review before you share, work in any editor or AI, and paste back losslessly. The conversion runs locally, and you stay the gate on what your AI ever sees.
Set Variable [ $orderTotal ;
Value: Sum ( LineItems::amount ) ]
If [ $orderTotal > 1000 ]
Set Variable [ $status ;
Value: "needs approval" ]
Else
Set Variable [ $status ;
Value: "approved" ]
End If What leaves your machine, and what never does
A tool that rewrites your scripts has to earn it. The tool uploads nothing, you choose what reaches your AI, and every step is verified lossless before it goes back.
Lossless by name
Every step pastes back exactly the way FileMaker resolves it by name. Current through FileMaker 2026: new steps render as clean text, and anything it doesn't yet recognize is preserved exactly rather than guessed at. Unknown means untouched.
Local conversion, sends nothing
The conversion runs on your machine, inside FileMaker. The tool uploads no script content; the only network traffic is licensing (your license key and a one-way machine ID, or just those IDs for the single call that starts a free trial) plus a daily anonymous check for a newer version, which you can switch off. Some alternative tools send your schema or scripts to a server to do their work. FMSB does not.
You are the gate
You copy out only the steps you choose and review every AI edit as plain text before it touches FileMaker. You decide what reaches your AI, and a copy that contains likely secrets stops for your decision before it ever reaches the clipboard.
One-click redaction
Every copy is scanned for likely secrets and PII. On a hit the copy stops: redact in one click (placeholders replace the values), copy unchanged, or cancel. Or set it to redact automatically. An aid to your care, not a replacement for it.
Catches broken references
Surfaces fields, scripts and layouts that won't resolve, and test-parses every calculation with FileMaker's own engine, so a renamed field or a calculation that won't compile doesn't silently break on paste.
No telemetry
No analytics in the tool. Bug reports are opt-in, redaction is applied to them, and you review what is in them before anything is sent.
A FileMaker script as text, and back again
Real engine output, not a mockup. The clean text you edit, and the clipboard XML it came from and pastes back to, identical the way FileMaker resolves references by name. Anyone can prettify XML; coming back perfectly is the hard part. New to the problem? Start with how to copy a FileMaker script as text, and paste it back.
Order approval
Pastes back identical, by nameSet Variable [ $orderTotal ; Value: Sum ( LineItems::amount ) ]
If [ $orderTotal > 1000 ]
Set Variable [ $status ; Value: "needs approval" ]
Exit Script [ Result: $status ]
Else
Set Variable [ $status ; Value: "approved" ]
End If <fmxmlsnippet type="FMObjectList">
<Step enable="True" id="141" name="Set Variable">
<Value>
<Calculation><![CDATA[Sum ( LineItems::amount )]]></Calculation>
</Value>
<Repetition>
<Calculation><![CDATA[1]]></Calculation>
</Repetition>
<Name>$orderTotal</Name>
</Step>
<Step enable="True" id="68" name="If">
<Restore state="False"/>
<Calculation><![CDATA[$orderTotal > 1000]]></Calculation>
</Step>
<Step enable="True" id="141" name="Set Variable">
<Value>
<Calculation><![CDATA["needs approval"]]></Calculation>
</Value>
<Repetition>
<Calculation><![CDATA[1]]></Calculation>
</Repetition>
<Name>$status</Name>
</Step>
<Step enable="True" id="103" name="Exit Script">
<Calculation><![CDATA[$status]]></Calculation>
</Step>
<Step enable="True" id="69" name="Else">
<Restore state="False"/>
</Step>
<Step enable="True" id="141" name="Set Variable">
<Value>
<Calculation><![CDATA["approved"]]></Calculation>
</Value>
<Repetition>
<Calculation><![CDATA[1]]></Calculation>
</Repetition>
<Name>$status</Name>
</Step>
<Step enable="True" id="70" name="End If"/>
</fmxmlsnippet> What gets caught, both ways
Two directions, two checks. Both on by default, each with its own switch.
Without redaction, this is what copies to your clipboard
Set Variable [ $apiKey ; Value: "demo-key-not-a-real-secret-9f8e7d" ]
Set Variable [ $endpoint ; Value: "https://api.billing.example/v1/charge" ]
Set Variable [ $notifyTo ; Value: "[email protected]" ]
If [ $orderTotal > 1000 ]
Set Variable [ $authHeader ; Value: "Bearer " & $apiKey ]
End If With FMSB, this is what reaches your AI
Set Variable [ $apiKey ; Value: "#!#REDACTED#!#" ]
Set Variable [ $endpoint ; Value: "https://api.billing.example/v1/charge" ]
Set Variable [ $notifyTo ; Value: "#!#REDACTED#!#" ]
If [ $orderTotal > 1000 ]
Set Variable [ $authHeader ; Value: "Bearer " & $apiKey ]
End If FMSB also adds a short warning line above the text, naming what it replaced (here: 1 secret variable value, 1 email address) and noting it is not safe to paste back as is.
Without guardrails you see this in FileMaker
Set Field [ Invoices::<Field Missing> ; 0 ]
Set Field [ <Table Missing> ; 0 ]
Perform Script [ “<unknown>” ; Specified: From list ; Parameter: "mode=full" ]
Set Variable [ $revenue ; Value: /*Customers::Loyalty_Tier * 12*/ ]
If [ /*$asd &*/ ]
Show Custom Dialog [ "Over target" ]
End If With FMSB you are informed where the problem is
# ⚠ ATTENTION [fmsb-missing-ref]: field "Invoices::Discount_Rate" not found in "Invoices"
Set Field [ Invoices::<Field Missing> ; 0 ]
# ⚠ ATTENTION [fmsb-missing-ref]: field "Archive::Total" not found in "Invoices"
Set Field [ <Table Missing> ; 0 ]
# ⚠ ATTENTION [fmsb-missing-ref]: script "Nightly Sync" not found in "Invoices"
Perform Script [ “<unknown>” ; Specified: From list ; Parameter: "mode=full" ]
# ⚠ ATTENTION [fmsb-missing-ref]: field "Customers::Loyalty_Tier" (in calculation) not found in "Invoices" → FileMaker disables this entire calculation (/*…*/) on paste
Set Variable [ $revenue ; Value: /*Customers::Loyalty_Tier * 12*/ ]
# ⚠ ATTENTION [fmsb-missing-ref]: calculation does not compile (FileMaker error 1204: syntax error) → FileMaker disables this entire calculation (/*…*/) on paste
If [ /*$asd &*/ ]
Show Custom Dialog [ "Over target" ]
End If Up to 77% lighter than clipboard XML for your AI
When you do share with an AI, the clean text is far smaller than FileMaker's clipboard XML. More of your script fits in the model's context, replies come back faster, and you spend fewer tokens. Measured on real captured scripts, conservatively.
Real engine output, not a mockup. Token counts are approximate (about 4 characters each); sizes are measured against the compact clipboard XML.
And your AI learns to write it back
One calculation function, FMSB_CopyAISpec, puts the complete writing guide for this text format on your clipboard. Paste it into your AI chat once and the AI writes push-ready scripts. Field-tested: 44 of 44 blind-authored scripts from different AI assistants pushed back clean.
Work with JSON a lot? Two functions do the typing for you.
FMSB adds a couple of calculation functions that have nothing to do with converting scripts. They write FileMaker calc and script code for you, straight from a JSON sample. Real output below, not a mockup.
Give it JSON and get the JSONSetElement expression that rebuilds it, every leaf a paste-ready [ path ; value ; type ] row, nested objects and arrays included.
You give it
{"orderId":1001,"customer":{"name":"Acme Corp","email":"[email protected]"},"total":249.5,"lines":[{"sku":"A1","qty":2}]} It returns, paste-ready
JSONSetElement ( "{}" ;
[ "orderId" ; 1001 ; JSONNumber ] ;
[ "customer.name" ; "Acme Corp" ; JSONString ] ;
[ "customer.email" ; "[email protected]" ; JSONString ] ;
[ "total" ; 249.5 ; JSONNumber ] ;
[ "lines[0].sku" ; "A1" ; JSONString ] ;
[ "lines[0].qty" ; 2 ; JSONNumber ]
) Give it the same JSON and get a complete, readable script that loops your records and composes it back. A nested array ("lines" here) makes the script call itself once per child record to walk into a related table.
You give it
{"orderId":1001,"customer":{"name":"Acme Corp","email":"[email protected]"},"total":249.5,"lines":[{"sku":"A1","qty":2}]} It returns: wire in your fields and run it
# ===== START HERE - this is a template, not a finished script =====
# 1) dotted keys (a.b) read the CURRENT record; arrays walk a child layout, found by the parent key
# 2) this script is RECURSIVE - give it a unique name
# 3) Fill in every SET THIS block.
# 4) Set the gate below to True when ready.
# 5) Run from the initial parent record without parameter - result returns via Get ( ScriptResult )
# ===== sample structure =====
# { orderId: number, customer: { name: text, email: text }, total: number, lines: [ 1 x { sku: text, qty: number } ] }
If [ False ]
Set Error Capture [ On ]
Freeze Window
Set Variable [ $fmsb.home ; Value: Get ( LayoutName ) ]
Set Variable [ $fmsb.collection ; Value: JSONGetElement ( Get ( ScriptParameter ) ; "collection" ) ]
Set Variable [ $fmsb.parentKey ; Value: JSONGetElement ( Get ( ScriptParameter ) ; "parentKey" ) ]
If [ IsEmpty ( Get ( ScriptParameter ) ) ]
# ===== TOP LEVEL - this part runs when there is no script parameter =====
# RUN THIS FROM THE PARENT RECORD YOU WANT TO EXPORT - wrong record = blank JSON, no error
# ############################################
# ##### SET THIS - one field per key #####
# ############################################
# $orderId_value - your field for "orderId". Sample: 1001
Set Variable [ $orderId_value ; Value: "SET VALUE" ]
# $customer_name_value - your field for "customer.name". Sample: "Acme Corp"
Set Variable [ $customer_name_value ; Value: "SET VALUE" ]
# $customer_email_value - your field for "customer.email". Sample: "[email protected]"
Set Variable [ $customer_email_value ; Value: "SET VALUE" ]
# $total_value - your field for "total". Sample: 249.5
Set Variable [ $total_value ; Value: "SET VALUE" ]
# $lines_parent_key - the key field on THIS record that "lines" rows point back to
# unquoted field - the VALUE, not the field's name
Set Variable [ $lines_parent_key ; Value: "SET VALUE" ]
# ############################################
# ##### END SET THIS #####
# ############################################
Set Variable [ $fmsb.json ; Value:
```
JSONSetElement ( "{}" ;
[ "orderId" ; $orderId_value ; JSONNumber ] ;
[ "customer.name" ; $customer_name_value ; JSONString ] ;
[ "customer.email" ; $customer_email_value ; JSONString ] ;
[ "total" ; $total_value ; JSONNumber ] ;
[ "lines" ; "[]" ; JSONArray ]
)
``` ]
# "lines" - this same script fills it in, one call per record.
Set Variable [ $fmsb.thisKey ; Value: $lines_parent_key ]
Perform Script [ by name: Get ( ScriptName ) ; Parameter: JSONSetElement ( "{}" ; [ "collection" ; "lines" ; JSONString ] ; [ "parentKey" ; $fmsb.thisKey ; JSONString ] ) ]
If [ Left ( Get ( ScriptResult ) ; 6 ) = "!FMSB:" ]
# Error handling block. Passing message instead of "?"
Exit Script [ Result: Get ( ScriptResult ) ]
End If
Set Variable [ $fmsb.json ; Value: JSONSetElement ( $fmsb.json ; "lines" ; Get ( ScriptResult ) ; JSONArray ) ]
Exit Script [ Result: $fmsb.json ]
Else If [ $fmsb.collection = "lines" ]
# ===== "lines" - one record becomes one element of this list =====
# Your sample had 1 element. Keys: sku, qty
# needs its OWN table occurrence + layout
# ############################################
# ##### SET THIS - find the "lines" records #####
# ############################################
# $lines_layout - layout of these records, quoted. Example: "Lines_list"
Set Variable [ $lines_layout ; Value: "SET VALUE" ]
# $lines_find_field - FK field on the child table, quoted. Example: "Lines::parent_id"
Set Variable [ $lines_find_field ; Value: "SET VALUE" ]
# ############################################
# ##### END SET THIS #####
# ############################################
# $lines_find_value - pre-wired: exact match on the parent key - drop the "==" for begins-with
Set Variable [ $lines_find_value ; Value: "==" & $fmsb.parentKey ]
If [ IsEmpty ( $fmsb.parentKey ) ]
Exit Script [ Result: "!FMSB: the parent key was empty - there is nothing to find the child records with" ]
End If
Go to Layout [ by name: $lines_layout ]
If [ Get ( LayoutName ) <> $lines_layout or PatternCount ( List ( $lines_find_field ; $lines_find_value ) ; "SET VALUE" ) ]
Go to Layout [ by name: $fmsb.home ]
Exit Script [ Result: "!FMSB: check the SET THIS block above - the layout name is wrong, or a line still says SET VALUE" ]
End If
Enter Find Mode [ Pause: Off ]
# find operators ( @ * # ? ! = < > " ~ .. ) inside key data need \ escaping
Set Field By Name [
Target name: $lines_find_field ;
Value: $lines_find_value
]
Perform Find
If [ Get ( LastError ) = 400 ]
# The find ran with no criteria (FileMaker error 400) - the field name above is blank or not a field on this layout's table
Go to Layout [ by name: $fmsb.home ]
Exit Script [ Result: "!FMSB: the find had no criteria - check the field name and the value in the SET THIS block above" ]
End If
Set Variable [ $fmsb.n ; Value: Get ( FoundCount ) ]
Set Variable [ $fmsb.rows ; Value: "" ]
Set Variable [ $fmsb.i ; Value: 1 ]
Loop [ Flush: Always ]
Exit Loop If [ $fmsb.i > $fmsb.n ]
Go to Record/Request/Page [ With dialog: Off ; $fmsb.i ]
# ############################################
# ##### SET THIS - one field per key #####
# ############################################
# read once per record - keep inside the loop
# $sku_value - your field for "sku". Sample: "A1"
Set Variable [ $sku_value ; Value: "SET VALUE" ]
# $qty_value - your field for "qty". Sample: 2
Set Variable [ $qty_value ; Value: "SET VALUE" ]
# ############################################
# ##### END SET THIS #####
# ############################################
Set Variable [ $fmsb.row ; Value:
```
JSONSetElement ( "{}" ;
[ "sku" ; $sku_value ; JSONString ] ;
[ "qty" ; $qty_value ; JSONNumber ]
)
``` ]
Set Variable [ $fmsb.rows ; Value: List ( $fmsb.rows ; $fmsb.row ) ]
Set Variable [ $fmsb.i ; Value: $fmsb.i + 1 ]
End Loop
Go to Layout [ by name: $fmsb.home ]
Exit Script [ Result: If ( IsEmpty ( $fmsb.rows ) ; "[]" ; JSONMakeArray ( $fmsb.rows ; "¶" ; JSONRaw ) ) ]
Else
# unknown collection
Exit Script [ Result: "!FMSB: no branch for this collection" ]
End If
End If Full reference for every function: Function help.
Pay once, no subscription
Buy it once and it is yours. No subscription, no annual renewal, no seat to manage.
Join the beta- Copy from and paste back into FileMaker. The full lossless round trip
- One-click secret and PII redaction: placeholders replace values before they leave FileMaker
- Broken-reference detection before you paste
- Whole-script and comment handling, with folders and separators
- Works with any editor or AI: Claude, ChatGPT, Cursor, no extra setup
- One license covers 2 of your machines (your desktop and laptop), transferable anytime.
- 1 year of updates included, counted from the day you activate: every new FileMaker release in that window.
- After that, an optional one-time $39 update pass adds another 1 year of new builds and stacks on any time you have left. Never a subscription, never charged automatically.
- A 14-day free trial of the whole tool, no card. Activating takes one online call, once; after that it works offline.
Questions FileMaker developers ask first
FileMaker 2026 is adding AI. Do I still need this?
Does my file or data ever leave my machine?
Does FMScriptBridge use AI on my code?
What does “lossless round-trip” actually guarantee?
Which AI tools does it work with?
What is FMSB? Is it the same as FMScriptBridge?
How is it different from other FileMaker AI tools?
What happens to secrets and passwords in my scripts?
Which FileMaker versions are supported?
Is it a subscription?
More: how to copy a FileMaker script as text, and paste it back · where your script goes with each tool · the functions FMSB adds.
Join the Mac beta
The Mac beta is live. Join and we'll email you the build and the install guide, usually within a day. Joining locks the founding price of $149 for launch; Windows is in progress. No card today.
Beta emails while you test, plus one launch email. Nothing else, unsubscribe anytime. See our privacy notice.
Know another FileMaker dev who'd want this? Forward it →