formlab-mcp 0.6.5 → 0.6.6

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/index.js CHANGED
@@ -17,6 +17,9 @@
17
17
  // call sees fresh data. No restart needed.
18
18
  // ============================================================
19
19
 
20
+ import { readFileSync } from 'node:fs';
21
+ import { fileURLToPath } from 'node:url';
22
+
20
23
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
21
24
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
22
25
  import {
@@ -115,8 +118,22 @@ const TOOLS = [
115
118
  ...Object.values(notebook.tools),
116
119
  ];
117
120
 
121
+ // Version reported in the MCP handshake. Read from package.json rather than
122
+ // hard-coded: the literal here said 0.6.0 while the package was 0.6.5 (caught by
123
+ // the 2026-08-01 claims audit), because publishing bumps package.json and nobody
124
+ // remembers this line. npm always ships package.json, so the read is safe from
125
+ // an installed copy as well as from the repo.
126
+ const PKG_VERSION = (() => {
127
+ try {
128
+ const pkgPath = fileURLToPath(new URL('./package.json', import.meta.url));
129
+ return JSON.parse(readFileSync(pkgPath, 'utf8')).version || '0.0.0';
130
+ } catch (e) {
131
+ return '0.0.0'; // never let a missing/unreadable manifest stop the server
132
+ }
133
+ })();
134
+
118
135
  const server = new Server(
119
- { name: 'formlab-mcp', version: '0.6.0' },
136
+ { name: 'formlab-mcp', version: PKG_VERSION },
120
137
  { capabilities: { tools: {} } }
121
138
  );
122
139
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formlab-mcp",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
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.5",
5
+ "version": "0.6.6",
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.5",
16
+ "version": "0.6.6",
17
17
  "runtimeHint": "npx",
18
18
  "transport": {
19
19
  "type": "stdio"
@@ -68,6 +68,12 @@ function _trimMeasurement(m, t, db) {
68
68
  pointCount: Array.isArray(m.points) ? m.points.length : 0,
69
69
  binCount: Array.isArray(m.bins) ? m.bins.length : 0,
70
70
  acceptanceCriteria: m.acceptanceCriteria || null,
71
+ // Captured run conditions this reading was measured under (e.g.
72
+ // {Storage: "40 °C / 75% RH"}, {RPM: "50"}) — the condition travels with the
73
+ // value, so a reading is self-describing and comparable across runs. Present
74
+ // only when captured. get_test_method reports which conditions a method
75
+ // declares (runConditions).
76
+ ...((m.conditions && Object.keys(m.conditions).length) ? { conditions: m.conditions } : {}),
71
77
  ...ins, // instrument, equipmentId, instrumentSource
72
78
  };
73
79
  }
@@ -134,7 +140,7 @@ const list_test_results = {
134
140
  const get_test_result = {
135
141
  definition: {
136
142
  name: 'get_test_result',
137
- description: 'Get full details for a single test result report: every measurement\'s value, unit, type, acceptance criteria, and the INSTRUMENT that produced it. instrument is RESOLVED (measurement override → the parameter\'s Test Method equipment → the report default) with instrumentSource telling you which — treat source "run" as an inherited assumption, not evidence. Accepts internal id or UID.',
143
+ description: 'Get full details for a single test result report: every measurement\'s value, unit, type, acceptance criteria, captured run CONDITIONS (e.g. {Storage:"40 °C / 75% RH"} — the storage/temp/RPM the reading was measured under, so it is self-describing and comparable across runs; present only when captured), and the INSTRUMENT that produced it. instrument is RESOLVED (measurement override → the parameter\'s Test Method equipment → the report default) with instrumentSource telling you which — treat source "run" as an inherited assumption, not evidence. Accepts internal id or UID.',
138
144
  inputSchema: {
139
145
  type: 'object',
140
146
  properties: {
package/tools/library.js CHANGED
@@ -133,6 +133,17 @@ function _trimMethod(p, { full = false } = {}) {
133
133
  equipment: p.equipment || '', equipmentId: p.equipmentId || '',
134
134
  calcFormula: p.calcFormula || '', version: p.version != null ? p.version : null,
135
135
  approvedBy: p.approvedBy || '', notes: p.notes || '',
136
+ // Declared run conditions this method varies by (the vocabulary — heating
137
+ // rate, spindle RPM, storage temp/RH…). Each measurement using this method
138
+ // captures a value per condition (see get_test_result → measurement.conditions).
139
+ runConditions: (Array.isArray(p.conditions) ? p.conditions : [])
140
+ .filter(c => c && (c.key || c.label))
141
+ .map(c => ({
142
+ key: c.key || '', label: c.label || c.key || '', type: c.type || 'text',
143
+ ...(c.unit ? { unit: c.unit } : {}),
144
+ ...(Array.isArray(c.options) && c.options.length ? { options: c.options } : {}),
145
+ ...(c.default != null && c.default !== '' ? { default: c.default } : {}),
146
+ })),
136
147
  };
137
148
  }
138
149
 
@@ -176,7 +187,7 @@ const list_test_methods = {
176
187
  const get_test_method = {
177
188
  definition: {
178
189
  name: 'get_test_method',
179
- description: 'Get one Test Method by UID (e.g. PARAM-001) or id — full definition including SOP / sample prep / equipment / calculation / acceptance spec, plus sibling methods that share its analyte (the same property measured at other conditions) and the Test Panels that use this method.',
190
+ description: 'Get one Test Method by UID (e.g. PARAM-001) or id — full definition including SOP / sample prep / equipment / calculation / acceptance spec, the declared runConditions this method varies by (the vocabulary — e.g. Storage temp/RH, spindle RPM; each measurement captures a value per condition), plus sibling methods that share its analyte (the same property measured at other conditions) and the Test Panels that use this method.',
180
191
  inputSchema: {
181
192
  type: 'object',
182
193
  properties: { id: { type: 'string', description: 'Test Method UID or internal id.' } },