@barivia/barsom-mcp 0.7.3 → 0.7.12

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.
Files changed (64) hide show
  1. package/README.md +58 -26
  2. package/dist/index.js +1 -1
  3. package/dist/shared.js +107 -33
  4. package/dist/tools/account.js +9 -1
  5. package/dist/tools/datasets.js +3 -4
  6. package/dist/tools/explore_map.js +1 -2
  7. package/dist/tools/feedback.js +0 -1
  8. package/dist/tools/guide_barsom.js +14 -42
  9. package/dist/tools/inference.js +0 -1
  10. package/dist/tools/jobs.js +35 -11
  11. package/dist/tools/results.js +0 -1
  12. package/dist/tools/training_guidance.js +1 -2
  13. package/dist/tools/training_monitor.js +43 -0
  14. package/dist/tools/training_prep.js +3 -1
  15. package/dist/tools/training_review_store.js +0 -1
  16. package/dist/views/src/views/results-explorer/index.html +1 -1
  17. package/dist/views/src/views/training-monitor/index.html +12 -12
  18. package/dist/viz-server.js +0 -1
  19. package/package.json +10 -4
  20. package/dist/index.d.ts +0 -25
  21. package/dist/index.d.ts.map +0 -1
  22. package/dist/index.js.map +0 -1
  23. package/dist/shared.d.ts +0 -176
  24. package/dist/shared.d.ts.map +0 -1
  25. package/dist/shared.js.map +0 -1
  26. package/dist/tools/account.d.ts +0 -3
  27. package/dist/tools/account.d.ts.map +0 -1
  28. package/dist/tools/account.js.map +0 -1
  29. package/dist/tools/datasets.d.ts +0 -11
  30. package/dist/tools/datasets.d.ts.map +0 -1
  31. package/dist/tools/datasets.js.map +0 -1
  32. package/dist/tools/explore_map.d.ts +0 -5
  33. package/dist/tools/explore_map.d.ts.map +0 -1
  34. package/dist/tools/explore_map.js.map +0 -1
  35. package/dist/tools/feedback.d.ts +0 -3
  36. package/dist/tools/feedback.d.ts.map +0 -1
  37. package/dist/tools/feedback.js.map +0 -1
  38. package/dist/tools/guide_barsom.d.ts +0 -3
  39. package/dist/tools/guide_barsom.d.ts.map +0 -1
  40. package/dist/tools/guide_barsom.js.map +0 -1
  41. package/dist/tools/inference.d.ts +0 -3
  42. package/dist/tools/inference.d.ts.map +0 -1
  43. package/dist/tools/inference.js.map +0 -1
  44. package/dist/tools/jobs.d.ts +0 -61
  45. package/dist/tools/jobs.d.ts.map +0 -1
  46. package/dist/tools/jobs.js.map +0 -1
  47. package/dist/tools/results.d.ts +0 -11
  48. package/dist/tools/results.d.ts.map +0 -1
  49. package/dist/tools/results.js.map +0 -1
  50. package/dist/tools/training_guidance.d.ts +0 -3
  51. package/dist/tools/training_guidance.d.ts.map +0 -1
  52. package/dist/tools/training_guidance.js.map +0 -1
  53. package/dist/tools/training_prep.d.ts +0 -4
  54. package/dist/tools/training_prep.d.ts.map +0 -1
  55. package/dist/tools/training_prep.js.map +0 -1
  56. package/dist/tools/training_review_store.d.ts +0 -17
  57. package/dist/tools/training_review_store.d.ts.map +0 -1
  58. package/dist/tools/training_review_store.js.map +0 -1
  59. package/dist/views/src/views/data-preview/index.html +0 -148
  60. package/dist/views/src/views/map-explorer/index.html +0 -288
  61. package/dist/views/src/views/som-explorer/index.html +0 -288
  62. package/dist/viz-server.d.ts +0 -16
  63. package/dist/viz-server.d.ts.map +0 -1
  64. package/dist/viz-server.js.map +0 -1
package/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # @barivia/barsom-mcp
2
2
 
3
- MCP proxy for the Barivia Analytics Engine -- connects any stdio MCP client (Cursor, Claude Desktop, etc.) to the barSOM cloud API. It implements a **Progressive Disclosure** architecture, exposing a `guide_barsom_workflow` top-level SOP tool and comprehensive `jobs` (`train_map`, `train_siom_map`, `train_floop_chain`, status/compare), `results`, `project`, and `inference` capabilities, following 2026 enterprise MCP best practices.
3
+ MCP proxy for the Barivia Analytics Engine connects any stdio MCP client (Cursor, Claude Desktop, etc.) to the barSOM cloud API. **`guide_barsom_workflow`** is the canonical bootstrap: it fetches **`GET /v1/docs/workflow`** (authenticated) and returns tier-scoped text — proxy model, tool map, async rules, training modes allowed for your key, and step-by-step SOP. If the API is unreachable, it returns a short offline stub. Core tools are `datasets`, `jobs` (`train_map`, `train_siom_map`, `train_floop_siom` where entitled; `train_floop_chain` is deprecated), `results`, and `inference`. **Optional MCP App tools** (`training_prep`, `training_monitor`, `results_explorer` / `explore_map`) add embedded or localhost viz; they are **not required** to complete upload → train → poll → results.
4
4
 
5
5
  ## Installation
6
6
 
7
- No install step needed. MCP clients run it via `npx`:
7
+ Published on the **public npm registry** as **`@barivia/barsom-mcp`**. No GitHub token or scoped `.npmrc` is required for installs.
8
+
9
+ MCP clients typically run it with **`npx`** (downloads on first use):
8
10
 
9
11
  ```json
10
12
  {
@@ -21,7 +23,11 @@ No install step needed. MCP clients run it via `npx`:
21
23
  }
22
24
  ```
23
25
 
24
- This is the standard pattern for MCP servers distributed as npm packages (same as `firecrawl-mcp`, `@paypal/mcp`, etc.).
26
+ **Verify:** `npm view @barivia/barsom-mcp version`
27
+
28
+ **Also available:** hosted HTTP MCP at **`https://mcp.barivia.se/mcp`** (same API key; no npm) for clients that support remote MCP.
29
+
30
+ **Future:** A **private npm org** scoped package is an option later (paid npm teams/orgs; users authenticate with npm, usually simpler than GitHub Packages for pure npm consumers).
25
31
 
26
32
  ## Environment Variables
27
33
 
