.cursor/skills/schema-build/SKILL.md
Create and modify FileMaker database schema via OData REST calls against a live hosted solution. Three sub-modes — connect (OData setup walkthrough), build (execute table and field creation), relationships (produce a manual relationship specification checklist). Use when the developer says "build schema", "create tables", "create fields", "run schema", "set up OData", "connect OData", "configure OData", "OData walkthrough", "relationship spec", "specify relationships", "define relationships", or "relationship checklist".
npx skillsauth add petrowsky/agentic-fm schema-buildInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create and modify a FileMaker database schema via OData REST calls. This skill covers the full schema creation workflow from OData connectivity through table/field creation to relationship specification.
Sub-modes: connect | build | relationships
Parse the developer's request to identify which sub-mode to run:
If ambiguous, ask the developer which sub-mode they need. If the developer says "build schema" without prior OData config, start with connect and flow into build.
Read agent/config/automation.json to check for existing OData configuration.
solutions.{name}.odataCONTEXT.json exists, read solution to identify the active solution and look up its configconnect sub-mode firstWalk the developer through OData setup for a FileMaker solution. The goal is a verified OData connection with credentials written to automation.json.
Present this checklist and confirm each item:
fmodata extended privilege enabled--insecure flag on curl/fetch calls)http:// instead of https://)Ask the developer for:
local.hub, 192.168.1.100, host.docker.internal)Get(FileName) value from FileMakerConstruct the base URL: https://{hostname}/fmi/odata/v4
Use WebFetch to test the OData endpoint:
GET {base_url}/{database}/$metadata
Authorization: Basic {base64(username:password)}
If using a self-signed certificate, add the appropriate insecure flag.
Success: the response contains <edmx:Edmx> with <EntityType> elements listing the solution's tables and fields. Confirm to the developer what tables were found.
Failure scenarios:
fmodata privilege is not enabled--insecure or trusted CA setupOn successful verification, write the OData config to automation.json under solutions.{database_name}.odata:
{
"base_url": "https://{hostname}/fmi/odata/v4",
"database": "{database_name}",
"username": "{username}",
"password": "{password}",
"script_bridge": "AGFMScriptBridge"
}
If the solution already has an entry in automation.json (e.g., with explode_xml), merge the odata block into the existing entry. Do not overwrite other keys.
Confirm to the developer that the OData connection is configured and ready.
Execute table and field creation via OData REST calls against a live hosted solution.
Read the FM model from plans/schema/{solution-name}-fm-model.md. This file is produced by the schema-plan skill and contains the complete table and field definitions for the solution.
If the FM model file does not exist, tell the developer:
No FM model found at
plans/schema/{solution-name}-fm-model.md. Run theschema-planskill first to generate the data model, then come back to build it.
Extract from the FM model:
| FM Type | OData Type | Notes |
|---------|-----------|-------|
| Text | VARCHAR(n) | n = max characters; use 100 for short text, 500 for medium, 1000+ for long |
| Number | NUMERIC | Use DECIMAL(p,s) when precision/scale matter; INT for integers |
| Date | DATE | |
| Time | TIME | |
| Timestamp | TIMESTAMP | |
| Container | BLOB | or VARBINARY |
Field properties mapping:
| FM Property | OData Property | Notes |
|-------------|---------------|-------|
| Auto-enter serial | primary: true | For PrimaryKey fields |
| Unique validation | unique: true | |
| Not empty validation | nullable: false | |
| Global storage | global: true | |
| Auto-enter creation timestamp | default: "CURRENT_TIMESTAMP" | |
| Auto-enter creation account | default: "USER" | |
Critical gotcha: Do NOT specify default: "NULL" — FileMaker interprets this as a TIMESTAMP default type, not an actual null value. Omit the default property entirely when no default is needed.
For each table in the FM model, issue:
POST {base_url}/{database}/FileMaker_Tables
Authorization: Basic {base64(username:password)}
Content-Type: application/json
{
"tableName": "TableName",
"fields": [
{ "name": "FieldName", "type": "VARCHAR(100)" },
{ "name": "Amount", "type": "NUMERIC" },
...
]
}
What FileMaker auto-creates when a table is created via OData:
PrimaryKey, CreationTimestamp, CreatedBy, ModificationTimestamp, ModifiedByBecause these default fields are auto-created, do NOT include them in the fields array of the POST body. Only include fields that are specific to the table beyond the defaults.
Order matters: Create parent tables before child tables. While OData does not enforce referential integrity at creation time (relationships are manual), creating in dependency order keeps the build log readable.
If a table already exists and needs additional fields, use PATCH:
PATCH {base_url}/{database}/FileMaker_Tables/{table-name}
Authorization: Basic {base64(username:password)}
Content-Type: application/json
{
"fields": [
{ "name": "NewField", "type": "VARCHAR(100)" }
]
}
Important: PATCH for adding fields is non-atomic — individual fields succeed or fail independently. If 5 fields are submitted and 1 fails, the other 4 are still created. Always check the response for partial failures.
After all tables and fields are created, fetch the full schema to verify:
GET {base_url}/{database}/$metadata
Parse the response to confirm:
EntityType elementsFor fields that will be used as foreign keys or frequently searched, create indexes:
POST {base_url}/{database}/FileMaker_Indexes/{table-name}
Authorization: Basic {base64(username:password)}
Content-Type: application/json
{
"indexName": "FieldName"
}
Index all ForeignKey* fields by default.
Write results to plans/schema/{solution-name}-build-log.md:
# {Solution Name} — Schema Build Log
Built: {date}
Source: plans/schema/{solution-name}-fm-model.md
## Tables Created
| Table | Fields | Status |
|-------|--------|--------|
| Company | 8 | Created |
| Contact | 12 | Created |
| Invoice | 10 | Created |
| LineItem | 7 | Created |
## Fields Added (to existing tables)
| Table | Field | Type | Status |
|-------|-------|------|--------|
| ... | ... | ... | ... |
## Indexes Created
| Table | Field | Status |
|-------|-------|--------|
| Contact | ForeignKeyCompany | Created |
| Invoice | ForeignKeyContact | Created |
| LineItem | ForeignKeyInvoice | Created |
## Errors
{any errors encountered, with the OData response body}
## Notes
- Default fields auto-created by FM: PrimaryKey, CreationTimestamp, CreatedBy, ModificationTimestamp, ModifiedBy
- {any other observations}
After the build completes, tell the developer:
Schema build complete. Next steps:
- Relationships — run
schema-buildwith therelationshipssub-mode to get the relationship specification checklist- Calculation fields — OData cannot create calculation or summary fields. These must be added manually in Manage Database > Fields
- Auto-enter calculations — OData only supports basic auto-enter defaults (USER, CURRENT_TIMESTAMP). Custom auto-enter calculations must be set manually
- Validation rules — beyond
nullable: falseandunique: true, custom validation must be set manually
connect sub-modeThe OData API supports deleting tables and fields:
DELETE {base_url}/{database}/FileMaker_Tables/{table} — permanently removes the table and all its dataDELETE {base_url}/{database}/FileMaker_Tables/{table}/{field} — permanently removes a field and its dataMANDATORY: Always confirm with the developer before executing any DELETE operation. State exactly what will be deleted and that the operation is irreversible. Wait for explicit approval before proceeding.
Produce a click-through checklist for manually creating relationships in FileMaker's Manage Database > Relationships dialog. OData cannot create relationships, TOs, or modify the relationship graph — this is a hard platform limitation.
Read plans/schema/{solution-name}-fm-model.md for the relationship definitions. The model should specify:
Write to plans/schema/{solution-name}-relationships.md:
# {Solution Name} — Relationship Specification
Generated: {date}
Source: plans/schema/{solution-name}-fm-model.md
## Instructions
Open **Manage Database > Relationships** in FileMaker and create each relationship below.
Check off each item as you complete it.
## Table Occurrences
These TOs were auto-created when tables were built via OData. Additional TOs listed below
must be created manually.
### Auto-created TOs (verify these exist)
- [ ] Company
- [ ] Contact
- [ ] Invoice
- [ ] LineItem
### Additional TOs to create
- [ ] Contact_Invoice (base table: Contact) — for filtered portal on Invoice layout
- [ ] ...
## Relationships
### 1. Company -> Contact (one-to-many)
- [ ] **Left TO**: Company
- [ ] **Right TO**: Contact
- [ ] **Join**: Company::PrimaryKey = Contact::ForeignKeyCompany
- [ ] **Join type**: Equal (=)
- [ ] **Allow creation of related records**: Right side (Contact)
- [ ] **Cascade delete**: Off
- [ ] **Sort**: None
### 2. Contact -> Invoice (one-to-many)
- [ ] **Left TO**: Contact
- [ ] **Right TO**: Invoice
- [ ] **Join**: Contact::PrimaryKey = Invoice::ForeignKeyContact
- [ ] **Join type**: Equal (=)
- [ ] **Allow creation of related records**: Right side (Invoice)
- [ ] **Cascade delete**: Off
- [ ] **Sort**: None
### 3. Invoice -> LineItem (one-to-many, with cascade delete)
- [ ] **Left TO**: Invoice
- [ ] **Right TO**: LineItem
- [ ] **Join**: Invoice::PrimaryKey = LineItem::ForeignKeyInvoice
- [ ] **Join type**: Equal (=)
- [ ] **Allow creation of related records**: Right side (LineItem)
- [ ] **Cascade delete**: On (deleting an Invoice deletes its LineItems)
- [ ] **Sort**: LineItem::SortOrder ascending
{repeat for all relationships}
## Multi-predicate relationships
If any relationship uses compound join conditions (multiple field pairs), list each predicate:
### N. {Relationship name}
- [ ] **Left TO**: ...
- [ ] **Right TO**: ...
- [ ] **Join predicate 1**: LeftTO::Field1 = RightTO::Field1
- [ ] **Join predicate 2**: LeftTO::Field2 = RightTO::Field2
- [ ] **Join type**: Equal (=)
## Post-relationship steps
After all relationships are created:
1. [ ] Run **Explode XML** to refresh `xml_parsed/` with the new relationship graph
2. [ ] Run **Push Context** on the primary layout to refresh `CONTEXT.json`
3. [ ] Verify relationships in `agent/context/{solution}/relationships.index`
Show the developer the relationship checklist and the file path. Remind them:
Relationships cannot be created via any external API — this is a FileMaker platform limitation. Use this checklist to create them manually in Manage Database > Relationships. After completing the relationships, run Explode XML to refresh the agent's context.
| Operation | Method | URL |
|-----------|--------|-----|
| Create table | POST | {base_url}/{database}/FileMaker_Tables |
| Add fields | PATCH | {base_url}/{database}/FileMaker_Tables/{table} |
| Delete table | DELETE | {base_url}/{database}/FileMaker_Tables/{table} |
| Delete field | DELETE | {base_url}/{database}/FileMaker_Tables/{table}/{field} |
| Create index | POST | {base_url}/{database}/FileMaker_Indexes/{table} |
| Delete index | DELETE | {base_url}/{database}/FileMaker_Indexes/{table}/{field} |
| Get schema | GET | {base_url}/{database}/$metadata |
All requests use HTTP Basic authentication:
Authorization: Basic {base64(username:password)}
| OData Type | FM Result | Notes |
|-----------|-----------|-------|
| VARCHAR(n) | Text | n = max characters |
| NUMERIC | Number | General number |
| DECIMAL(p,s) | Number | p = precision, s = scale |
| INT | Number | Integer |
| DATE | Date | |
| TIME | Time | |
| TIMESTAMP | Timestamp | |
| BLOB | Container | |
| VARBINARY | Container | |
| Property | Type | Values | Notes |
|----------|------|--------|-------|
| primary | bool | true/false | Marks as primary key |
| unique | bool | true/false | Unique validation |
| global | bool | true/false | Global storage |
| nullable | bool | true/false | false = not empty validation |
| default | string | "USER", "CURRENT_TIMESTAMP", "CURRENT_DATE", "CURRENT_TIME" | Auto-enter default |
"NULL" as the default value causes FileMaker to interpret it as a TIMESTAMP type default — omit the default property entirely when no default is neededdevelopment
Generate a complete web application inside a FileMaker Web Viewer — self-contained HTML/CSS/JS styled with the FM theme, plus companion FM bridge scripts for bidirectional data flow. Use when the developer says "web viewer", "webviewer app", "HTML in FileMaker", "build web viewer", or when the layout-design skill delegates to the web-first output path. Recommended for modern, responsive UI, complex interactions (drag-and-drop, charts, rich text), or solutions considering future migration off FileMaker.
development
Trace references to a FileMaker object across the entire solution. Supports usage reports ("where is this field used?"), impact analysis ("what breaks if I rename this?"), and dead object scans ("show unused fields/scripts"). Use when the developer says "trace", "find references", "where is X used", "impact of renaming", "unused fields/scripts", "dead code", "what references X", or "is X used anywhere".
development
Analyze a FileMaker solution and produce a structured profile covering data model, business logic, UI layer, integrations, and health metrics. Uses on-disk pre-processing to handle solutions of any size without sending raw XML through the agent. Use when the developer says "analyze solution", "solution overview", "solution analysis", "solution profile", "solution spec", "what does this solution do", "solution summary", or wants a high-level understanding of an entire FileMaker solution.
development
Interactive setup wizard for agentic-fm. Detects what's already configured, walks the user through each remaining step, and verifies completion before proceeding. Use when the developer says "help me set up", "setup", "get started", "onboard", "first time setup", "install agentic-fm", "configure agentic-fm", or is clearly new to the project and needs guidance.