@barivia/barsom-mcp 0.9.0 → 0.10.0

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/dist/shared.js CHANGED
@@ -19,7 +19,7 @@ export const RETRYABLE_STATUS = new Set([502, 503, 504]);
19
19
  * X-Barsom-Client-Version so the server can annotate tool guidance with the
20
20
  * wrapper version each action requires. Keep in sync with package.json on bump.
21
21
  */
22
- export const CLIENT_VERSION = "0.9.0";
22
+ export const CLIENT_VERSION = "0.10.0";
23
23
  /** User-facing links; keep aligned with barivia.se / api.barivia.se. */
24
24
  export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
25
25
  /** Poll window for datasets(add_expression) / derive jobs (server-side work can exceed 30s). */
@@ -8,6 +8,7 @@ export const JOBS_DESCRIPTION_BASE = `Manage and inspect jobs.
8
8
  | list | Finding job IDs, checking what is pending/completed, reviewing hyperparameters. Response includes job_type (train_map, report, recolor, project, transition_flow, compare, predict, impute_column, annotated_dataset, reduce_spectral) to filter or display. |
9
9
  | compare | Picking the best training run from a set of completed jobs |
10
10
  | train_map | Submitting a new map training job — returns job_id for polling |
11
+ | train_impute | Sparse training data: train a map AND impute missing cells in one job (accelerated missSOM) — returns map + imputed.csv |
11
12
  | train_siom_map | Submitting a self-interacting map training job — same map flow with SIOM coverage control |
12
13
  | train_floop_siom | Submitting a FLooP-SIOM job (growing manifold; default topology=free / CHL) — requires Premium or Enterprise plan (all_algorithms) |
13
14
  | train_floop_chain | Deprecated alias for train_floop_siom — same behavior; prefer train_floop_siom |
@@ -31,6 +32,7 @@ ESCALATION (action=status):
31
32
  - NaN error: user must clean the dataset
32
33
 
33
34
  action=train_map / train_siom_map: Submits a grid-map training job. Returns job_id — poll with jobs(action=status, job_id=...).
35
+ action=train_impute: Submits missing-tolerant map training on a dataset with holes in the feature matrix. Use when many cells are missing across training columns; use inference(impute_column) instead when you already have a complete-case map and need to fill one held-out column. Plain numeric columns only (no cyclic/temporal/categorical). Returns map artifacts plus imputed.csv (dense matrix). Same grid/epochs/backend params as train_map.
34
36
  Presets: quick | standard | refined | high_res — use preset=... for grid/epochs/batch defaults; call training_guidance for details.
35
37
  Presets refined/high_res may use GPU. On CPU-only hosts pass backend=cpu. API expects strings "cpu" | "gpu" | "gpu_graphs" (no colon). Future backends (e.g. non-CUDA) may be added under the same contract.
36
38
  normalize: "auto" (default) = scale only non-cyclic features; "all" = scale every feature. Use "auto" when using cyclic_features.
@@ -224,7 +226,7 @@ export function buildTrainMapParams(args, presets) {
224
226
  export function registerJobsTool(server, description) {
225
227
  server.tool("jobs", description, {
226
228
  action: z
227
- .enum(["status", "list", "compare", "cancel", "delete", "train_map", "train_siom_map", "train_floop_siom", "train_floop_chain", "batch_predict", "run_baseline_study"])
229
+ .enum(["status", "list", "compare", "cancel", "delete", "train_map", "train_impute", "train_siom_map", "train_floop_siom", "train_floop_chain", "batch_predict", "run_baseline_study"])
228
230
  .describe("status: check progress; list: see all jobs; compare: metrics table; cancel: stop job; delete: remove job + files; train_map: submit standard map training; train_siom_map: submit SIOM map training; train_floop_siom: submit FLooP-SIOM (preferred); train_floop_chain: deprecated alias for train_floop_siom; batch_predict: submit multiple predict jobs at once; run_baseline_study: auto-configure and train a baseline SOM"),
229
231
  job_id: z
230
232
  .string()
@@ -306,6 +308,8 @@ export function registerJobsTool(server, description) {
306
308
  output_dpi: z.enum(["standard", "retina", "print"]).optional().default("retina"),
307
309
  colormap: z.string().optional(),
308
310
  row_range: z.tuple([z.number().int().min(1), z.number().int().min(1)]).optional(),
311
+ cv_folds: z.number().int().min(0).max(20).optional()
312
+ .describe("train_impute only: held-out MAE/RMSE/R2 per column (0=off, 2..20) → quality.csv"),
309
313
  gamma: z.preprocess((v) => (v !== undefined && v !== null && typeof v === "string") ? parseFloat(v) : v, z.number().optional()),
310
314
  gamma_f: z.preprocess((v) => (v !== undefined && v !== null && typeof v === "string") ? parseFloat(v) : v, z.number().optional()),
311
315
  siom_decay: z.preprocess((v) => (v !== undefined && v !== null && typeof v === "string") ? parseFloat(v) : v, z.number().optional()),
@@ -361,8 +365,8 @@ export function registerJobsTool(server, description) {
361
365
  const jid = String(data.id ?? "");
362
366
  return { content: [{ type: "text", text: `Baseline study submitted. Job ID: ${jid}\nGrid size: ${side}x${side}\nNormalization: MAD\n\nPoll with jobs(action=status, job_id="${jid}") until complete, then retrieve with results(action=get, job_id="${jid}"). Optional: training_monitor(job_id="${jid}") for a visual panel—not required.` }] };
363
367
  }
364
- if (action === "train_map" || action === "train_siom_map") {
365
- const { preset, grid_x, grid_y, epochs, model, periodic, columns, cyclic_features, temporal_features, feature_weights, transforms, auto_log_transforms, time_delay_embeddings, categorical_features, normalize, sigma_f, learning_rate, batch_size, quality_metrics, backend, output_format, output_dpi, colormap, row_range, gamma, gamma_f, siom_decay, siom_penalty, penalty_alpha, reset_per_epoch, siom_feature_geometry, siom_qe_backend, siom_qe_batch_size, label, } = args;
368
+ if (action === "train_map" || action === "train_siom_map" || action === "train_impute") {
369
+ const { preset, grid_x, grid_y, epochs, model, periodic, columns, cyclic_features, temporal_features, feature_weights, transforms, auto_log_transforms, time_delay_embeddings, categorical_features, normalize, sigma_f, learning_rate, batch_size, quality_metrics, backend, output_format, output_dpi, colormap, row_range, gamma, gamma_f, siom_decay, siom_penalty, penalty_alpha, reset_per_epoch, siom_feature_geometry, siom_qe_backend, siom_qe_batch_size, label, cv_folds, } = args;
366
370
  let PRESETS = {};
367
371
  try {
368
372
  PRESETS = await fetchTrainingPresets();
@@ -375,7 +379,7 @@ export function registerJobsTool(server, description) {
375
379
  }
376
380
  }
377
381
  if (!dataset_id)
378
- throw new Error("jobs(train_map) requires dataset_id");
382
+ throw new Error(`jobs(${action}) requires dataset_id`);
379
383
  const { params, paramSummary, effectiveGrid } = buildTrainMapParams({
380
384
  preset, grid_x, grid_y, epochs, model, periodic, columns, cyclic_features,
381
385
  temporal_features, feature_weights, transforms, auto_log_transforms,
@@ -383,6 +387,18 @@ export function registerJobsTool(server, description) {
383
387
  normalize, sigma_f, learning_rate, batch_size, quality_metrics, backend,
384
388
  output_format, output_dpi, colormap, row_range,
385
389
  }, PRESETS);
390
+ if (action === "train_impute") {
391
+ params._job_type = "train_impute";
392
+ if (cv_folds !== undefined)
393
+ params.cv_folds = cv_folds;
394
+ // Plain numeric path only — strip unsupported keys if caller passed them
395
+ delete params.cyclic_features;
396
+ delete params.temporal_features;
397
+ delete params.categorical_features;
398
+ delete params.transforms;
399
+ delete params.auto_log_transforms;
400
+ delete params.time_delay_embeddings;
401
+ }
386
402
  if (action === "train_siom_map") {
387
403
  params._job_type = "train_siom";
388
404
  if (gamma !== undefined)
@@ -415,7 +431,9 @@ export function registerJobsTool(server, description) {
415
431
  submitBody.label = label;
416
432
  const data = (await apiCall("POST", "/v1/jobs", submitBody));
417
433
  const newJobId = data.id;
418
- const variantPrefix = action === "train_siom_map" ? "variant=siom" : "variant=som";
434
+ const variantPrefix = action === "train_siom_map" ? "variant=siom"
435
+ : action === "train_impute" ? "variant=train_impute"
436
+ : "variant=som";
419
437
  data.effective_params = `${variantPrefix}, ${paramSummary}`;
420
438
  try {
421
439
  const sys = (await apiCall("GET", "/v1/system/info"));
@@ -426,7 +444,7 @@ export function registerJobsTool(server, description) {
426
444
  let msg = `Job submitted (${variantPrefix}, ${paramSummary}). `;
427
445
  if (waitMinutes > 1)
428
446
  msg += `You are #${pending + 1} in queue. Estimated wait: ~${waitMinutes} min. `;