@@ -30,25 +36,32 @@ This is the standard pattern for MCP servers distributed as npm packages (same a
30
36
  | `BARIVIA_API_KEY` | Yes | -- | API key (starts with `bv_`) |
31
37
  | `BARIVIA_API_URL` | No | `https://api.barivia.se` | API base URL |
32
38
  | `BARIVIA_WORKSPACE_ROOT` | No | `process.cwd()` or `PWD` | Directory for relative `file_path` and `save_to_disk`. In Cursor MCP, `process.cwd()` is often the MCP install dir — add `BARIVIA_WORKSPACE_ROOT` to your MCP config `env` with your project path (e.g. `/home/user/myproject`). Absolute paths and `file://` URIs work without it. |
39
+ | `BARIVIA_FETCH_TIMEOUT_MS` | No | `30000` | Per-request HTTP timeout (ms) to the API. Increase (e.g. `120000`) on slow networks or when the API does long-running work. Large dataset uploads use a separate longer timeout internally. |
40
+ | `BARIVIA_VIZ_PORT` | No | OS-assigned | When the client has no MCP Apps support, the proxy may start a local viz server on `127.0.0.1`; set a fixed port if you need stable bookmark URLs. |
33
41
 
34
42
  Legacy `BARSOM_API_KEY` / `BARSOM_API_URL` / `BARSOM_WORKSPACE_ROOT` are also accepted as fallbacks.
35
43
 
36
- ## Tools (13 + MCP App)
44
+ **Supported stack:** Node **18+** (see `engines` in `package.json`). Built with `@modelcontextprotocol/sdk` **^1.x** — if a major MCP client upgrade breaks tools, check SDK release notes alongside this package version.
45
+
46
+ **Local viz fallback:** If the IDE does not advertise MCP Apps, the proxy starts an HTTP server bound to **127.0.0.1** only (not exposed to the LAN). It serves built-in HTML for training prep / monitor / results explorer and proxies read-only API calls with your existing API key. Responses may include `http://127.0.0.1:<port>/viz/...` links. Browser `Access-Control-Allow-Origin: *` applies only to that localhost origin so the embedded pages can load data.
47
+
48
+ ## Tools (14 registrations)
37
49
 
38
50
  All multi-action tools follow the `datasets` pattern: a required `action` enum routes to the correct operation.
39
51
 
40
52
  ### `guide_barsom_workflow`
41
- SOP dispatch. Call this first if unsure of the workflow. No parameters.
53
+ Call at the **start of mapping work** (or when the user asks what the MCP can do). Loads full orientation from the API (`GET /v1/docs/workflow`); content reflects **your plan** (`allowed_job_types`). No parameters. **`training_guidance`** uses `GET /v1/training/config` with the same entitlement filtering.
42
54
 
43
55
  ### `datasets(action)`
44
56
 
45
57
  | Action | Use when |
46
58
  |--------|----------|
47
59
  | `upload` | Adding a new CSV — returns dataset_id |
48
- | `preview` | Before jobs(action=train_map), jobs(action=train_siom_map), or jobs(action=train_floop_chain) — inspect columns, stats, cyclic/datetime hints |
60
+ | `preview` | Before training — inspect columns, stats, cyclic/datetime hints |
61
+ | `analyze` | Pre-training recommendations (correlation, columns to consider dropping, etc.) |
49
62
  | `list` | Finding dataset IDs |
50
63
  | `subset` | Creating a filtered/sliced copy (row_range, filter conditions) |
51
- | `add_expression` | Add a derived column from an expression (same as project(expression) without project_onto_job) |
64
+ | `add_expression` | Add a derived column from an expression (formula new column on the dataset) |
52
65
  | `delete` | Removing a dataset |
53
66
 
54
67
  ### `jobs(action)`
@@ -57,12 +70,15 @@ SOP dispatch. Call this first if unsure of the workflow. No parameters.
57
70
  |--------|----------|
58
71
  | `train_map` | Submitting a new map training job — full control: model type, grid, epochs, cyclic/temporal features, transforms. Returns `job_id`; poll with `jobs(action=status, job_id=...)`. |
59
72
  | `train_siom_map` | Submitting a self-interacting map training job — same grid-map workflow plus SIOM controls such as `gamma`, `siom_decay`, and penalty selection. |
60
- | `train_floop_chain` | Submitting a FLooP-SIOM training job — use when you want a growing chain or free-topology manifold instead of a fixed 2D grid. |
73
+ | `train_floop_siom` | Submitting a FLooP-SIOM job — growing node-budget manifold; default `topology=free` (CHL); optional `topology=chain` for strict 1D linked list. |
74
+ | `train_floop_chain` | Deprecated alias for `train_floop_siom` — same behavior. |
61
75
  | `status` | Polling after any async job — every 10–15s |
62
76
  | `list` | Finding job IDs, checking pipeline state |
63
77
  | `compare` | Picking the best run from a set (QE, TE, silhouette table) |
64
78
  | `cancel` | Stopping a running job |
65
79
  | `delete` | Permanently removing a job + its S3 files |
80
+ | `batch_predict` | Submit multiple predict jobs from one parent training job |
81
+ | `run_baseline_study` | Auto-configured baseline SOM for a dataset |
66
82
 
67
83
  ### `results(action)`
68
84
 
@@ -74,23 +90,17 @@ SOP dispatch. Call this first if unsure of the workflow. No parameters.
74
90
  | `recolor` | async | Changing colormap or output format without retraining |
75
91
  | `transition_flow` | async | Temporal state transition analysis on time-ordered data |
76
92
 
77
- All visualizations and metrics come from **`results(action=get)`**. Grid-map jobs return combined map figures, component planes, and quality metrics; FLooP-SIOM jobs return chain-structure figures, occupation/profile views, and chain metrics. Use `figures=all` or `export_type=...` for more. There is no separate `analyze` tool.
78
-
79
- ### `project(action)`
80
-
81
- | Action | Use when |
82
- |--------|----------|
83
- | `expression` | Computing a derived variable from a formula (`revenue / cost`, `diff(temp)`, rolling stats) — add to dataset or project onto the map |
84
- | `values` | Projecting a pre-computed external array (anomaly scores, labels from another system) onto the map |
93
+ All visualizations and metrics come from **`results(action=get)`**. Grid-map jobs return combined map figures, component planes, and quality metrics; FLooP-SIOM jobs return structure/coverage figures, occupation/profile views, and metrics. Use `figures=all` or `export_type=...` for more. There is no separate `analyze` tool.
85
94
 
86
95
  ### `inference(action)`
87
- All actions use a frozen trained map — no retraining. All are async.
96
+ All actions use a frozen trained map — no retraining. Derived columns use **`datasets(action=add_expression)`**; overlaying columns on the map uses **`inference(action=project_columns)`**.
88
97
 
89
98
  | Action | Output | Timing |
90
99
  |--------|--------|--------|
91
100
  | `predict` | predictions.csv: per-row bmu_x/y, cluster_id, quantization_error, potential_anomaly (QE > 95th pct); summary includes qe_p95 | 5–120s |
92
101
  | `enrich` | enriched.csv: training data + bmu_x/y/node_index/cluster_id | 5–60s |
93
102
  | `compare` | density-diff heatmap + top gained/lost nodes — drift, A/B, cohort | 30–120s |
103
+ | `project_columns` | Project one or more dataset columns onto the trained map (component planes) | async |
94
104
  | `report` | comprehensive PDF: metrics, views, component grid, cluster table | 30–180s |
95
105
 
96
106
  ### `account(action)`
@@ -104,8 +114,17 @@ All actions use a frozen trained map — no retraining. All are async.
104
114
  | `history` | Viewing recent compute usage and spend |
105
115
  | `add_funds` | Getting instructions to add credits |
106
116
 
107
- ### `explore_map` (MCP App)
108
- Interactive inline map explorer — clickable nodes, feature toggles, export controls.
117
+ ### MCP App tools (optional UIs)
118
+
119
+ | Tool | Role |
120
+ |------|------|
121
+ | `training_prep` | Review variables, transforms, hyperparameters before submit; use with `submit_prepared_training` |
122
+ | `training_monitor` | Visual progress for a `job_id`; optional — `jobs(action=status)` is enough |
123
+ | `results_explorer` | Browse metrics and figures after training completes |
124
+ | `explore_map` | Legacy alias of `results_explorer` |
125
+ | `_fetch_figure` | Internal helper for Results Explorer (single figure as image) |
126
+
127
+ When the client does not advertise MCP Apps support, the proxy starts **`viz-server`** on **`127.0.0.1`** (localhost-only). Tool responses can include `http://127.0.0.1:PORT/viz/...` links. See **Local viz fallback** under Environment Variables.
109
128
 
110
129
  ### `send_feedback`
111
130
  Submit feedback or feature requests (max 1400 characters, ~190 words).
@@ -121,7 +140,7 @@ When adding or refining tools, follow [MCP best practices](https://modelcontextp
121
140
 
122
141
  ## Data preparation
123
142
 
124
- To train on a subset of your data (e.g. first 2000 rows, or rows where region=Europe) without re-uploading: use **datasets(action=subset)** with `row_range` and/or `filter` to create a new dataset, then train with **jobs(action=train_map, dataset_id=...)**, **jobs(action=train_siom_map, dataset_id=...)**, or **jobs(action=train_floop_chain, dataset_id=...)** on the new dataset_id; or pass **row_range** in the training job params for a one-off slice.
143
+ To train on a subset of your data (e.g. first 2000 rows, or rows where region=Europe) without re-uploading: use **datasets(action=subset)** with `row_range` and/or `filter` to create a new dataset, then train with **jobs(action=train_map, dataset_id=...)**, **jobs(action=train_siom_map, dataset_id=...)**, or **jobs(action=train_floop_siom, dataset_id=...)** on the new dataset_id; or pass **row_range** in the training job params for a one-off slice.
125
144
 
126
145
  ## How It Works
127
146
 
@@ -131,6 +150,17 @@ The proxy implements the MCP stdio transport locally and translates tool calls i
131
150
  MCP Client (Cursor/Claude) ←stdio→ @barivia/barsom-mcp ←HTTPS→ api.barivia.se
132
151
  ```
133
152
 
153
+ ## Troubleshooting
154
+
155
+ | Symptom | What to check |
156
+ |--------|----------------|
157
+ | `401` / invalid key | `BARIVIA_API_KEY` in MCP config; regenerate or verify at [barivia.se](https://barivia.se). Error text includes a **request id** for support. |
158
+ | Request timed out | Raise `BARIVIA_FETCH_TIMEOUT_MS` (e.g. `120000`). Large uploads already use an extended timeout. |
159
+ | `Path must be within the workspace` / upload can’t find file | Set `BARIVIA_WORKSPACE_ROOT` to your project directory, or use an absolute path / `file:///...` URI. |
160
+ | Job stuck “running” | Poll `jobs(action=status)` every 10–15s; large grids or FLooP-SIOM can take several minutes—not an MCP error. |
161
+ | `429` | Rate limit—wait and retry. |
162
+ | Malformed MCP / client errors | Ensure nothing writes to **stdout** except MCP JSON-RPC (the proxy logs API traffic to **stderr** only). |
163
+
134
164
  ## Development
135
165
 
136
166
  ```bash
@@ -148,22 +178,24 @@ BARIVIA_API_URL=http://localhost:8080 BARIVIA_API_KEY=bv_test_key npm run dev
148
178
 
149
179
  ## Publishing
150
180
 
151
- Published to npm via GitHub Actions on tag `mcp-proxy-v*`:
181
+ The npm tarball is **runtime-only**: minified `dist/**/*.js`, the three embedded view HTML files, plus `package.json` and `README.md`. It does **not** include TypeScript source, `.map`, `.d.ts`, tests, or `src/`. CI runs `build:publish` (clean `dist`, compile without source maps, views, minify entrypoint) via `prepublishOnly`.
182
+
183
+ Published to **registry.npmjs.org** via GitHub Actions when you push tag `mcp-proxy-v*` (version in `package.json` must match the tag, e.g. tag `mcp-proxy-v0.7.10` for version `0.7.10`):
152
184
 
153
185
  ```bash
154
- git tag mcp-proxy-v0.1.0
155
- git push origin mcp-proxy-v0.1.0
186
+ git tag mcp-proxy-v0.7.10
187
+ git push origin mcp-proxy-v0.7.10
156
188
  ```
157
189
 
158
- Requires `NPM_TOKEN` secret in GitHub repository settings.
190
+ The repository needs a **`NPM_TOKEN`** secret (npm automation/publish token with access to the `@barivia` scope).
159
191
 
160
192
  ### Checking local vs published
161
193
 
162
- From the platform root, compare the current build to the published npm package (same version in `package.json`):
194
+ From the platform root:
163
195
 
164
196
  ```bash
165
197
  cd barivia-platform
166
198
  bash scripts/check-mcp-proxy-publish.sh
167
199
  ```
168
200
 
169
- Exit 0 = local matches published. Exit 1 = local differs (bump version and publish to update `npx`). Use `VERBOSE=1` for a full file diff.
201
+ Compares local `build:publish` output to the tarball on **public npm**. Exit 0 = match; exit 1 = diff. `VERBOSE=1` for a full `dist/` diff.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as o}from"zod";import{getUiCapability as a,registerAppResource as i,RESOURCE_MIME_TYPE as r}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as n}from"./viz-server.js";import{API_KEY as s,apiCall as c,apiRawCall as l,loadViewHtml as d,setVizPort as p,setClientSupportsMcpApps as m}from"./shared.js";import{registerDatasetsTool as u}from"./tools/datasets.js";import{registerJobsTool as g,JOBS_DESCRIPTION_BASE as f}from"./tools/jobs.js";import{registerResultsTool as h}from"./tools/results.js";import{registerExploreMapTool as _,RESULTS_EXPLORER_URI as b}from"./tools/explore_map.js";import{registerAccountTool as y}from"./tools/account.js";import{registerInferenceTool as w}from"./tools/inference.js";import{registerGuideBarsomTool as j}from"./tools/guide_barsom.js";import{registerTrainingGuidanceTool as v}from"./tools/training_guidance.js";import{registerFeedbackTool as x}from"./tools/feedback.js";import{registerTrainingPrepTools as k,TRAINING_PREP_URI as I}from"./tools/training_prep.js";s||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));const M=new e({name:"analytics-engine",version:"0.6.3",instructions:"# Barivia Mapping Analytics Engine\n\nYou have access to a mapping (Self-Organizing Map) analytics platform that projects high-dimensional data onto a 2D grid, revealing clusters, gradients, and anomalies.\n\n## Typical workflow\n\n1. **Upload** → `datasets(action=upload)` — ingest a CSV\n2. **Preview** → `datasets(action=preview)` — inspect columns, detect cyclics/datetimes; add derived columns with `datasets(action=add_expression)` if needed\n3. **Train** → choose one:\n - `jobs(action=train_map, dataset_id=...)` for a standard fixed-grid SOM\n - `jobs(action=train_siom_map, dataset_id=...)` for a fixed-grid SIOM with coverage regularization\n - `jobs(action=train_floop_chain, dataset_id=...)` for a growing FLooP-SIOM chain/free-topology manifold\n Returns a job_id; poll `jobs(action=status, job_id=...)` until completed\n4. **Analyze** → `results(action=get)` — view metrics and figures (grid-map or chain-specific; no separate analyze tool)\n5. **Compare / Export / Inference** → `jobs(action=compare)`, `results(download)`, `inference` (predict/enrich/compare/report)\n\nFor the full step-by-step procedure, call `guide_barsom_workflow`.\n\n## Tool categories\n\n| Category | Tools |\n|----------|-------|\n| Data management | `datasets` (upload/preview/list/subset/delete/add_expression) |\n| Jobs & training | `jobs` (train_map/train_siom_map/train_floop_chain/status/list/compare/cancel/delete/batch_predict) |\n| Results | `results` (get/recolor/download/export/transition_flow) |\n| Inference & export | `inference` (predict/enrich/compare/report/project_columns) |\n| Account | `account` (status/request_compute/compute_status/release_compute/history/add_funds) |\n| Utility | `guide_barsom_workflow`, `results_explorer`, `training_prep`, `send_feedback` |\n\n## Async job pattern\n\nMost operations are async. Every tool that submits a job either:\n- **Auto-polls** (results(recolor/transition_flow), inference) — waits up to the action-specific timeout then returns or gives a job_id for manual polling\n- **Returns immediately** (jobs(action=train_map/train_siom_map/train_floop_chain)) — always requires manual `jobs(action=status, job_id=...)` polling\n\n**Do not tell the user a job failed because it is still running.** If a tool returns a job_id, poll `jobs(action=status)` every 10–15 seconds. Grid-map training takes 30s–10min depending on grid size and dataset; FLooP-SIOM jobs can also take several minutes for larger `max_nodes` or `thorough` runs.\nFor FLooP-SIOM, `max_nodes` is a total node budget, not a grid side length. If omitted, the backend uses a dataset-size heuristic; reduce `max_nodes` before increasing `effort` when coverage looks collapsed.\n\n## Credit and cost\n\nJobs consume compute credits. Inference jobs are priced the same as projection jobs. Check `account(action=status)` to see the remaining balance and queue depth before starting large jobs.\n\n## Key constraints\n\n- For parameter guidance (grid, epochs, presets, when to use standard SOM vs SIOM vs FLooP-SIOM), use `training_guidance` or `prepare_training` — do not guess.\n- Column names are case-sensitive; always match exactly what `datasets(action=preview)` returns\n- Uploads may include text/categorical columns, but the default training path is still numeric/cyclic/temporal. If a categorical column truly matters, use explicit `categorical_features` for simple baseline encoding.\n- `predict` input must match the model's supported inference contract. Numeric/batch predict expects the training feature set; single-row stateless inference can also accept raw categorical strings for baseline `categorical_features` models."}),S="ui://barsom/training-monitor";i(M,b,b,{mimeType:r},async()=>{const e=await d("results-explorer");return{contents:[{uri:b,mimeType:r,text:e??"<html><body>Results Explorer view not built yet. Run: npm run build:views</body></html>"}]}}),i(M,I,I,{mimeType:r},async()=>{const e=await d("training-prep");return{contents:[{uri:I,mimeType:r,text:e??"<html><body>Training Preparation view not built yet.</body></html>"}]}}),i(M,S,S,{mimeType:r},async()=>{const e=await d("training-monitor");return{contents:[{uri:S,mimeType:r,text:e??"<html><body>Training Monitor view not built yet.</body></html>"}]}}),_(M),k(M),j(M),u(M),g(M,f),h(M),y(M),w(M),v(M),x(M),M.prompt("info","Overview of the Barivia Mapping MCP: capabilities, workflow, tools, analysis types, and tips. Use when the user asks what this MCP can do, how to get started, or what the process is.",{},()=>({messages:[{role:"user",content:{type:"text",text:["Inform the user using this overview:","","**What it is:** Barivia MCP connects you to a mapping analytics engine that learns a 2D map from high-dimensional data for visualization, clustering, pattern discovery, and temporal analysis.","","**Core workflow:**","0. **Prepare** — CSV with header; no NaNs in used columns; numeric and datetime columns are the default training path; raw categoricals can stay in the CSV but should usually be excluded unless you explicitly use simple `categorical_features` encoding","1. **Upload** — `datasets(action=upload)` with a CSV file path or inline data","2. **Preview** — `datasets(action=preview)` to inspect columns, stats, and detect cyclic/datetime fields","3. **Prepare** — use the `prepare_training` prompt for a guided checklist (column selection, transforms, cyclic encoding, feature weights, grid sizing)","4. **Train** — choose one: `jobs(action=train_map, dataset_id=...)` for a standard fixed-grid SOM, `jobs(action=train_siom_map, dataset_id=...)` for a fixed-grid SIOM with coverage regularization, or `jobs(action=train_floop_chain, dataset_id=...)` for a growing FLooP-SIOM chain/free-topology manifold. Use `preset=quick|standard|refined|high_res` for standard or SIOM map defaults","5. **Monitor** — `jobs(action=status, job_id=...)` to track progress; `results(action=get, job_id=...)` to retrieve figures when complete","6. **View and interpret** — `results(action=get)` returns metrics and figures. Grid-map jobs show U-matrix, component planes, and clusters; FLooP-SIOM jobs show chain structure, occupation/profile views, and chain metrics. Use `results(action=export)` or `results(action=download)` for more.","7. **Feedback** — Ask the user if they'd like to submit feedback via `send_feedback` based on their experience.","8. **Iterate** — `results(action=recolor)` to change colormap, `jobs(action=compare)` to compare hyperparameters, `inference(action=project_columns)` to overlay dataset columns onto the map","","All visualizations and metrics come from `results(action=get)`; use figures=all or export_type=... for more.","","**Data tools:**","- `datasets(action=subset)` — filter by row range, value thresholds (gt/lt/gte/lte), equality, or set membership. Combine row_range + filter","- `datasets(action=add_expression)` — create computed columns from expressions (ratios, differences, etc.)","- `inference(action=project_columns)` — overlay one or more dataset columns onto a trained map (component planes)","","**Output options:** Format (png/pdf/svg) and colormap (coolwarm, viridis, plasma, inferno, hsv, twilight, etc.) can be set at training or changed later via results(action=recolor).","","**Key tools:** datasets, jobs (train_map/train_siom_map/train_floop_chain/status/list/...), results, inference (predict/enrich/compare/project_columns/report), account, guide_barsom_workflow, training_prep, results_explorer.","","**Tips:**","- For the full step-by-step SOP, use the `guide_barsom_workflow` tool","- Always `datasets(action=preview)` before training to understand your data","- Use `account(action=status)` to check GPU availability, queue depth, and plan limits before large jobs","- Start with `preset=quick` for fast iteration, then `refined` for publication quality","- For time-series data, consider `transition_flow` after training","","Keep the reply scannable with headers and bullet points."].join("\n")}}]})),M.prompt("prepare_training","Guided pre-training checklist. Use after uploading a dataset and before calling jobs(action=train_map), jobs(action=train_siom_map), or jobs(action=train_floop_chain). Walks through data inspection, column selection, transforms, cyclic/temporal features, weighting, subsetting, and grid sizing.",{dataset_id:o.string().describe("Dataset ID to prepare for training")},async({dataset_id:e})=>{let t=`Please run datasets(action=preview, dataset_id="${e}") to inspect columns, then datasets(action=analyze, dataset_id="${e}") to see which columns and temporal periods are most informative. Then choose the training path: jobs(action=train_map, dataset_id="${e}", ...) for a standard fixed-grid SOM, jobs(action=train_siom_map, dataset_id="${e}", ...) for a fixed-grid SIOM, or jobs(action=train_floop_chain, dataset_id="${e}", ...) for a growing FLooP-SIOM chain/free-topology manifold.`;try{const o=await c("GET",`/v1/docs/prepare_training?dataset_id=${e}`);o.prompt&&(t=o.prompt)}catch(e){}return{messages:[{role:"user",content:{type:"text",text:t}}]}});const O=new t;(async function(){try{const e=await n(c,l,d);p(e)}catch(e){process.env.BARIVIA_VIZ_PORT&&console.error("Barivia viz server failed to start:",e)}const e=M.server;e.oninitialized=()=>{const t=e.getClientCapabilities(),o=a(t);m(!!o?.mimeTypes?.includes(r))},await M.connect(O)})().catch(console.error);
2
+ import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as o}from"zod";import{getUiCapability as a,registerAppResource as i,RESOURCE_MIME_TYPE as r}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as n}from"./viz-server.js";import{API_KEY as s,apiCall as l,apiRawCall as c,loadViewHtml as p,setVizPort as d,setClientSupportsMcpApps as m}from"./shared.js";import{registerDatasetsTool as u}from"./tools/datasets.js";import{registerJobsTool as f,JOBS_DESCRIPTION_BASE as g}from"./tools/jobs.js";import{registerResultsTool as h}from"./tools/results.js";import{registerExploreMapTool as _,RESULTS_EXPLORER_URI as b}from"./tools/explore_map.js";import{registerAccountTool as y}from"./tools/account.js";import{registerInferenceTool as w}from"./tools/inference.js";import{registerGuideBarsomTool as j}from"./tools/guide_barsom.js";import{registerTrainingGuidanceTool as v}from"./tools/training_guidance.js";import{registerFeedbackTool as x}from"./tools/feedback.js";import{registerTrainingPrepTools as I,TRAINING_PREP_URI as k}from"./tools/training_prep.js";import{registerTrainingMonitorTool as M,TRAINING_MONITOR_URI as P}from"./tools/training_monitor.js";s||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));const O=new e({name:"analytics-engine",version:"0.7.10",instructions:"# Barivia Mapping Analytics Engine\n\nYou have access to a mapping (Self-Organizing Map) analytics platform that projects high-dimensional data onto a 2D grid, revealing clusters, gradients, and anomalies.\n\n## Typical workflow\n\n1. **Upload** → `datasets(action=upload)` — ingest a CSV\n2. **Preview** → `datasets(action=preview)` — inspect columns, detect cyclics/datetimes; add derived columns with `datasets(action=add_expression)` if needed\n3. **Train** → choose one:\n - `jobs(action=train_map, dataset_id=...)` for a standard fixed-grid SOM\n - `jobs(action=train_siom_map, dataset_id=...)` for a fixed-grid SIOM with coverage regularization\n - `jobs(action=train_floop_siom, dataset_id=...)` for FLooP-SIOM when the plan includes all_algorithms (Premium or Enterprise; default topology=free / CHL; optional topology=chain)\n Returns a job_id; poll `jobs(action=status, job_id=...)` until completed\n4. **Analyze** → `results(action=get)` — view metrics and figures (grid-map or FLooP-specific; no separate analyze tool)\n5. **Compare / Export / Inference** → `jobs(action=compare)`, `results(download)`, `inference` (predict/enrich/compare/report)\n\nFor **proxy model, full tool map, async rules, training modes (tier-scoped), and optional MCP App UIs**, call `guide_barsom_workflow` first—it loads the ground-truth bootstrap from the API (`GET /v1/docs/workflow`; this section stays short on purpose).\n\n## Tool categories\n\n| Category | Tools |\n|----------|-------|\n| Data management | `datasets` (upload/preview/list/subset/delete/add_expression) |\n| Jobs & training | `jobs` (train_map/train_siom_map/train_floop_siom/status/list/compare/cancel/delete/batch_predict; deprecated alias train_floop_chain) |\n| Results | `results` (get/recolor/download/export/transition_flow) |\n| Inference & export | `inference` (predict/enrich/compare/report/project_columns) |\n| Account | `account` (status/request_compute/compute_status/release_compute/history/add_funds) |\n| Utility | `guide_barsom_workflow`, `training_guidance`, `training_prep`, `results_explorer`, `training_monitor`, `send_feedback` |\n\n## Optional rich UI (MCP Apps)\n\n**None of these are required** to train, poll, or read results—`jobs` + `results` + text output are enough. **Recommended** for learning: `training_prep` (preparation), `results_explorer` (postprocessing). **Optional:** `training_monitor(job_id)` for visual progress; `jobs(action=status)` is sufficient without it.\n\n## Async job pattern\n\nMost operations are async. Every tool that submits a job either:\n- **Auto-polls** (results(recolor/transition_flow), inference) — waits up to the action-specific timeout then returns or gives a job_id for manual polling\n- **Returns immediately** (jobs(action=train_map/train_siom_map/train_floop_siom)) — always requires manual `jobs(action=status, job_id=...)` polling (`train_floop_chain` is a deprecated alias for `train_floop_siom`)\n\n**Do not tell the user a job failed because it is still running.** If a tool returns a job_id, poll `jobs(action=status)` every 10–15 seconds. Grid-map training takes 30s–10min depending on grid size and dataset; FLooP-SIOM jobs can also take several minutes for larger `max_nodes` or `thorough` runs.\nFor FLooP-SIOM, `max_nodes` is a total node budget, not a grid side length. If omitted, the backend uses a dataset-size heuristic; reduce `max_nodes` before increasing `effort` when coverage looks collapsed.\n\n## Credit and cost\n\nJobs consume compute credits. Inference jobs are priced the same as projection jobs. Check `account(action=status)` to see the remaining balance and queue depth before starting large jobs.\n\n## Key constraints\n\n- For parameter guidance (grid, epochs, presets, when to use standard SOM vs SIOM vs FLooP-SIOM), use `training_guidance` or `prepare_training` — do not guess.\n- Column names are case-sensitive; always match exactly what `datasets(action=preview)` returns\n- Uploads may include text/categorical columns, but the default training path is still numeric/cyclic/temporal. If a categorical column truly matters, use explicit `categorical_features` for simple baseline encoding.\n- `predict` input must match the model's supported inference contract. Numeric/batch predict expects the training feature set; single-row stateless inference can also accept raw categorical strings for baseline `categorical_features` models."});i(O,b,b,{mimeType:r},async()=>{const e=await p("results-explorer");return{contents:[{uri:b,mimeType:r,text:e??"<html><body>Results Explorer view not built yet. Run: npm run build:views</body></html>"}]}}),i(O,k,k,{mimeType:r},async()=>{const e=await p("training-prep");return{contents:[{uri:k,mimeType:r,text:e??"<html><body>Training Preparation view not built yet.</body></html>"}]}}),i(O,P,P,{mimeType:r},async()=>{const e=await p("training-monitor");return{contents:[{uri:P,mimeType:r,text:e??"<html><body>Training Monitor view not built yet.</body></html>"}]}}),_(O),I(O),M(O),j(O),u(O),f(O,g),h(O),y(O),w(O),v(O),x(O),O.prompt("info","Overview of the Barivia Mapping MCP: capabilities, workflow, tools, analysis types, and tips. Use when the user asks what this MCP can do, how to get started, or what the process is.",{},()=>({messages:[{role:"user",content:{type:"text",text:["Inform the user using this overview:","","**What it is:** Barivia MCP connects you to a mapping analytics engine that learns a 2D map from high-dimensional data for visualization, clustering, pattern discovery, and temporal analysis.","","**Orientation:** Call `guide_barsom_workflow` first for proxy model, full tool list, async rules, training modes, and optional MCP App UIs (defer long-form detail there).","","**Core workflow:**","0. **Prepare** — CSV with header; no NaNs in used columns; numeric and datetime columns are the default training path; raw categoricals can stay in the CSV but should usually be excluded unless you explicitly use simple `categorical_features` encoding","1. **Upload** — `datasets(action=upload)` with a CSV file path or inline data","2. **Preview** — `datasets(action=preview)` to inspect columns, stats, and detect cyclic/datetime fields","3. **Prepare** — use the `prepare_training` prompt for a guided checklist (column selection, transforms, cyclic encoding, feature weights, grid sizing)","4. **Train** — choose one: `jobs(action=train_map, dataset_id=...)` for a standard fixed-grid SOM, `jobs(action=train_siom_map, dataset_id=...)` for a fixed-grid SIOM with coverage regularization, or `jobs(action=train_floop_siom, dataset_id=...)` for FLooP-SIOM only on Premium / Enterprise (`all_algorithms`; default topology=free / CHL; optional topology=chain). Use `preset=quick|standard|refined|high_res` for standard or SIOM map defaults","5. **Monitor** — `jobs(action=status, job_id=...)` every 10–15s until complete (required path). Optional: `training_monitor(job_id=...)` for a visual panel—not required.","6. **View and interpret** — `results(action=get)` returns metrics and figures. Grid-map jobs show U-matrix, component planes, and clusters; FLooP-SIOM jobs show structure/coverage, occupation/profile views, and metrics. Optional: `results_explorer(job_id=...)` for interactive browsing; `results(action=get)` alone is enough.","7. **Feedback** — Ask the user if they'd like to submit feedback via `send_feedback` based on their experience.","8. **Iterate** — `results(action=recolor)` to change colormap, `jobs(action=compare)` to compare hyperparameters, `inference(action=project_columns)` to overlay dataset columns onto the map","","All visualizations and metrics come from `results(action=get)`; use figures=all or export_type=... for more.","","**Data tools:**","- `datasets(action=subset)` — filter by row range, value thresholds (gt/lt/gte/lte), equality, or set membership. Combine row_range + filter","- `datasets(action=add_expression)` — create computed columns from expressions (ratios, differences, etc.)","- `inference(action=project_columns)` — overlay one or more dataset columns onto a trained map (component planes)","","**Output options:** Format (png/pdf/svg) and colormap (coolwarm, viridis, plasma, inferno, hsv, twilight, etc.) can be set at training or changed later via results(action=recolor).","","**Key tools:** datasets, jobs (train_map/train_siom_map/train_floop_siom/status/list/...), results, inference (predict/enrich/compare/project_columns/report), account, guide_barsom_workflow, training_guidance, training_prep, results_explorer, training_monitor.","","**Optional rich UI:** `training_prep` and `results_explorer` are recommended for prep and postprocessing; `training_monitor` is optional. None are required—`jobs` + `results` complete the workflow.","","**Tips:**","- For the full step-by-step SOP and capabilities text (from the API, scoped to your plan), use the `guide_barsom_workflow` tool","- Always `datasets(action=preview)` before training to understand your data","- Use `account(action=status)` to check GPU availability, queue depth, and plan limits before large jobs","- Start with `preset=quick` for fast iteration, then `refined` for publication quality","- For time-series data, consider `transition_flow` after training","","Keep the reply scannable with headers and bullet points."].join("\n")}}]})),O.prompt("prepare_training","Guided pre-training checklist. Use after uploading a dataset and before calling jobs(action=train_map), jobs(action=train_siom_map), or jobs(action=train_floop_siom) when entitled. Walks through data inspection, column selection, transforms, cyclic/temporal features, weighting, subsetting, and grid sizing.",{dataset_id:o.string().describe("Dataset ID to prepare for training")},async({dataset_id:e})=>{let t=`Please run datasets(action=preview, dataset_id="${e}") to inspect columns, then datasets(action=analyze, dataset_id="${e}") to see which columns and temporal periods are most informative. Then choose the training path: jobs(action=train_map, dataset_id="${e}", ...) for a standard fixed-grid SOM, jobs(action=train_siom_map, dataset_id="${e}", ...) for a fixed-grid SIOM, or jobs(action=train_floop_siom, dataset_id="${e}", ...) for FLooP-SIOM (default topology=free / CHL; optional topology=chain).`;try{const o=await l("GET",`/v1/docs/prepare_training?dataset_id=${e}`);o.prompt&&(t=o.prompt)}catch(e){}return{messages:[{role:"user",content:{type:"text",text:t}}]}});const C=new t;(async function(){try{const e=await n(l,c,p);d(e)}catch(e){process.env.BARIVIA_VIZ_PORT&&console.error("Barivia viz server failed to start:",e)}const e=O.server;e.oninitialized=()=>{const t=e.getClientCapabilities(),o=a(t);m(!!o?.mimeTypes?.includes(r))},await O.connect(C)})().catch(console.error);
package/dist/shared.js CHANGED
@@ -14,6 +14,12 @@ export const API_KEY = process.env.BARIVIA_API_KEY ?? process.env.BARSOM_API_KEY
14
14
  export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ?? "30000", 10);
15
15
  export const MAX_RETRIES = 2;
16
16
  export const RETRYABLE_STATUS = new Set([502, 503, 504]);
17
+ /** User-facing links; keep aligned with barivia.se / api.barivia.se. */
18
+ export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
19
+ /** Poll window for datasets(add_expression) / derive jobs (server-side work can exceed 30s). */
20
+ export const POLL_DERIVE_MAX_MS = 120_000;
21
+ /** Large CSV uploads may exceed default FETCH_TIMEOUT_MS. */
22
+ export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
17
23
  // ---------------------------------------------------------------------------
18
24
  // Shared state (mutable)
19
25
  // ---------------------------------------------------------------------------
@@ -149,10 +155,50 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
149
155
  throw new Error(`File not accessible. For relative paths, set BARIVIA_WORKSPACE_ROOT in your MCP config. Or use an absolute path or file:// URI.`);
150
156
  }
151
157
  }
