formlab-mcp 0.6.16 → 0.6.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formlab-mcp",
3
- "version": "0.6.16",
3
+ "version": "0.6.17",
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/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.16",
5
+ "version": "0.6.17",
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",
16
+ "version": "0.6.17",
17
17
  "runtimeHint": "npx",
18
18
  "transport": {
19
19
  "type": "stdio"
@@ -10,7 +10,7 @@
10
10
  // ============================================================
11
11
 
12
12
  import { getStore, resolveById, flattenComposition } from '../data.js';
13
- import { _batchActualMassKg, _massToKg } from './lab.js';
13
+ import { _batchActualMassKg, _massToKg, _batchUnitMassKg } from './lab.js';
14
14
  import { _costPerKg } from './ingredients.js';
15
15
 
16
16
  // ---- shared compute (mirrors _bcActualPctMap / _bcProposedPctMap / _bcSpreadLevel) ----
@@ -43,7 +43,7 @@ function _amountMap(b) {
43
43
  return out;
44
44
  }
45
45
 
46
- function _targetKg(b) { const q = parseFloat(b.targetQuantity); return isFinite(q) ? _massToKg(q, b.unit) : null; }
46
+ function _targetKg(b) { const q = parseFloat(b.targetQuantity); if (!isFinite(q)) return null; const mk = _massToKg(q, b.unit); if (mk != null) return mk; const umk = _batchUnitMassKg(b); return umk != null ? q * umk : null; }
47
47
  function _yieldPct(b) { const tk = _targetKg(b), mk = _batchActualMassKg(b); return (tk && tk > 0 && mk != null) ? +(mk / tk * 100).toFixed(1) : null; }
48
48
 
49
49
  // Blend cost per kg = Σ ingredient costPerKg × weightFraction over the flattened
package/tools/lab.js CHANGED
@@ -18,9 +18,19 @@ const _MASS_TO_KG = { mg: 1e-6, cg: 1e-5, dg: 1e-4, g: 1e-3, hg: 0.1, kg: 1, oz:
18
18
  const _VOL_TO_L = { 'µl': 1e-6, ul: 1e-6, ml: 1e-3, cl: 1e-2, dl: 0.1, l: 1, gal: 3.78541, fl_oz: 0.0295735, floz: 0.0295735, pt: 0.473176, qt: 0.946353, tsp: 0.00492892, tbsp: 0.0147868, cup: 0.236588 };
19
19
  function _massToKg(v, u) { const f = _MASS_TO_KG[String(u || '').trim().toLowerCase()]; return (f != null && isFinite(v)) ? v * f : null; }
20
20
  function _volToL(v, u) { const f = _VOL_TO_L[String(u || '').trim().toLowerCase()]; return (f != null && isFinite(v)) ? v * f : null; }
21
+ // Mass (kg) of one count-unit (tablet/capsule/…) from b.unitMass {value, unit};
22
+ // null when unset. Lets dose-form batches derive a total mass (count × unit mass).
23
+ function _batchUnitMassKg(b) {
24
+ if (!b || !b.unitMass) return null;
25
+ const v = parseFloat(b.unitMass.value);
26
+ return (isFinite(v) && v > 0) ? _massToKg(v, b.unitMass.unit) : null;
27
+ }
21
28
  function _batchActualMassKg(b) {
22
29
  const y = parseFloat(b.actualYield);
23
- if (isFinite(y)) { const kg = _massToKg(y, b.unit); if (kg != null) return kg; }
30
+ if (isFinite(y)) {
31
+ const kg = _massToKg(y, b.unit); if (kg != null) return kg;
32
+ const umk = _batchUnitMassKg(b); if (umk != null) return y * umk; // count unit × per-unit mass
33
+ }
24
34
  if (Array.isArray(b.actualComposition)) {
25
35
  let t = 0, any = false;
26
36
  for (const c of b.actualComposition) { const kg = _massToKg(parseFloat(c.amount), c.unit); if (kg != null) { t += kg; any = true; } }
@@ -68,6 +78,9 @@ function _trimBatch(b, formulationsById) {
68
78
  status: b.status || '',
69
79
  targetQuantity: b.targetQuantity ?? null,
70
80
  unit: b.unit || '',
81
+ // Per-count-unit mass for dose-form batches (tablet/capsule) — lets a count
82
+ // batch derive a total mass (count × unit mass) for cost / yield.
83
+ unitMass: (b.unitMass && b.unitMass.value != null) ? { value: b.unitMass.value, unit: b.unitMass.unit || 'mg' } : null,
71
84
  actualYield: b.actualYield ?? null,
72
85
  // Finished-product density (g/mL === kg/L), measured on the blend, + the
73
86
  // derived/measured actual VOLUME (mass is the additive quantity; volume is
@@ -303,4 +316,4 @@ const get_sample = {
303
316
  export const tools = { list_batches, get_batch, list_samples, get_sample };
304
317
 
305
318
  // Exported for the batch-analyze tools so they reuse the SAME mass logic.
306
- export { _batchActualMassKg, _massToKg };
319
+ export { _batchActualMassKg, _massToKg, _batchUnitMassKg };