formlab-mcp 0.6.2 → 0.6.4
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 +1 -1
- package/tools/analytics.js +32 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "formlab-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
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
|
"type": "module",
|
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,11 +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,
|
|
531
542
|
dEfficiencyKind: Number.isFinite(d.dEfficiency)
|
|
532
543
|
? (d.dEfficiencyKind || (/^scheffe-/.test(String((d.settings || {}).doptModel || '')) ? 'relative' : 'absolute'))
|
|
533
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,
|
|
534
556
|
droppedRuns: d.droppedRuns || 0,
|
|
535
557
|
response: d.responseText || null,
|
|
536
558
|
createdAt: d.createdAt,
|
|
@@ -618,7 +640,16 @@ const get_doe_design = {
|
|
|
618
640
|
const linked = (d.formulationIds || []).map(id => formsById.get(id)).filter(Boolean);
|
|
619
641
|
const out = {
|
|
620
642
|
...base,
|
|
621
|
-
|
|
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,
|
|
622
653
|
settings,
|
|
623
654
|
augmentedFromProjectId: d.augmentedFromProjectId || null,
|
|
624
655
|
formulationsGenerated: (d.formulationIds || []).length,
|