152
- // ---------------------------------------------------------------------------
153
- // API helpers
154
- // ---------------------------------------------------------------------------
155
- export async function apiCall(method, path, body, extraHeaders) {
158
+ /** User-visible API error line (includes request id for support). Exported for tests. */
159
+ export function formatApiErrorMessage(status, bodyText, requestId) {
160
+ let parsed = null;
161
+ try {
162
+ const j = JSON.parse(bodyText);
163
+ if (j && typeof j === "object")
164
+ parsed = j;
165
+ }
166
+ catch {
167
+ /* ignore */
168
+ }
169
+ const detail = (parsed?.error != null && String(parsed.error)) ||
170
+ (bodyText.trim() ? bodyText.trim() : `HTTP ${status}`);
171
+ const code = parsed?.error_code != null ? ` (error_code: ${parsed.error_code})` : "";
172
+ const accountHint = ` Regenerate or verify your key via ${PUBLIC_SITE_ORIGIN} if needed.`;
173
+ const hint = status === 400
174
+ ? " Check parameter types and required fields."
175
+ : status === 401
176
+ ? ` Check BARIVIA_API_KEY in your MCP config.${accountHint}`
177
+ : status === 403
178
+ ? ` Access denied for this operation (plan or permissions).${accountHint}`
179
+ : status === 402
180
+ ? ` Credits or subscription may be insufficient — see ${PUBLIC_SITE_ORIGIN}.`
181
+ : status === 404
182
+ ? " The resource may not exist or may have been deleted."
183
+ : status === 409
184
+ ? " The job may not be in the expected state."
185
+ : status === 429
186
+ ? " Rate limit exceeded — wait a moment and retry."
187
+ : status >= 500
188
+ ? " Server error — retry later."
189
+ : "";
190
+ const rid = ` (request id: ${requestId} — include if contacting support)`;
191
+ return `${detail}${code}${hint}${rid}`;
192
+ }
193
+ function throwApiError(status, bodyText, requestId) {
194
+ const err = new Error(formatApiErrorMessage(status, bodyText, requestId));
195
+ err.httpStatus = status;
196
+ throw err;
197
+ }
198
+ /**
199
+ * @param requestTimeoutMs Optional per-request timeout (default `BARIVIA_FETCH_TIMEOUT_MS`).
200
+ */
201
+ export async function apiCall(method, path, body, extraHeaders, requestTimeoutMs) {
156
202
  const url = `${API_URL}${path}`;
157
203
  const contentType = extraHeaders?.["Content-Type"] ?? "application/json";
158
204
  const requestId = Math.random().toString(36).slice(2, 10);
@@ -167,6 +213,7 @@ export async function apiCall(method, path, body, extraHeaders) {
167
213
  serializedBody =
168
214
  contentType === "application/json" ? JSON.stringify(body) : String(body);
169
215
  }
216
+ const effectiveTimeout = requestTimeoutMs ?? FETCH_TIMEOUT_MS;
170
217
  const t0 = Date.now();
171
218
  console.error(`${new Date().toISOString()} API -> ${method} ${path} rid=${requestId}`);
172
219
  let lastError;
@@ -176,7 +223,7 @@ export async function apiCall(method, path, body, extraHeaders) {
176
223
  method,
177
224
  headers,
178
225
  body: serializedBody,
179
- });
226
+ }, effectiveTimeout);
180
227
  const text = await resp.text();
