formlab-mcp
v0.6.48
Published
Read-only Model Context Protocol server for FormLab — lets Claude (and other MCP clients) read and analyze your FormLab data, from a local export file OR your live cloud workspace.
Maintainers
Readme
formlab-mcp
A read-only Model Context Protocol server for FormLab. Lets Claude (or any MCP-compatible AI assistant) read and analyze your local FormLab database — your formulations, ingredients, batches, samples and test reports — without your data ever leaving your machine.
Local-first + AI-native. Your proprietary recipes stay on your laptop; only the LLM's answer to your question travels.
Listed in the official MCP Registry as io.github.juliu1980/formlab-mcp.
What it does
Ask Claude (or another MCP client) things like:
- "Which of my formulations use silicone fluid at 5% or more?"
- "Compare the composition of Formula FORM-001 and FORM-004 side by side."
- "Compare the test results of Coating A and Coating B — which parameters differ, and does either fail?"
- "What parameters fail most often in Q2 testing?"
- "Show me the DOE matrix at sample grain for all 'Anti-aging serum' family formulas."
- "What's untested? Which of my approved formulas have no measurements yet?"
- "Map the design space of my WallPaint project — which ingredients vary, and what combination haven't I tried yet?"
- "List all peptide ingredients with purity above 95%."
- "Which botanicals do I source from Madagascar?"
- "Find ingredients where the sequence contains KTTKS."
Tools exposed
| Tool | Purpose |
|---|---|
| list_formulations | Filtered list of recipes |
| get_formulation | Full record + flattened wt-% composition (sub-formulas expanded), the ordered step procedure, user custom fields, any process-factor values (DOE inputs — cure temp, mix time, RPM, pH…), and the declared finishedDensity (g/mL of the mixed product; volume-targeted batches scale by it) |
| find_similar_formulations | Find formulas using a given ingredient ≥ threshold % |
| compare_formulations | Pairwise side-by-side composition diff |
| list_ingredients | Filtered list of raw materials. Filters: family, supplier, name_contains, in_stock_only, ingredient_class (small-molecule / surfactant / polymer / extract / fragrance / pigment / sequence / mixture), sequence_contains (e.g. KTTKS → Matrixyl), taxon_contains (e.g. Centella) |
| get_ingredient | Full record + supplier / cost ($/kg) / stock / chemical name / molecular weight / storage / formulations using it, plus GHS safety (pictograms, H/P codes, signal word), per-jurisdiction regulatory status, approved sources (the suppliers / manufacturers qualified to supply this material — ASL/AML), and inventory lots (balances + expiry + the source each was received from). Returns every class-specific sub-object when present: sequence (peptide / oligo), taxon (NCBI ID + scientific name), ingredientClass, extractDetails, sequenceDetails, polymerDetails, surfactantDetails, pigmentDetails, fragranceDetails |
| list_lots | Inventory lots across ingredients (each a received batch with its own remaining balance, supplier, expiry, unit cost, status — active / hold / quarantine / rejected; held lots are blocked from consumption, with a holdReason — and the approved source it was received from: source.supplier + source.manufacturer, where recalls hinge on the manufacturer). Each lot also reports its Certificate of Analysis (coa: linked, or matched by lot number) and coaMissing for an in-stock lot without one. Filters: ingredient_id, expiring_within_days, status (e.g. hold to find lots held for a recall), missing_coa |
| list_suppliers | Supplier / manufacturer records with roll-ups — ingredients approved, lots received / on hand (+ value at paid cost), qualified / trial / disqualified source counts, last receipt; lots count by supplier and by manufacturer |
| get_supplier | One supplier in full: record, every ingredient it's an approved source for, every lot from it, and the recall trace (batches that consumed those lots → products, shipments, samples, sub-batches; lots on hold flagged) |
| list_documents | The document register (same as the app's Documents page): ingredient SDS / PDS / CoA / other with lot, version, source and expiry; test and notebook attachments; per-record uploads and links (formulas, batches, samples, suppliers, equipment…). File metadata only, never the text inside a file, with who added each file (addedBy) and when. Filters: type, record, linked, query, lot, format, expiry, expiring_within_days, without_expiry, uploaded_within_days, added_by. A summary block carries the page's tile counts: expired, expiring in 30 days, SDS/CoA without expiry, ingredients missing an SDS (+ coverage %), lots in stock without a CoA, added last 7 days |
| list_inventory | Portfolio stock rollup — one row per ingredient with on-hand qty, summed lot balance, nearest expiry, and a low-stock flag (on-hand ≤ reorderThreshold, or zero). Filters: low_stock_only, expiring_within_days, family |
| list_batches | Filtered list of production / lab-prep events |
| get_batch | Full record + actual composition, measured/derived actual volume (mass ÷ density, never a sum of per-ingredient volumes) and actual mass (yield, else summed as-prepared inputs — mL rows through that ingredient's density — else measured volume × density), estimatedVolume (mass ÷ the formula's finished density when no actual exists, flagged estimate: true), plannedMass for volume-target batches (target × finished density) and formulaFinishedDensity, the process method + any process-factor values (DOE inputs), samples + blend lineage, and — as a finished-goods lot — its storage location + finished-goods stockLedger (shipments out + adjustments; shippedQty total) |
| list_samples | Filtered list of physical specimens |
| get_sample | Full record + canonical variant + test reports + blend lineage |
| list_test_results | Filtered list of test reports (by sample, parameter, measured-value range (value_min/value_max — a curve at its final point, a distribution at its spec's statistic or D50), grade (min_grade/max_grade on a ranked scale), text value (value_equals), run condition (condition), date, or lab) |
| get_test_result | Full report: every measurement's value, spec, any per-measurement run conditions, and the resolved instrument (instrumentSource: row / method / run). Complex types carry a representative (time-series → final, distribution → D50; a distribution also its bins and, when its spec names a statistic, judged at it); a ranked grade carries its scale and rank; a time series adds aggregated (per-timestamp mean ± sd) and pointReplicates (repeat readings at one t). Report-level seriesReplicates lists parameters measured 2+ times as separate rows — point vs series replicates are distinct. Each measurement also gives its effective spec (effectiveSpec, specSource: override / template / parameter / none) and passFail — panel and Test Method specs apply exactly as in the app, and every pass/fail count across the tools uses the same rule |
| get_doe_matrix | Pivot matrix (CSV by default) — rows × ingredients × parameters. A parameter measured under two or more run conditions (storage 25 °C vs 40 °C) is one column per condition (pH, pH · 40 °C; the bare name is the base arm), never averaged together — same columns, in the same order (grouped by measurement type, the test panel's order within a group), as the app's DOE Matrix |
| find_failures | Parameters ranked by failure rate (failing ÷ all readings; replicates in one report count once), lowN flag under 5 readings, plus formulations ranked by share of failing reports — same numbers as the app's Test Analytics → Failures |
| get_coverage_matrix | TEST coverage by formulation, batch or sample (grain) × parameter. Each cell is the latest reading by test date (same-day reports averaged, any fail → fail) with its date and how many reports measured it; one column per run condition when a parameter was measured under two or more — same rollup as the app's Test Analytics → Coverage. A ranked scale shows its median grade (with its rank) when several readings share the date, a text result its most frequent value, a distribution with no D50 its bins; columns in the app's order (type groups, the test panel's order) |
| compare_test_results | Test results side by side across 2+ formulas, batches, samples or reports — the app's Compare Results table. Parameters grouped by measurement type (Numbers, Curves, Distributions, Ranked scales, Text results) and, within a group, in the test panel's order (order: method, default) or az. One cell per item × parameter: aggregation latest (default; one value per test date, replicates averaged ± sd), mean (± sd between dates), median (min–max) or trend (change from the first date). Curves compare at their final point, distributions at D50; ranked scales and text show the latest reading (with its rank); a parameter measured under 2+ run conditions is split into arms, never averaged. differs flags a >5% spread across items, as the app highlights |
| compare_batches | Reproducibility of 2+ runs (ideally of one formula): each run's yield %, actual produced mass, cost/kg; per-ingredient drift across the runs vs the formula's proposed wt-%; and the biggest outlier run. basis: wt_percent (default) or amount |
| get_batch_pivot | Production analytics — aggregate batches by group_by (formula / status / month / prepared_by / project) × metric (count, avg_yield_pct, total_produced_kg, avg_cost_per_batch, total_samples, total_tests), with share + total for additive metrics. Optional batch_uids scope + status filter |
| get_project_pivot | Portfolio analytics — aggregate projects by group_by (status / phase / priority / business_unit / site / customer / lead, or cf:<custom field>) × metric (count, total_formulas / batches / samples, total_cost, avg_formulas / batches / cost_per_batch), rolling child activity + cost up by any project attribute. Optional project_uids scope |
| get_stability | Grounded stability / shelf-life analysis for a formulation, sample or batch × parameter (auto-detected): time-series, drift (per-month slope + R²), an I-chart (mean ±3σ + out-of-control points), spec status, and an ICH-Q1E-flavored projected shelf life (point + 95%-CI crossing), all on one point per test date per run condition (replicates averaged, never separate time points), split by storage condition. A ranked-scale (ordinal) or text parameter returns its step / pass-fail series instead: per-condition points, firstFail, latest, scaleShift |
| get_design_space | COMPOSITION design space for one project, several (projects) or any formula list (formulas, across projects — like the app's Scope → DOE Matrix): which ingredients vary and over what observed wt-% ranges (ranked), an occupied-region summary ("where you've been"), the biggest untested interior gap as ready-to-seed DOE ranges (a combination you could have made but skipped), and an optional standardised 2-component PCA of all varying ingredients — loadings, variance-explained, scree, and a full-dimensional gap. Optional axes (choose the 2–3 axes), by_role (map ingredients summed by primary Function, e.g. Pigment / Solvent / Film Former — no gap, roles aren't DOE factors) and property (highest / lowest formula plus the ingredients whose level tracks it, Spearman ρ). Reports formulas that aren't on the map and the spread on it. Matches the in-app Design Space Viewer exactly. Answers "where haven't I explored?" / "what should I formulate next?" |
| list_doe_designs | Saved DOE designs — the recipe behind each batch of runs: design type, factors + ranges, constraints, run count, D-efficiency. Filters: project, design_type, constrained_only, name_contains |
| get_doe_design | One design in full: constraints in plain language, the model it was optimised for, generation settings (run budget, replicates, centre points, seed) and the complete run matrix with the formulation each run became |
| find_by_smarts | SMARTS-pattern substructure search across every ingredient with a SMILES; peptides that only store a sequence are searched with the structure derived from it, as the app draws them (smilesDerived: true). Requires @rdkit/rdkit (optional dependency — install with npm install @rdkit/rdkit in mcp/ if you get a "not installed" error). Examples: c1ccccc1 (any aromatic 6-ring), [OX2H1] (any hydroxyl), C(=O)O (carboxylic acid), [F,Cl,Br,I] (any halogen). |
| list_equipment | Filtered list of the Equipment registry (mixers, ovens, viscometers, balances…). Filters: category, status, manufacturer, name_contains |
| get_equipment | Full equipment record + usedIn (panels, step presets, test methods, formula steps, batches, testReports resolved to it), usage (reports / formulas / batches / samples / pass rate / top formulas) and measured (per parameter: mean here vs mean on other instruments, biasPct) |
| list_test_methods | Filtered list of Test Methods (the parameter library). Filter by analyte (the property measured, e.g. Viscosity — finds every method for it), category, name_contains, include_retired |
| get_test_method | One Test Method: full definition, run conditions, analyte siblings, using panels, plus usage, valueSummary (mean / sd / min / max) and byInstrument |
| list_step_presets | Filtered list of Step Presets (reusable procedure steps: duration / temp / RPM / equipment). Filters: industry, name_contains |
| get_step_preset | Full step-preset record incl. the multi-value equipment list ({name, equipmentId}) |
| list_test_panels | Filtered list of Test Panels (reusable column-sets — the parameters measured together on a sample). Filters: industry, name_contains |
| get_test_panel | One Test Panel: ordered parameters with resolved method UIDs + specs, plus usage (reports created from it) and performanceByParameter |
| list_projects | Filtered list of projects (the buckets formulations are filed under) + per-project formulation count. Filters: status, name_contains |
| get_project | Full project record + the formulations filed under it |
| list_notebook_entries | ELN feed — dated authored notes attached to records. Append-only: a withdrawn note is kept with retracted = {at, by, reason}. Filters: entity_type, entity_id, author, text_contains, since, until, retracted |
| get_notebook_entry | One notebook entry — full body, author, attachment metadata, resolved linked record, and retracted when withdrawn |
Install
# Run directly without installing — recommended:
npx formlab-mcp /path/to/formlab-export.json
# Or install globally for repeated use:
npm install -g formlab-mcp
formlab-mcp /path/to/formlab-export.jsonFor local development from this repo:
cd mcp
npm install
node index.js /path/to/formlab-export.jsonRequires Node 18+.
Get your FormLab export
- Open FormLab
- Sidebar → ⇅ Import / Export
- Click Export — saves
formlab-export-YYYY-MM-DD.jsonto your downloads folder - Point this MCP at the downloaded file
The MCP server watches the file — re-export from FormLab and the next tool call sees the fresh data without restarting the server.
Or: LIVE cloud mode (no export needed)
Instead of an export file, point the server at your live cloud workspace so it's always current.
- In FormLab (Pro): Settings → Account → AI tools (MCP) → Connect…
- Click Create token, copy the generated config, and paste it into Claude Desktop.
The config sets a single env var, which switches the server into cloud mode:
| Env var | Value |
|---|---|
| FORMLAB_MCP_TOKEN | a dedicated, read-only, revocable token (flmcp_…) |
| FORMLAB_SUPABASE_URL | (optional) override the backend URL — defaults to production |
| FORMLAB_SUPABASE_ANON_KEY | (optional) override the publishable key — defaults to production |
| FORMLAB_REFRESH_SECONDS | (optional) poll interval, default 60 |
When FORMLAB_MCP_TOKEN is present, the server POSTs it to FormLab's mcp-data Edge Function, which returns your workspace scoped to you by row-level security and re-fetches every FORMLAB_REFRESH_SECONDS. It's read-only — enforced at the database (a dedicated mcp_readonly Postgres role with SELECT-only grants), not by trust.
Security. The token grants read-only access to one workspace and holds no account session — only a SHA-256 hash is stored server-side, and it never rotates. Treat the config like a password (don't share or commit it). Revoke or re-mint any time from Settings → Account → AI tools (MCP) → Connect.
Cloud mode has no extra dependency — it's a plain fetch.
Wire it up to Claude Desktop
Add this to your claude_desktop_config.json (on macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"formlab": {
"command": "npx",
"args": ["formlab-mcp", "/Users/you/Downloads/formlab-export-2026-05-30.json"]
}
}
}Or for local-dev (from the repo):
{
"mcpServers": {
"formlab": {
"command": "node",
"args": [
"/Users/you/formlab/mcp/index.js",
"/Users/you/Downloads/formlab-export-2026-05-30.json"
]
}
}
}Restart Claude Desktop. You should see a hammer icon indicating tools are available, and FormLab's 40 tools become callable in any conversation.
Wire it up to Claude Code
claude mcp add formlab npx formlab-mcp /Users/you/Downloads/formlab-export.jsonOr with a stored env var:
export FORMLAB_EXPORT=/Users/you/Downloads/formlab-export-2026-05-30.json
claude mcp add formlab npx formlab-mcpPrivacy and architecture
- No network calls. The server runs on stdio between Claude and your local Node process. Your data file never leaves your disk.
- No FormLab cloud. FormLab itself is a local-first app; the MCP follows the same model.
- Hot-reload on file change. Re-export from FormLab while the server is running — the next tool call picks up fresh data.
- Read-only. Tier 1 cannot mutate your FormLab data. (Write tools are planned for a future paid "Pro" tier — register / batch / log-test mutations with cascade-safe confirmation.)
Data shape
The server accepts both FormLab export shapes:
- Wrapped FAIR export:
{ "fair": {...metadata...}, "data": {...db...} } - Legacy bare-db JSON:
{...db...}directly
The wrapped form is the default since 2026; the legacy form is supported for older exports.
Tier 2 (planned, paid)
Live file-sync with write tools: create_formulation, update_formulation, log_test_result, create_batch, etc. Mutations from Claude write back to the same JSON FormLab reads. Conflict detection, cascade-safe deletes, schema versioning.
Not yet shipped — see the FormLab roadmap.
License
MIT
