formlab-mcp 0.6.57 → 0.6.59

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/LICENSE ADDED
@@ -0,0 +1,46 @@
1
+ formlab-mcp is distributed under two licenses. Part 1 (MIT) covers every file
2
+ in this package except the vendor/ directory. Part 2 (proprietary) covers the
3
+ vendor/ directory.
4
+
5
+
6
+ PART 1: MIT LICENSE (all files except vendor/)
7
+
8
+ Copyright (c) 2026 FormVix Software Solutions LLC
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+
29
+ PART 2: PROPRIETARY LICENSE (vendor/)
30
+
31
+ Files covered: vendor/cost.js, vendor/nesting.js, vendor/theoretical-params.js,
32
+ vendor/equipment-calibration.js, and any other file in the vendor/ directory.
33
+
34
+ Copyright (c) 2026 FormVix Software Solutions LLC. All rights reserved.
35
+
36
+ These files are included solely so formlab-mcp can compute values identically
37
+ to the FormLab app. You may use them only as part of an unmodified formlab-mcp
38
+ installation. No other license is granted: you may not copy, modify, extract,
39
+ redistribute or reuse them, in whole or in part, separately from formlab-mcp.
40
+
41
+ THESE FILES ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46
+ OUT OF OR IN CONNECTION WITH THESE FILES OR THE USE OR OTHER DEALINGS IN THEM.
package/README.md CHANGED
@@ -1,14 +1,13 @@
1
1
  # formlab-mcp
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/formlab-mcp.svg)](https://www.npmjs.com/package/formlab-mcp)
4
- [![license](https://img.shields.io/npm/l/formlab-mcp.svg)](https://www.npmjs.com/package/formlab-mcp)
5
4
  [![FormLab](https://img.shields.io/badge/site-formvix.com-2563eb.svg)](https://formvix.com)
6
5
 
7
6
  A **read-only Model Context Protocol server** for [FormLab](https://formvix.com). Lets Claude (or any MCP-compatible AI assistant) read and analyze your FormLab data: formulations, ingredients, batches, samples and test reports, from a local export file or your live cloud workspace.
8
7
 
9
8
  > In file mode the server makes no network calls of its own. What a tool returns goes to the model your MCP client uses (for Claude Desktop, Anthropic), so only connect data you're comfortable sending to that provider. See [Privacy and data flow](#privacy-and-data-flow).
10
9
 
11
- Listed in the official MCP Registry as `io.github.juliu1980/formlab-mcp`. The source ships in the npm package (MIT).
10
+ Listed in the official MCP Registry as `io.github.juliu1980/formlab-mcp`. The server code is MIT-licensed. It bundles FormLab's calculation engine (`vendor/`) under a proprietary license that allows use only as part of formlab-mcp; see LICENSE.
12
11
 
13
12
  ## What it does
14
13
 
@@ -82,7 +81,7 @@ npm install -g formlab-mcp
82
81
  formlab-mcp /path/to/formlab-export.json
83
82
  ```
84
83
 
85
- To read or run the source, unpack it from npm (it ships in the package, MIT):
84
+ To read or run the source, unpack it from npm (the source ships in the package; see [License](#license)):
86
85
 
87
86
  ```bash
88
87
  npm pack formlab-mcp && tar xzf formlab-mcp-*.tgz
@@ -207,4 +206,4 @@ The wrapped form is the default since 2026; the legacy form is supported for old
207
206
 
208
207
  ## License
209
208
 
210
- MIT
209
+ The server code is MIT-licensed. It bundles FormLab's calculation engine (`vendor/`) under a proprietary license that allows use only as part of formlab-mcp; see LICENSE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formlab-mcp",
3
- "version": "0.6.57",
3
+ "version": "0.6.59",
4
4
  "mcpName": "io.github.juliu1980/formlab-mcp",
5
5
  "description": "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.",
6
6
  "homepage": "https://formvix.com/mcp",
@@ -19,7 +19,8 @@
19
19
  "tools/",
20
20
  "vendor/",
21
21
  "server.json",
22
- "README.md"
22
+ "README.md",
23
+ "LICENSE"
23
24
  ],
24
25
  "scripts": {
25
26
  "start": "node index.js",
@@ -45,5 +46,5 @@
45
46
  "design-of-experiments",
46
47
  "claude"
47
48
  ],
48
- "license": "MIT"
49
+ "license": "SEE LICENSE IN LICENSE"
49
50
  }
package/server.json CHANGED
@@ -2,13 +2,13 @@
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 server for FormLab formulation data: a local export file or your live cloud workspace.",
5
- "version": "0.6.57",
5
+ "version": "0.6.59",
6
6
  "websiteUrl": "https://formvix.com/mcp",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "formlab-mcp",
11
- "version": "0.6.57",
11
+ "version": "0.6.59",
12
12
  "runtimeHint": "npx",
13
13
  "transport": {
14
14
  "type": "stdio"
@@ -123,7 +123,7 @@ function _trimMeasurement(m, t, db) {
123
123
  binCount: Array.isArray(m.bins) ? m.bins.length : 0,
124
124
  ...complex,
125
125
  acceptanceCriteria: m.acceptanceCriteria || null,
126
- // Effective spec + where it came from (override | template | parameter | none)
126
+ // Effective spec + where it came from (override | template | parameter | result: a Pass / Fail text result judged as written | none)
127
127
  // and the app's pass/fail verdict under it.
128
128
  ...(() => { const r = _resolveSpec(m, t && t.templateId, db); return { effectiveSpec: r.spec || null, specSource: r.source, passFail: _evalPassFail(m, t && t.templateId, db) }; })(),
129
129
  // Captured run conditions this reading was measured under (e.g.
@@ -822,8 +822,19 @@ function _resolveSpec(m, templateId, db) {
822
822
  const lib = (store.parameters || []).find(p => p && p.id === m.parameterId);
823
823
  if (lib && lib.acceptanceCriteria) return { spec: lib.acceptanceCriteria, source: 'parameter' };
824
824
  }
825
+ // As the app (24b): no spec anywhere, a text result that is itself a verdict (Pass / Fail) is judged as written.
826
+ if (_measType(m) === 'categorical' && /^(pass|fail)$/.test(_catNorm(m.value))) return { spec: { requiredValue: 'pass', fromResult: true }, source: 'result' };
825
827
  return { spec: null, source: 'none' };
826
828
  }
829
+ // Text results, as the app (js/views/testing.js, 24b): a spec may accept several results
830
+ // ("=Clear | Slightly hazy"); Pass / Passed and Fail / Failed read as one word each.
831
+ function _catNorm(v) {
832
+ const s = String(v == null ? '' : v).trim().toLowerCase();
833
+ return /^pass(ed)?$/.test(s) ? 'pass' : /^fail(ed)?$/.test(s) ? 'fail' : s;
834
+ }
835
+ function _catPasses(required, value) {
836
+ return String(required == null ? '' : required).split('|').map(_catNorm).filter(Boolean).includes(_catNorm(value));
837
+ }
827
838
 
828
839
  const _MEAS_TYPES = ['scalar', 'ordinal', 'categorical', 'time_series', 'distribution'];
829
840
  function _measType(m) {
@@ -867,7 +878,7 @@ function _evalPassFail(m, templateId, db) {
867
878
  if (type === 'categorical') {
868
879
  if (!raw) return 'untested';
869
880
  if (!crit.requiredValue) return 'no-criteria';
870
- return raw.toLowerCase() === String(crit.requiredValue).toLowerCase() ? 'pass' : 'fail';
881
+ return _catPasses(crit.requiredValue, raw) ? 'pass' : 'fail';
871
882
  }
872
883
  // As the app (12y): one representative value against the range — a curve's
873
884
  // FINAL point, a distribution's statistic named by the spec (crit.stat:
package/vendor/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Vendored app files — do not edit here
2
2
 
3
+ **License:** these files are proprietary (Copyright (c) 2026 FormVix Software Solutions LLC),
4
+ licensed only for use as part of an unmodified formlab-mcp installation. See Part 2 of
5
+ `../LICENSE`. They carry no header of their own because they must stay byte-identical
6
+ to the app's files (`test/mcp-theoretical.test.js`), which is what keeps the numbers identical.
7
+
3
8
  Exact copies of the FormLab app's theoretical-parameter engine, so the MCP
4
9
  server computes the same numbers the Params tab shows (the npm package can't
5
10
  reach files outside `mcp/`):
package/vendor/cost.js CHANGED
@@ -1993,7 +1993,7 @@ function renderWeighoutPanel(w, actuals, formula, copyOnclick, noun) {
1993
1993
  const _woVolTarget = (typeof isVolUnit === 'function') && isVolUnit(w.unit);
1994
1994
  const qsTag = r => (!r.qs) ? '' :
1995
1995
  ` <span style="font-size:9.5px;color:var(--dk-sky-fg, #0369a1);font-weight:600;background:var(--dk-blue-bg, #eff6ff);border:1px solid var(--dk-blue-line, #bfdbfe);border-radius:4px;padding:0 3px;vertical-align:middle;white-space:nowrap" title="${_woVolTarget ? `Balance ingredient — add it up to the ${w.qty} ${esc(w.unit)} mark rather than to this number, then log what actually went in` : 'Balance ingredient, set to hold the batch at its target size'}">${_woVolTarget ? `q.s. to ${w.qty} ${esc(w.unit)}` : 'q.s.'}</span>`;
1996
- const dColor = rel => rel == null ? '#94a3b8' : (rel < 0.02 ? '#15803d' : rel < 0.10 ? '#b45309' : '#b91c1c');
1996
+ const dColor = rel => rel == null ? 'var(--dk-faint, #94a3b8)' : (rel < 0.02 ? 'var(--dk-green-fg, #15803d)' : rel < 0.10 ? 'var(--dk-amber-fg, #b45309)' : 'var(--dk-red-fg, #b91c1c)');
1997
1997
  // Tint a whole row when it deviates, so off-plan ingredients pop at a glance
1998
1998
  // (matches the composition-% table's row tinting). On-target rows stay white.
1999
1999
  const dRowBg = rel => (rel == null || rel < 0.02) ? '' : (rel < 0.10 ? 'background:var(--dk-amber-bg, #fffbeb)' : 'background:var(--dk-red-bg, #fef2f2)');
@@ -2590,7 +2590,7 @@ function entityCostValueHtml(o) {
2590
2590
  const msg = (why === 'needs-density')
2591
2591
  ? `⚠ add a density to the formula to price ${esc(o.unit || 'this unit')} by mass`
2592
2592
  : `can't convert ${esc(o.unit || 'this unit')} to mass`;
2593
- const col = (why === 'needs-density') ? '#b45309' : 'var(--text-muted)';
2593
+ const col = (why === 'needs-density') ? 'var(--dk-amber-fg, #b45309)' : 'var(--text-muted)';
2594
2594
  return `<span style="color:var(--text-muted)">—</span> <span style="font-size:11px;color:${col}">${msg}</span>${editBtn}`;
2595
2595
  }
2596
2596
  const breakdown = `<span style="font-size:11px;color:var(--text-muted);margin-left:6px">${esc(String(o.qty))} ${esc(o.unit || '')} × ${money(perKg)}/kg</span>`;