181
228
  if (!resp.ok) {
182
229
  if (attempt < MAX_RETRIES && isTransientError(null, resp.status)) {
@@ -184,23 +231,7 @@ export async function apiCall(method, path, body, extraHeaders) {
184
231
  continue;
185
232
  }
186
233
  console.error(`${new Date().toISOString()} API <- ${resp.status} ${Date.now() - t0}ms rid=${requestId}`);
187
- const errBody = (() => { try {
188
- return JSON.parse(text);
189
- }
190
- catch {
191
- return null;
192
- } })();
193
- const detail = errBody?.error ?? text;
194
- const hint = resp.status === 400 ? " Check parameter types and required fields."
195
- : resp.status === 401 ? " Check BARIVIA_API_KEY in your environment or regenerate at https://barivia.com/dashboard."
196
- : resp.status === 404 ? " The resource may not exist or may have been deleted."
197
- : resp.status === 409 ? " The job may not be in the expected state."
198
- : resp.status === 429 ? " Rate limit exceeded — wait a moment and retry."
199
- : "";
200
- const code = errBody?.error_code ? ` (error_code: ${errBody.error_code})` : "";
201
- const err = new Error(`${detail}${code}${hint}`);
202
- err.httpStatus = resp.status;
203
- throw err;
234
+ throwApiError(resp.status, text, requestId);
204
235
  }
205
236
  console.error(`${new Date().toISOString()} API <- ${resp.status} ${Date.now() - t0}ms rid=${requestId}`);
206
237
  return JSON.parse(text);
@@ -211,27 +242,38 @@ export async function apiCall(method, path, body, extraHeaders) {
211
242
  await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
212
243
  continue;
213
244
  }
245
+ if (err instanceof DOMException &&
246
+ err.name === "AbortError" &&
247
+ !err.httpStatus) {
248
+ throw new Error(`Request timed out after ${effectiveTimeout}ms. Increase BARIVIA_FETCH_TIMEOUT_MS in your MCP env (e.g. 120000) for slow or large requests. (request id: ${requestId})`);
249
+ }
214
250
  throw err;
215
251
  }