429
- msg += `Poll with jobs(action=status, job_id="${newJobId}"). When status is completed, use results(action=get, job_id="${newJobId}") to view the map and metrics.`;
447
+ msg += `Poll with jobs(action=status, job_id="${newJobId}"). When status is completed, use results(action=get, job_id="${newJobId}") to view the map${action === "train_impute" ? " and imputed.csv" : ""} and metrics.`;
430
448
  msg += ` Optional: training_monitor(job_id="${newJobId}") for charts—not required.`;
431
449
  if (effectiveGrid && totalRows > 0 && effectiveGrid[0] * effectiveGrid[1] > totalRows * 0.75) {
432
450
  msg += ` Note: Grid may be large for ${totalRows} rows (consider grid=auto for fewer dead nodes).`;
@@ -434,7 +452,7 @@ export function registerJobsTool(server, description) {
434
452
  data.message = msg;
435
453
  }
436
454
  catch {
437
- let msg = `Job submitted (${variantPrefix}, ${paramSummary}). Poll with jobs(action=status, job_id="${newJobId}"). When status is completed, use results(action=get, job_id="${newJobId}") to view the map and metrics.`;
455
+ let msg = `Job submitted (${variantPrefix}, ${paramSummary}). Poll with jobs(action=status, job_id="${newJobId}"). When status is completed, use results(action=get, job_id="${newJobId}") to view the map${action === "train_impute" ? " and imputed.csv" : ""} and metrics.`;
438
456
  msg += ` Optional: training_monitor(job_id="${newJobId}") for charts—not required.`;
439
457
  if (effectiveGrid && totalRows > 0 && effectiveGrid[0] * effectiveGrid[1] > totalRows * 0.75) {
440
458
  msg += ` Note: Grid may be large for ${totalRows} rows (consider grid=auto for fewer dead nodes).`;
@@ -393,7 +393,7 @@ NOT FOR: Jobs that haven't completed. Use jobs(action=status) to check first.`,
393
393
  return "";
394
394
  return `Columns not used in training: ${excluded.join(", ")}. You can project them onto this map with inference(action=project_columns, job_id=${job_id}, dataset_id=<dataset_id>, columns=[${excluded.map((c) => `"${c}"`).join(", ")}]) to see how they distribute across the topology. If you ran datasets(action=analyze) before training, any columns it recommended as "project later" are especially good candidates.`;
395
395
  })(),
396
- ...((jobType === "train_som" || jobType === "train_siom") ? ["", `Next: results(action=export, export_type=training_log) for learning curve; results(action=download) to save figures; jobs(action=compare, job_ids=[...]) to compare runs; inference(action=predict) to score new data; inference(action=project_columns) to project other variables onto the map.`] : []),
396
+ ...((jobType === "train_som" || jobType === "train_siom" || jobType === "train_impute") ? ["", `Next: results(action=export, export_type=training_log) for learning curve; results(action=download) to save figures${jobType === "train_impute" ? " (includes imputed.csv)" : ""}; jobs(action=compare, job_ids=[...]) to compare runs; inference(action=predict) to score new data; inference(action=project_columns) to project other variables onto the map.`] : []),
397
397
  ].filter((l) => l !== "").join("\n");
398
398
  content.push({ type: "text", text: textSummary });
399
399
  const imagesToFetch = getResultsImagesToFetch(jobType, summary, figures, include_individual);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barivia/barsom-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "barSOM MCP proxy — connect any MCP client to the barSOM cloud API for Self-Organizing Map analytics",
5
5
  "keywords": [
6
6
  "mcp",