Reference
Everything on this page is generated from the code by pnpm docs, and CI fails
if it drifts. If it says something, that is what the build does.
Environment variables
Only FM_USER and FM_PASS are required.
| Variable | What it is |
|---|---|
FM_USER, FM_PASS |
REQUIRED. The FileMaker account the connector logs in as. Needs the fmodata extended privilege to read tables, plus fmrest to expose scripts. Without them discovery returns 401. Credentials are read from the environment only. They are never written to the config file, so they stay out of backups and out of the hosted tier. |
FM_USER_<SLUG>, FM_PASS_<SLUG> |
Several FileMaker files on one connector? Each is a "profile" with its own slug, its own /mcp/<slug> endpoint and its own token. Give a profile its own account by suffixing the slug in upper snake case; anything unset falls back to FM_USER / FM_PASS above, so a single-file setup needs nothing here. profile "sales-live" -> FM_USER_SALES_LIVE / FM_PASS_SALES_LIVE |
FM_HOST, FM_DATABASE |
Optional. Prefills the wizard's Connection step. The wizard's own values win, and the published config is what the MCP endpoint actually uses at runtime. Must include the scheme and no trailing slash: it is used as a URL prefix, <FM_HOST>/fmi/data/vLatest/databases/<FM_DATABASE> |
ADMIN_TOKEN, MCP_TOKEN |
Optional. Guards /admin and /mcp. Leave blank and the connector generates them on first run, prints them, and persists them on the config volume so they survive a restart. Set them here to pin your own values. |
PORT |
Optional. Defaults to 8081. |
CONFIG_DIR |
Leave CONFIG_DIR unset. Local default is ./.ailimu-config and the Docker image sets /config itself. Setting it here would override the volume path. |
Credentials for several files
With more than one profile, each may have its own FileMaker account, named after the profile's slug in upper snake case, falling back to the unsuffixed pair:
FM_USER_SALES_LIVE=...
FM_PASS_SALES_LIVE=...
Credentials are read from the environment only. They are never written to the
config file and never handled by the wizard, which is why adding a profile with
its own account means editing .env and restarting.
Endpoints
| Method | Path | Auth | What |
|---|---|---|---|
GET |
/healthz |
none | Liveness. Returns {ok:true} and nothing about your data. |
GET |
/docs |
none | This manual, for the version you are running. |
GET |
/admin |
admin token | The setup wizard. The token is in the URL the connector prints on boot. |
POST |
/mcp |
mcp token | MCP endpoint. Serves the only profile when the connector holds exactly one. |
POST |
/mcp/:profile |
profile token | MCP endpoint for one profile, with its own token. |
GET |
/admin/api/status |
admin token | Credential readiness, defaults and the profile list. |
GET |
/admin/api/profiles |
admin token | Every profile this connector serves. |
GET |
/admin/api/profiles/:slug |
admin token | One profile's published config. |
GET |
/admin/api/profiles/:slug/schema |
admin token | The cached schema, so opening a profile costs no FileMaker read. |
DELETE |
/admin/api/profiles/:slug |
admin token | Delete a profile. Its endpoint stops immediately; FileMaker is untouched. |
POST |
/admin/api/databases |
admin token | Files this FileMaker server exposes to the account. |
POST |
/admin/api/test-connection |
admin token | Check the host, file and privileges before discovering. |
POST |
/admin/api/discover |
admin token | Read the schema: tables, row counts, scripts and layouts. |
POST |
/admin/api/sample |
admin token | Read a bounded row sample for named tables, to find unused columns. |
GET |
/admin/api/config |
admin token | The current config and the tools it generates. |
PUT |
/admin/api/config |
admin token | Publish. Takes effect immediately, with no restart. |
/healthz and /docs are deliberately unauthenticated: one is a liveness probe
that reveals nothing, the other is this manual.
Tools
A table called invoices and a script exposed as recalculate_totals produce:
| Tool | What it does |
|---|---|
get_invoices |
One record by its identifier. Every readable field. |
find_invoices |
Search. Returns the identifier plus the searchable fields, and the total match count. |
describe_invoices |
The fields a search cannot filter on, and how this table links to others. |
update_invoices |
Change a record. Only exists when writes are enabled and read-only is off. |
recalculate_totals |
One exposed FileMaker script. Only exists when read-only is off. |
Limits
| Limit | Value | Why |
|---|---|---|
| Rows sampled per table | 50 | Enough to tell an always-empty column from a sparse one. |
| Sample cell budget | 5000 | FileMaker's OData returns malformed JSON past roughly 280KB, so a wide table gets fewer rows. A 208-column table samples 24 rows. |
| Concurrent FileMaker requests | 8 | Discovery is one request per table; unbounded would hammer the server. |
| Tool result budget | 200KB | Whole rows are dropped to fit, and the tool says how many. |
| Tables per sample request | 500 | A malformed request cannot turn into an unbounded scan. |
Config file
One JSON file per profile, under profiles/ on the config volume. You should
not need to edit it by hand, but it is plain JSON so you can read it and keep it
in version control if you want to.
| Field | Type | Default |
|---|---|---|
tenantId |
string | required |
slug |
string | optional |
displayName |
string | required |
connection |
object | required |
connection.host |
string | required |
connection.database |
string | required |
connection.apiVersion |
string | "vLatest" |
connection.backend |
dataapi | odata | "dataapi" |
connection.scriptDatabase |
string | optional |
readOnly |
boolean | true |
resources |
array | [] |
scripts |
array | [] |
audit |
object | {} |
audit.enabled |
boolean | true |
audit.logWrites |
boolean | true |
audit.logReads |
boolean | false |
updatedAt |
string | optional |
lastDiscovered |
string | optional |
Known FileMaker behaviour
Things the FileMaker APIs do that Ailimu works around. They are listed here because they look like Ailimu bugs and are not:
- OData returns malformed JSON past roughly 280KB. The body arrives truncated mid-number. On a 208-column table, 30 rows come back intact while 40 and 50 do not, and 50 rows of 150 columns also fails: the limit tracks payload size, not row or column count. This is why samples are capped by cell count.
$count=trueinline is ignored. FileMaker answers the/$countsegment instead, and refuses that request ifAcceptnames a type.- Percent-encoded quotes, parens, commas and colons are rejected by FileMaker's OData parser, so those are sent raw in filters.
- Without
$top, up to 10,000 rows come back. Every read is bounded. - A container field is returned as the file itself, base64 inline. A single row can reach 1.4MB this way, so containers are never exposed.
- The Data API lists scripts by name only: no body, no parameters, and a
duplicated name cannot be addressed by
script=. - A table appears once per relationship path. A large file can expose 231 entity sets covering around 75 distinct tables. The wizard groups the copies and shows you the plainest name.