216
252
  }
217
253
  throw lastError;
218
254
  }
219
255
  /** Fetch raw bytes from the API (for image downloads). */
220
- export async function apiRawCall(path) {
256
+ export async function apiRawCall(path, requestTimeoutMs) {
221
257
  const url = `${API_URL}${path}`;
258
+ const requestId = Math.random().toString(36).slice(2, 10);
259
+ const effectiveTimeout = requestTimeoutMs ?? FETCH_TIMEOUT_MS;
222
260
  let lastError;
223
261
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
224
262
  try {
225
263
  const resp = await fetchWithTimeout(url, {
226
264
  method: "GET",
227
- headers: { Authorization: `Bearer ${API_KEY}` },
228
- });
265
+ headers: {
266
+ Authorization: `Bearer ${API_KEY}`,
267
+ "X-Request-ID": requestId,
268
+ },
269
+ }, effectiveTimeout);
229
270
  if (!resp.ok) {
230
271
  if (attempt < MAX_RETRIES && isTransientError(null, resp.status)) {
231
272
  await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
232
273
  continue;
233
274
  }
234
- throw new Error(`Request failed with status ${resp.status}`);
275
+ const text = await resp.text();
276
+ throwApiError(resp.status, text, requestId);
235
277
  }
236
278
  const arrayBuf = await resp.arrayBuffer();
237
279
  return {
@@ -245,6 +287,11 @@ export async function apiRawCall(path) {
245
287
  await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
246
288
  continue;
247
289
  }
290
+ if (err instanceof DOMException &&
291
+ err.name === "AbortError" &&
292
+ !err.httpStatus) {
293
+ throw new Error(`Request timed out after ${effectiveTimeout}ms. Increase BARIVIA_FETCH_TIMEOUT_MS (e.g. 120000) for large images. (request id: ${requestId})`);
294
+ }
248
295
  throw err;
249
296
  }
250
297
  }
