packages/fmdapi/skills/typegen-fmdapi/SKILL.md
ENTRY POINT for @proofkit/fmdapi projects. Generate TypeScript types and Zod validators from FileMaker layouts using @proofkit/typegen. Covers proofkit-typegen-config.jsonc for Data API mode, npx @proofkit/typegen setup, OttoAdapter and FetchAdapter env vars, FM MCP mode for local Web Viewer development, generated vs override file structure, schema/generated/client directory layout, Standard Schema validation, InferZodPortals, fmMcp mode prerequisites, and choosing between OttoAdapter and FetchAdapter auth.
npx skillsauth add proofgeist/proofkit typegen-fmdapiInstall 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.
ALWAYS use
@proofkit/typegento set up an @proofkit/fmdapi project. Typegen generates type-safe, schema-validated clients directly from your FileMaker layouts. It is the preferred and recommended path — manual client setup is only a fallback. Before writing any DataApi code, run typegen first.
fmrest extended privilege enabledpnpm add @proofkit/fmdapi
# .env
FM_SERVER=https://your-server.com # must start with https://
FM_DATABASE=MyFile.fmp12 # must end with .fmp12
# OttoFMS Data API key (recommended)
OTTO_API_KEY=dk_123456...789
# OR username/password (requires token store in production)
FM_USERNAME=admin
FM_PASSWORD=password
npx @proofkit/typegen@beta init
This creates proofkit-typegen-config.jsonc at your project root. Configure your layouts:
{
"$schema": "https://proofkit.proof.sh/typegen-config-schema.json",
"config": {
"type": "fmdapi",
"path": "schema",
"validator": "zod/v4",
"clientSuffix": "Layout",
"clearOldFiles": true,
"generateClient": true,
"layouts": [
{ "layoutName": "api_Customers", "schemaName": "Customers" },
{
"layoutName": "api_Invoices",
"schemaName": "Invoices",
"valueLists": "strict",
"strictNumbers": true
}
]
}
}
Run typegen to generate clients:
npx @proofkit/typegen@beta
Add a convenience script to package.json:
{
"scripts": {
"typegen": "npx @proofkit/typegen@beta"
"typegen:ui": "npm run typegen ui"
}
}
import { CustomersLayout } from "./schema/client";
const { data } = await CustomersLayout.findOne({
query: { id: "==123" },
});
console.log(data.fieldData);
The generated client includes full type safety and runtime schema validation — no manual type definitions needed.
schema/
generated/ # Auto-generated. NEVER edit.
Customers.ts
Invoices.ts
client/ # Auto-generated. NEVER edit.
Customers.ts
Invoices.ts
index.ts
Customers.ts # Override file. Safe to edit.
Invoices.ts # Override file. Safe to edit.
Override files live at schema/<schemaName>.ts. They re-export from generated/ and are never overwritten by typegen. Customize by modifying the Zod schema:
// schema/Customers.ts (override file)
import { z } from "zod/v4";
import {
ZCustomers as ZCustomers_generated,
} from "./generated/Customers";
export const ZCustomers = ZCustomers_generated
.omit({ active: true })
.extend({
active: z.coerce.boolean(),
});
export type TCustomers = z.infer<typeof ZCustomers>;
The generated client automatically imports from the override file, so transformations apply at runtime.
For portals, use InferZodPortals from @proofkit/fmdapi:
import type { InferZodPortals } from "@proofkit/fmdapi";
export const ZInvoicesPortals = {
lineItems: ZLineItems,
};
export type TInvoicesPortals = InferZodPortals<typeof ZInvoicesPortals>;
{
"config": {
"type": "fmdapi",
"envNames": {
"server": "MY_FM_SERVER",
"db": "MY_FM_DATABASE",
"auth": {
"apiKey": "MY_OTTO_KEY"
}
},
"layouts": [
{ "layoutName": "api_Users", "schemaName": "Users" }
]
}
}
Default env variable names when envNames is omitted: FM_SERVER, FM_DATABASE, OTTO_API_KEY (or FM_USERNAME/FM_PASSWORD for Fetch adapter).
The config key can be an array mixing fmdapi and fmodata entries, each with its own path and envNames.
FM MCP mode lets typegen fetch layout metadata from a locally running FileMaker file via the FM MCP proxy, without needing OttoFMS, a hosted server, or any credentials. Generated clients use WebViewerAdapter — FM MCP is only used during typegen.
{
"$schema": "https://proofkit.proof.sh/typegen-config-schema.json",
"config": {
"type": "fmdapi",
"fmMcp": { "enabled": true },
"layouts": [
{ "layoutName": "api_Contacts", "schemaName": "Contacts" }
]
}
}
fmMcp accepts an object with optional overrides:
enabled — true to enable (default when object is present)scriptName — FM script the proxy calls for Data API operations. Resolution: fmMcp.scriptName > webviewerScriptName > "execute_data_api"baseUrl — FM MCP server URL (default: http://127.0.0.1:1365). Can also be set via FM_HTTP_BASE_URL env varconnectedFileName — FileMaker file name. If omitted, auto-discovered from GET /connectedFiles and written back to configThe generated client uses WebViewerAdapter with webviewerScriptName if set, otherwise "execute_data_api".
Prerequisites:
GET http://127.0.0.1:1365/health should return OK)No .env file needed for typegen in fmMcp mode — baseUrl defaults, connectedFileName is auto-discovered.
Wrong:
// Hand-writing types that duplicate your FM layout
interface Customer {
name: string;
email: string;
phone: string;
}
const client = DataApi<Customer>({
adapter: new OttoAdapter({ /* ... */ }),
layout: "API_Contacts",
});
Correct:
# Generate types and clients from FileMaker
npx @proofkit/typegen@beta
// Use the generated client
import { ContactsLayout } from "./schema/client";
const { data } = await ContactsLayout.find({ query: { email: "[email protected]" } });
Hand-written types drift when FileMaker fields change, with no runtime protection. The typegen-generated client bundles a Standard Schema validator that catches field renames at runtime.
Source: packages/typegen/src/buildSchema.ts
Wrong:
// schema/generated/Customers.ts <-- editing this file
export const ZCustomers = z.object({
name: z.string(),
active: z.coerce.boolean(), // manually added
});
Correct:
// schema/Customers.ts <-- edit the override file instead
import { ZCustomers as ZCustomers_generated } from "./generated/Customers";
export const ZCustomers = ZCustomers_generated
.omit({ active: true })
.extend({ active: z.coerce.boolean() });
export type TCustomers = z.infer<typeof ZCustomers>;
Files in generated/ and client/ are overwritten on every typegen run; all customizations belong in the override files at the schema root.
Source: apps/docs/content/docs/typegen/customization.mdx
Wrong:
// fmschema.config.mjs
export default {
schemas: [{ layout: "Customers", schemaName: "Customers" }],
};
Correct:
// proofkit-typegen-config.jsonc
{
"$schema": "https://proofkit.proof.sh/typegen-config-schema.json",
"config": {
"type": "fmdapi",
"layouts": [
{ "layoutName": "Customers", "schemaName": "Customers" }
]
}
}
The config migrated from fmschema.config.mjs to proofkit-typegen-config.jsonc in v5. Run npx @proofgeist/fmdapi@latest upgrade to auto-migrate.
Source: apps/docs/content/docs/fmdapi/version-5.mdx
Wrong:
{
"envNames": {
"server": "https://my-server.com",
"auth": {
"apiKey": "dk_abc123secret"
}
}
}
Correct:
{
"envNames": {
"server": "MY_FM_SERVER",
"auth": {
"apiKey": "MY_OTTO_KEY"
}
}
}
envNames expects the names of environment variables (e.g. "MY_OTTO_KEY"), not their secret values; typegen reads the actual values from the environment at runtime.
Source: apps/docs/content/docs/typegen/config.mdx
Wrong:
// Manually adding a new field that was added in FileMaker
const ZCustomers = ZCustomers_generated.extend({
newField: z.string(),
});
Correct:
npx @proofkit/typegen@beta
After changing layouts in FileMaker, re-run typegen to regenerate types; the generated schemas are the source of truth for field names and types.
Source: packages/typegen/src/typegen.ts
Wrong:
{
"config": [
{ "type": "fmdapi", "validator": "zod/v4", "layouts": [] },
{ "type": "fmdapi", "validator": "zod/v3", "layouts": [] }
]
}
Correct:
{
"config": [
{ "type": "fmdapi", "validator": "zod/v4", "layouts": [] },
{ "type": "fmdapi", "validator": "zod/v4", "layouts": [] }
]
}
Zod v3 and v4 have incompatible APIs (z.infer vs z.output, different extend behavior); use one version across all configs to avoid runtime conflicts.
Source: apps/docs/content/docs/typegen/config.mdx
Wrong:
import { FmMcpAdapter } from "@proofkit/fmdapi/adapters/fm-mcp";
const client = DataApi({
adapter: new FmMcpAdapter({
baseUrl: "http://127.0.0.1:1365",
connectedFileName: "MyFile",
}),
layout: "Contacts",
});
Correct:
// Use the typegen-generated client (uses WebViewerAdapter internally)
import { ContactsLayout } from "./schema/client";
const { data } = await ContactsLayout.find({ query: { name: "==John" } });
FmMcpAdapter is internal to typegen's metadata fetching process. It only runs during code generation, never in production. Generated clients use WebViewerAdapter for runtime data access inside FileMaker Web Viewer.
Wrong:
# Agent configures both standard and fmMcp vars
FM_SERVER=https://fm.example.com
FM_DATABASE=MyFile.fmp12
OTTO_API_KEY=dk_abc123
FM_HTTP_BASE_URL=http://127.0.0.1:1365
Correct:
# fmMcp mode only — no server/db/auth needed
# baseUrl defaults to http://127.0.0.1:1365 if not set
# connectedFileName is auto-discovered if not set
FM_CONNECTED_FILE_NAME=MyFile
fmMcp mode bypasses the standard FM_SERVER/FM_DATABASE/auth env vars entirely. Setting both causes confusion when standard validation reports missing values.
If typegen fails to connect in fmMcp mode, do NOT suggest falling back to OttoFMS or FetchAdapter. The developer chose fmMcp because they don't have hosted credentials or are working with a local-only file.
Troubleshooting checklist:
curl http://127.0.0.1:1365/health — should return {"service":"fm-mcp","status":"ok"}curl http://127.0.0.1:1365/connectedFiles — should list the target fileWrong:
FM_SERVER=filemaker.example.com
Correct:
FM_SERVER=https://filemaker.example.com
@proofkit/fmdapi expects FM_SERVER to be a full URL including the https:// protocol prefix. Without it, requests fail with connection errors or malformed URL exceptions.
Source: apps/docs/content/docs/fmdapi/quick-start.mdx
The Data API requires the fmrest extended privilege on the FileMaker account. Without it, all API calls return authorization errors. Enable via File > Manage > Security > Privilege Sets > Extended Privileges.
Source: apps/docs/content/docs/cli/guides/getting-started.mdx
development
FileMaker WebDirect ProofKit Web Viewer runtime behavior refresh resilience session state localStorage browser resize reload same deployment embedded bundle avoid separate deployment avoid separate web server @proofkit/webviewer fmFetch callFMScript WebViewerAdapter WebDirect page refresh
development
webviewer fmFetch callFMScript WebViewerAdapter globalSettings setWebViewerName SendCallback window.FileMaker browser-only FileMaker Web Viewer script execution fire-and-forget FMScriptOption PerformScript callback fetchId handleFmWVFetchCallback
development
ENTRY POINT for @proofkit/fmodata projects. Generate TypeScript table schemas with entity IDs from FileMaker OData metadata using @proofkit/typegen. Covers proofkit-typegen-config.jsonc for OData mode, npx @proofkit/typegen setup, fmTableOccurrence generation, entity IDs (FMFID/FMTID), generated output structure, field exclusion, type overrides, InferTableSchema, env var configuration, OData prerequisites, fmodata privilege, and why typegen is required for entity ID correctness.
development
OData performance patterns for @proofkit/fmodata. Covers defaultSelect schema vs all, select() for minimal field fetching, select("all") override, pagination with top/skip, default 1000 record limit, batch operations for reducing round trips, entity IDs FMFID FMTID for rename resilience, null field query performance, getQueryString() debugging, relationship query performance testing, FileMaker OData optimization, avoiding OData service overload during testing.