formlab-mcp 0.6.50 → 0.6.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ Ask Claude (or another MCP client) things like:
30
30
  | Tool | Purpose |
31
31
  |---|---|
32
32
  | `list_formulations` | Filtered list of recipes |
33
- | `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…), the declared **finishedDensity** (g/mL of the mixed product; volume-targeted batches scale by it), and its **theoretical parameters computed** the way the app's Params tab does (Energy kcal/100g, NV %, Cost/kg…: value, unit, formula, spec pass/fail, and a `warning` when ingredients lack a value or carry mixed units) — the app's own engine, vendored in `mcp/vendor/` and run in an isolated `node:vm` context |
33
+ | `get_formulation` | Full record (incl. `versionLabel`, the label the user types, e.g. 1.0) + **version history** (`versionHistory`: one snapshot per save, v1, v2…, newest first, with the label each save had and its note) + 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…), the declared **finishedDensity** (g/mL of the mixed product; volume-targeted batches scale by it), and its **theoretical parameters computed** the way the app's Params tab does (Energy kcal/100g, NV %, Cost/kg…: value, unit, formula, spec pass/fail, and a `warning` when ingredients lack a value or carry mixed units) — the app's own engine, vendored in `mcp/vendor/` and run in an isolated `node:vm` context |
34
34
  | `find_similar_formulations` | Find formulas using a given ingredient ≥ threshold % |
35
35
  | `compare_formulations` | Pairwise side-by-side composition diff |
36
36
  | `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`) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formlab-mcp",
3
- "version": "0.6.50",
3
+ "version": "0.6.51",
4
4
  "mcpName": "io.github.juliu1980/formlab-mcp",
5
5
  "description": "Read-only Model Context Protocol server for FormLab \u2014 lets Claude (and other MCP clients) read and analyze your FormLab data, from a local export file OR your live cloud workspace.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.juliu1980/formlab-mcp",
4
4
  "description": "Read-only MCP for FormLab — let Claude query your formulation lab. Free reads a local export; Pro connects to your live cloud workspace with a dedicated read-only token.",
5
- "version": "0.6.50",
5
+ "version": "0.6.51",
6
6
  "websiteUrl": "https://formvix.com/mcp",
7
7
  "repository": {
8
8
  "url": "https://github.com/juliu1980/FormLab",
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "formlab-mcp",
16
- "version": "0.6.50",
16
+ "version": "0.6.51",
17
17
  "runtimeHint": "npx",
18
18
  "transport": {
19
19
  "type": "stdio"
@@ -19,6 +19,9 @@ function _trimFormulation(f) {
19
19
  name: f.name,
20
20
  description: f.description || '',
21
21
  status: f.status || 'Draft',
22
+ // The Version LABEL the user types (1.0, 2.1-alpha) — not the save count;
23
+ // the automatic history is get_formulation's versionHistory.
24
+ versionLabel: f.version != null ? String(f.version) : '',
22
25
  family: f.family || '',
23
26
  application: f.application || '',
24
27
  owner: f.owner || '',
@@ -83,7 +86,7 @@ const list_formulations = {
83
86
  const get_formulation = {
84
87
  definition: {
85
88
  name: 'get_formulation',
86
- description: 'Get full details for a single formulation including its composition (raw rows AND flattened wt-% leaves with sub-formulas expanded), step procedure, any recorded process-factor values (cure temp, mix time, RPM, pH — DOE inputs), and recent batches. Accepts either internal id or UID (e.g. FORM-001). Also returns `theoreticalParameters`: values computed from the composition the way the app Params tab does (e.g. Energy kcal/100g, NV %, Cost/kg) — each with name, value, unit, formula, spec + pass, and `warning` when ingredients lack a value the formula reads or carry mixed units (those rows are not counted). Quote these; do not recompute nutrition or NV yourself.',
89
+ description: 'Get full details for a single formulation including its composition (raw rows AND flattened wt-% leaves with sub-formulas expanded), step procedure, any recorded process-factor values (cure temp, mix time, RPM, pH — DOE inputs), and recent batches. Accepts either internal id or UID (e.g. FORM-001). Also returns `theoreticalParameters`: values computed from the composition the way the app Params tab does (e.g. Energy kcal/100g, NV %, Cost/kg) — each with name, value, unit, formula, spec + pass, and `warning` when ingredients lack a value the formula reads or carry mixed units (those rows are not counted). Quote these; do not recompute nutrition or NV yourself. `versionLabel` is the label the user types (e.g. 1.0); `versionHistory` is the automatic save history (v1, v2… one per save, newest first, up to 20, with the label each save had and its note) — two different things.',
87
90
  inputSchema: {
88
91
  type: 'object',
89
92
  properties: {
@@ -134,6 +137,20 @@ const get_formulation = {
134
137
  processFactors: (f.factorValues || []).filter(x => x && x.name).map(x => ({ name: x.name, value: x.value != null ? x.value : '', unit: x.unit || '' })),
135
138
  // Computed by the app's own engine (mcp/theoretical.js → mcp/vendor/).
136
139
  theoreticalParameters: theoreticalParams(db, 'formulation', f.id),
140
+ // Automatic history: one snapshot per save (v1, v2…), newest first,
141
+ // with the Version label each save had. Independent of versionLabel.
142
+ versionHistory: (() => {
143
+ const vs = (db.formulaVersions || []).filter(v => v && v.formulationId === f.id)
144
+ .sort((a, b) => (b.versionNumber || 0) - (a.versionNumber || 0));
145
+ return {
146
+ count: vs.length,
147
+ versions: vs.slice(0, 20).map(v => ({
148
+ version: v.versionNumber, label: (v.snapshot && v.snapshot.version != null) ? String(v.snapshot.version) : '',
149
+ savedAt: v.snapshotAt || null, savedBy: v.snapshotBy || '', note: v.changeNote || '',
150
+ ingredientRows: ((v.snapshot && v.snapshot.composition) || []).length, stepCount: ((v.snapshot && v.snapshot.steps) || []).length,
151
+ })),
152
+ };
153
+ })(),
137
154
  batchCount: batches.length,
138
155
  sampleCount: samples.length,
139
156
  };