@@ -286,25 +333,34 @@ export async function pollUntilComplete(jobId, maxWaitMs = 30_000, intervalMs =
286
333
  }
287
334
  return { status: "timeout" };
288
335
  }
289
- /** Fetch training guidance from API (used by training_guidance tool). Domain knowledge returned when asked; valid license required. */
336
+ function formatGuidanceScopeSuffix(config) {
337
+ const scope = config?.guidance_scope;
338
+ const ajt = scope?.allowed_job_types;
339
+ if (Array.isArray(ajt) && ajt.length > 0) {
340
+ return "\n\n(Your plan allows these training job types: " + ajt.map(String).join(", ") + ")";
341
+ }
342
+ return "";
343
+ }
344
+ /** Fetch training guidance from API (used by training_guidance tool). Domain knowledge returned when asked; valid license required. Hints are filtered by plan (allowed_job_types). */
290
345
  export async function fetchTrainingGuidanceFromApi() {
291
346
  try {
292
347
  const config = (await apiCall("GET", "/v1/training/config"));
348
+ const scopeSuffix = formatGuidanceScopeSuffix(config);
293
349
  const presetDesc = config?.preset_descriptions;
294
350
  const presetBlock = Array.isArray(presetDesc) && presetDesc.length > 0
295
351
  ? "Presets: " + presetDesc.join(" | ") + "\n\n"
296
352
  : "";
297
353
  const hints = config?.training_hints;
298
354
  if (Array.isArray(hints) && hints.length > 0) {
299
- return presetBlock + "Parameter guidance (from API):\n- " + hints.join("\n- ");
355
+ return presetBlock + "Parameter guidance (from API):\n- " + hints.join("\n- ") + scopeSuffix;
300
356
  }
301
357
  const pg = config?.parameter_guidance;
302
358
  if (pg && typeof pg === "object") {
303
359
  const lines = Object.entries(pg).map(([k, v]) => `${k}: ${v}`);
304
- return presetBlock + "Parameter guidance (from API):\n- " + lines.join("\n- ");
360
+ return presetBlock + "Parameter guidance (from API):\n- " + lines.join("\n- ") + scopeSuffix;
305
361
  }
306
362
  if (presetBlock)
307
- return presetBlock + "No additional parameter guidance in config.";
363
+ return presetBlock + "No additional parameter guidance in config." + scopeSuffix;
308
364
  }
309
365
  catch (e) {
310
366
  if (e?.httpStatus === 401 || e?.httpStatus === 403)
@@ -313,6 +369,25 @@ export async function fetchTrainingGuidanceFromApi() {
313
369
  }
314
370
  return "No parameter guidance available.";
315
371
  }
372
+ /**
373
+ * Full MCP workflow + bootstrap text from API (tier-scoped). Null on network/parse failure (caller may show offline stub).
374
+ * Re-throws 401/403 so the MCP host surfaces auth errors.
375
+ */
376
+ export async function fetchWorkflowGuideFromApi() {
377
+ try {
378
+ const data = (await apiCall("GET", "/v1/docs/workflow"));
379
+ const md = data?.workflow_markdown;
380
+ if (typeof md === "string" && md.trim().length > 0) {
381
+ return md.trim();
382
+ }
383
+ return null;
384
+ }
385
+ catch (e) {
386
+ if (e?.httpStatus === 401 || e?.httpStatus === 403)
387
+ throw e;
388
+ return null;
389
+ }
390
+ }
316
391
  // ---------------------------------------------------------------------------
317
392
  // Image helpers
318
393
  // ---------------------------------------------------------------------------
@@ -331,13 +406,13 @@ export function getCaptionForImage(filename) {
331
406
  if (base.startsWith("coverage_overview"))
332
407
  return "FLooP coverage overview — single-panel Voronoi colored by SIOM occupation measure.";
333
408
  if (base.startsWith("structure"))
334
- return "FLooP structure overview — topology-aware surface without the extra node/edge overlay clutter.";
409
+ return "FLooP structure overview — default free (CHL) topology; surface without the extra node/edge overlay clutter.";
335
410
  if (base.startsWith("umatrix"))
336
411
  return "U-matrix — weight distance between neighbors. Dark = similar (cluster interior), bright = dissimilar (boundary).";
337
412
  if (base.startsWith("hit_histogram"))
338
413
  return "Hit histogram — sample count per node. Hot = dense clusters; empty = sparse or oversized grid.";
339
414
  if (base.startsWith("hits"))
340
- return "FLooP hit distribution — Voronoi cells colored by how often chain nodes win.";
415
+ return "FLooP hit distribution — Voronoi cells colored by how often nodes win the BMU (free or chain topology).";
341
416
  if (base.startsWith("clusters"))
342
417
  return "Cluster analysis — boundary detection. Colors = cluster assignments.";
343
418
  if (base.startsWith("transition_flow"))
@@ -509,4 +584,3 @@ export async function loadViewHtml(viewName) {
509
584
  }
510
585
  return null;
511
586
  }
512
- //# sourceMappingURL=shared.js.map
@@ -35,8 +35,17 @@ NOT FOR: Training itself — use jobs(action=train_map). This tool only manages
35
35
  const fmtLimit = (v) => v === -1 || v === "-1" ? "unlimited" : String(v ?? "?");
36
36
  const historyData = await apiCall("GET", "/v1/compute/history?limit=5").catch(() => null);
37
37
  const leaseData = await apiCall("GET", "/v1/compute/lease").catch(() => null);
38
+ const algoNames = {
39
+ train_som: "SOM",
40
+ train_siom: "SIOM",
41
+ train_floop_siom: "FLooP-SIOM",
42
+ };
43
+ const algos = Array.isArray(plan.allowed_algorithms) && plan.allowed_algorithms.length > 0
44
+ ? plan.allowed_algorithms.map((a) => algoNames[a] ?? a).join(", ")
45
+ : "All";
38
46
  const lines = [
39
47
  `Plan: ${String(plan.tier ?? "unknown").charAt(0).toUpperCase()}${String(plan.tier ?? "unknown").slice(1)} | Compute: ${computeDesc}`,
48
+ ` Algorithms: ${algos}`,
40
49
  ` Concurrency: ${plan.max_concurrent_jobs ?? "?"} jobs | Datasets: ${fmtLimit(plan.max_datasets)} (${fmtLimit(plan.max_dataset_rows)} rows each)`,
41
50
  ` Monthly Jobs: ${fmtLimit(plan.max_monthly_jobs)} | Grid Size: ${fmtLimit(plan.max_grid_size)} | Features: ${fmtLimit(plan.max_features)}`,
42
51
  ];
@@ -154,4 +163,3 @@ bash scripts/manage-credits.sh add <org_id> <amount_usd>` }]
154
163
  };
155
164
  });
156
165
  }
157
- //# sourceMappingURL=account.js.map
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import fs from "node:fs/promises";
3
3
  import { z } from "zod";
4
- import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, pollUntilComplete, } from "../shared.js";
4
+ import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, pollUntilComplete, POLL_DERIVE_MAX_MS, UPLOAD_DATASET_TIMEOUT_MS, } from "../shared.js";
5
5
  export function registerDatasetsTool(server) {
6
6
  server.tool("datasets", `Manage datasets: upload, preview, list, subset, add_expression, or delete.
7
7
 
@@ -110,7 +110,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
110
110
  const data = (await apiCall("POST", "/v1/datasets", body, {
111
111
  "X-Dataset-Name": name,
112
112
  "Content-Type": "text/csv",
113
- }));
113
+ }, UPLOAD_DATASET_TIMEOUT_MS));
114
114
  const id = data.id ?? data.dataset_id;
