formlab-mcp 0.6.26 → 0.6.27
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/server.json +2 -2
- package/tools/analytics.js +13 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "formlab-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.27",
|
|
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.
|
|
5
|
+
"version": "0.6.27",
|
|
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.
|
|
16
|
+
"version": "0.6.27",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
package/tools/analytics.js
CHANGED
|
@@ -983,24 +983,30 @@ function _shelfLife(points, spec) {
|
|
|
983
983
|
for (let tt = lastX; tt <= lastX + 120; tt += 0.25) {
|
|
984
984
|
if (fit.slope < 0 ? bound(tt) <= limit : bound(tt) >= limit) { ciRemaining = tt - lastX; break; }
|
|
985
985
|
}
|
|
986
|
-
return { edge, limit, remainingMonths: remaining, ciRemainingMonths: ciRemaining, r2: fit.r2, elapsedMonths: lastX };
|
|
986
|
+
return { edge, limit, remainingMonths: remaining, ciRemainingMonths: ciRemaining, r2: fit.r2, elapsedMonths: lastX, lastDate: points[points.length - 1].date };
|
|
987
987
|
}
|
|
988
988
|
|
|
989
989
|
// Same verdict rule as the app (js/views/stability.js stabShelfLifeAssess):
|
|
990
990
|
// months from T0 vs the target shelf life — short (trend crosses first),
|
|
991
991
|
// marginal (only the 95% bound does), meets; no target → by time left
|
|
992
992
|
// (<3 mo high, <12 mo medium). Nothing past the 5-year horizon is reported.
|
|
993
|
-
function _shelfLifeAssess(sl, targetMonths) {
|
|
993
|
+
function _shelfLifeAssess(sl, targetMonths, now) {
|
|
994
994
|
if (!sl || !(sl.r2 >= 0.3) || !(sl.remainingMonths > 0.05)) return null;
|
|
995
995
|
const pointTotal = sl.elapsedMonths + sl.remainingMonths;
|
|
996
996
|
const ciTotal = sl.ciRemainingMonths != null ? sl.elapsedMonths + sl.ciRemainingMonths : null;
|
|
997
|
+
// Overdue: the projected crossing (last test + remaining) is already past.
|
|
998
|
+
const crossMs = sl.lastDate ? sl.lastDate.getTime() + sl.remainingMonths * _MS_PER_MONTH : null;
|
|
999
|
+
const nowMs = now != null ? +now : Date.now();
|
|
1000
|
+
const overdue = crossMs != null && crossMs < nowMs;
|
|
1001
|
+
const crossDate = crossMs != null ? new Date(crossMs).toISOString().slice(0, 10) : null;
|
|
997
1002
|
if (targetMonths) {
|
|
998
1003
|
const verdict = pointTotal < targetMonths ? 'short' : (ciTotal != null && ciTotal < targetMonths) ? 'marginal' : 'meets';
|
|
999
1004
|
if (verdict === 'meets' && sl.remainingMonths > 60) return null;
|
|
1000
|
-
|
|
1005
|
+
const level = (overdue && verdict !== 'meets') ? 'high' : verdict === 'short' ? 'high' : verdict === 'marginal' ? 'medium' : 'info';
|
|
1006
|
+
return { verdict, level, pointTotal, ciTotal, overdue, crossDate };
|
|
1001
1007
|
}
|
|
1002
1008
|
if (sl.remainingMonths > 60) return null;
|
|
1003
|
-
return { verdict: null, level: sl.remainingMonths < 3 ? 'high' : sl.remainingMonths < 12 ? 'medium' : 'info', pointTotal, ciTotal };
|
|
1009
|
+
return { verdict: null, level: (overdue || sl.remainingMonths < 3) ? 'high' : sl.remainingMonths < 12 ? 'medium' : 'info', pointTotal, ciTotal, overdue, crossDate };
|
|
1004
1010
|
}
|
|
1005
1011
|
|
|
1006
1012
|
const get_stability = {
|
|
@@ -1095,6 +1101,8 @@ const get_stability = {
|
|
|
1095
1101
|
r2: _round(sl.r2, 2), basis: 'linear extrapolation to spec crossing',
|
|
1096
1102
|
verdict: as ? as.verdict : null,
|
|
1097
1103
|
concern: as ? as.level : null,
|
|
1104
|
+
projectedCrossDate: as ? as.crossDate : null,
|
|
1105
|
+
overdue: as ? as.overdue : false,
|
|
1098
1106
|
reportable: !!as,
|
|
1099
1107
|
};
|
|
1100
1108
|
return out;
|
|
@@ -1114,7 +1122,7 @@ const get_stability = {
|
|
|
1114
1122
|
entity: { type: entityType, uid: entity.uid, name: entity.name || entity.uid },
|
|
1115
1123
|
parameter: args.parameter, unit, spec, pointCount: points.length, byCondition: split, series,
|
|
1116
1124
|
targetShelfLife: target ? { months: target.months, source: target.source } : null,
|
|
1117
|
-
note: 'shelfLife.fromT0Months / ci95FromT0Months are the projected shelf life counted from the first test (T0): point estimate and the conservative (ICH Q1E) 95%-bound crossing. remainingMonths / ci95Months are the same, counted from the last test. verdict compares against targetShelfLife: "short" = the trend crosses spec before the target, "marginal" = the trend clears it but the 95% bound does not, "meets" = both clear it; null = no target set (then concern is by time left: high <3 mo, medium <12 mo). reportable=false means the app shows no projection (R² < 0.3, crossing already past, or beyond 5 years). Numbers are computed here — narrate them, do not recompute.',
|
|
1125
|
+
note: 'shelfLife.fromT0Months / ci95FromT0Months are the projected shelf life counted from the first test (T0): point estimate and the conservative (ICH Q1E) 95%-bound crossing. remainingMonths / ci95Months are the same, counted from the last test. verdict compares against targetShelfLife: "short" = the trend crosses spec before the target, "marginal" = the trend clears it but the 95% bound does not, "meets" = both clear it; null = no target set (then concern is by time left: high <3 mo, medium <12 mo). overdue=true means projectedCrossDate is already past today (time left runs from the last test), so the sample is probably out of spec now — recommend a re-test. reportable=false means the app shows no projection (R² < 0.3, crossing already past, or beyond 5 years). Numbers are computed here — narrate them, do not recompute.',
|
|
1118
1126
|
};
|
|
1119
1127
|
},
|
|
1120
1128
|
};
|