formlab-mcp 0.6.0 → 0.6.3
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/package.json +3 -3
- package/tools/analytics.js +38 -2
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "formlab-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"mcpName": "io.github.juliu1980/formlab-mcp",
|
|
5
|
-
"description": "Read-only Model Context Protocol server for FormLab
|
|
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
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"formlab-mcp": "./index.js"
|
|
@@ -38,4 +38,4 @@
|
|
|
38
38
|
"url": "https://github.com/juliu1980/FormLab",
|
|
39
39
|
"directory": "mcp"
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|
package/tools/analytics.js
CHANGED
|
@@ -503,6 +503,11 @@ const _DOE_TYPE_LABELS = {
|
|
|
503
503
|
full: 'Full-factorial', pb: 'Plackett-Burman', ccd: 'Central Composite',
|
|
504
504
|
bbd: 'Box-Behnken', lhs: 'Latin Hypercube', simplex: 'Simplex-Lattice', dopt: 'D-optimal',
|
|
505
505
|
};
|
|
506
|
+
// The model key a design was optimised for. Only D-optimal stores one; the other
|
|
507
|
+
// generators impose their own structure, so there is nothing to report.
|
|
508
|
+
function _doeModelKey(d) {
|
|
509
|
+
return d && d.designType === 'dopt' ? ((d.settings || {}).doptModel || null) : null;
|
|
510
|
+
}
|
|
506
511
|
// Factor names come off the RECORD first so a design still reads correctly
|
|
507
512
|
// after an ingredient is renamed or deleted.
|
|
508
513
|
function _doeNamer(d, db) {
|
|
@@ -526,8 +531,28 @@ function _trimDoeDesign(d, db) {
|
|
|
526
531
|
factors: (d.factors || []).map(f => ({ ingredient: f.name || nameOf(f.ingredientId), low: f.low, high: f.high, unit: f.unit || '%' })),
|
|
527
532
|
constraints: (d.constraints || []).map(c => _doeConstraintText(c, nameOf)),
|
|
528
533
|
mixtureMode: !!d.mixtureMode,
|
|
534
|
+
// The model the design was built for. Lives in settings, so without this a
|
|
535
|
+
// caller listing designs can see "D-optimal" but not whether it was fitted
|
|
536
|
+
// as a process or a Scheffé (mixture) model — which changes what the runs
|
|
537
|
+
// and the D-efficiency mean.
|
|
538
|
+
model: _doeModelKey(d) || null,
|
|
539
|
+
modelLabel: _DOE_MODEL_LABELS[_doeModelKey(d)] || _doeModelKey(d) || null,
|
|
529
540
|
// Only D-optimal computes one; null here means "not applicable", not "failed".
|
|
530
541
|
dEfficiency: Number.isFinite(d.dEfficiency) ? +d.dEfficiency.toFixed(2) : null,
|
|
542
|
+
dEfficiencyKind: Number.isFinite(d.dEfficiency)
|
|
543
|
+
? (d.dEfficiencyKind || (/^scheffe-/.test(String((d.settings || {}).doptModel || '')) ? 'relative' : 'absolute'))
|
|
544
|
+
: null,
|
|
545
|
+
// The relative/absolute PAIR is the point: relative is the grade ("as good as
|
|
546
|
+
// this region allows"), absolute is what the rules cost. Shipping only the
|
|
547
|
+
// relative one lets a caller read 100% on a heavily restricted region and
|
|
548
|
+
// conclude the restrictions were free. Null unless the two actually differ.
|
|
549
|
+
// Both are percentages on a 0-100 scale. The absolute one goes very small on a
|
|
550
|
+
// tightly restricted region (a real mixture measured 0.027), so it keeps more
|
|
551
|
+
// decimals below 1 — toFixed(2) alone would report 0.03, and the UI's own
|
|
552
|
+
// toFixed(0) renders it "0%".
|
|
553
|
+
dEfficiencyAbsolute: Number.isFinite(d.dEfficiencyAbsolute)
|
|
554
|
+
? +d.dEfficiencyAbsolute.toFixed(d.dEfficiencyAbsolute < 1 ? 4 : 2)
|
|
555
|
+
: null,
|
|
531
556
|
droppedRuns: d.droppedRuns || 0,
|
|
532
557
|
response: d.responseText || null,
|
|
533
558
|
createdAt: d.createdAt,
|
|
@@ -615,7 +640,16 @@ const get_doe_design = {
|
|
|
615
640
|
const linked = (d.formulationIds || []).map(id => formsById.get(id)).filter(Boolean);
|
|
616
641
|
const out = {
|
|
617
642
|
...base,
|
|
618
|
-
|
|
643
|
+
// A balance factor is the component that absorbs the slack when the mixture
|
|
644
|
+
// is imposed on a design that knows nothing about it (the classical types).
|
|
645
|
+
// Simplex-Lattice and Scheffé D-optimal design every component on the
|
|
646
|
+
// simplex, so there is nothing to absorb and balanceFactorId is legitimately
|
|
647
|
+
// null. Passing that null to nameOf() fell through its `id || '?'` tail and
|
|
648
|
+
// answered "?", which reads as missing data rather than as "not applicable".
|
|
649
|
+
balanceFactor: d.mixtureMode && d.balanceFactorId ? nameOf(d.balanceFactorId) : null,
|
|
650
|
+
balanceFactorNote: d.mixtureMode && !d.balanceFactorId
|
|
651
|
+
? 'No balance factor: this design places its runs on the blend simplex directly, so every component is designed and each run already sums to the mixture total. Nothing has to absorb the remainder.'
|
|
652
|
+
: undefined,
|
|
619
653
|
settings,
|
|
620
654
|
augmentedFromProjectId: d.augmentedFromProjectId || null,
|
|
621
655
|
formulationsGenerated: (d.formulationIds || []).length,
|
|
@@ -623,7 +657,9 @@ const get_doe_design = {
|
|
|
623
657
|
notes: d.notes || null,
|
|
624
658
|
};
|
|
625
659
|
if (Number.isFinite(d.dEfficiency)) {
|
|
626
|
-
out.dEfficiencyNote =
|
|
660
|
+
out.dEfficiencyNote = out.dEfficiencyKind === 'relative'
|
|
661
|
+
? 'RELATIVE D-efficiency: this design against the best achievable for the same model and run budget IN ITS OWN DESIGN REGION, so 100% means "as good as this model and budget allow there". It is NOT the textbook absolute measure. It is used whenever the orthogonal ideal is unreachable by construction — a mixture (components collinear, so even a perfect mixture design scores about 33% on the absolute scale) or a region carved by linear constraints (an orthogonal design generally does not survive the cut). Do not compare this figure with an unconstrained design\'s D-efficiency or with another tool\'s.'
|
|
662
|
+
: 'ABSOLUTE D-efficiency: 100·|XᵀX|^(1/p)/N on coded factors, where 100% is the orthogonal ideal. Compare only against designs fitting the SAME model at the SAME run count; it is not a pass mark and does not transfer across model forms.';
|
|
627
663
|
}
|
|
628
664
|
if (args.include_runs !== false) {
|
|
629
665
|
const facs = d.factors || [];
|