115
115
  if (id != null)
116
116
  data.suggested_next_step = `Suggested next step: datasets(action=preview, dataset_id=${id}) to inspect columns before training.`;
@@ -284,7 +284,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
284
284
  body.options = options;
285
285
  const data = (await apiCall("POST", `/v1/datasets/${dataset_id}/derive`, body));
286
286
  const deriveJobId = data.id;
287
- const poll = await pollUntilComplete(deriveJobId);
287
+ const poll = await pollUntilComplete(deriveJobId, POLL_DERIVE_MAX_MS);
288
288
  if (poll.status === "completed") {
289
289
  const results = (await apiCall("GET", `/v1/results/${deriveJobId}`));
290
290
  const summary = (results.summary ?? {});
@@ -341,4 +341,3 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
341
341
  throw new Error("Invalid action");
342
342
  });
343
343
  }
344
- //# sourceMappingURL=datasets.js.map
@@ -52,7 +52,7 @@ function buildMetrics(summary) {
52
52
  const samples = String(summary.n_samples ?? "N/A");
53
53
  const features = String(summary.n_features ?? "N/A");
54
54
  if (jobType === "train_floop_siom") {
55
- const topology = String(summary.topology ?? "chain");
55
+ const topology = String(summary.topology ?? "free");
56
56
  const coverage = summary.coverage;
57
57
  const utilization = coverage?.utilization != null ? `${(Number(coverage.utilization) * 100).toFixed(1)}%` : "N/A";
58
58
  const deadFraction = coverage?.dead_fraction != null ? `${(Number(coverage.dead_fraction) * 100).toFixed(1)}%` : "N/A";
@@ -194,4 +194,3 @@ export function registerExploreMapTool(server) {
194
194
  }
195
195
  });
196
196
  }
197
- //# sourceMappingURL=explore_map.js.map
@@ -27,4 +27,3 @@ export function registerFeedbackTool(server) {
27
27
  }
28
28
  });
29
29
  }
30
- //# sourceMappingURL=feedback.js.map