@barivia/barmesh-mcp 0.2.1 → 0.3.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/README.md +6 -1
- package/dist/shared.js +10 -5
- package/dist/tools/cfd.js +2 -6
- package/dist/tools/datasets.js +11 -2
- package/dist/tools/feedback.js +2 -2
- package/dist/tools/guide.js +16 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,6 +50,11 @@ API key; otherwise the analysis calls return HTTP 403. Contact Barivia to enable
|
|
|
50
50
|
| `barmesh_richardson` | Richardson/GCI on scalar QoIs (async job). |
|
|
51
51
|
| `barmesh_jobs` | Poll job status / list jobs. |
|
|
52
52
|
| `barmesh_results` | Distances, convergence reading, and figures. |
|
|
53
|
+
| `barmesh_send_feedback` | Send a short note or bug report to the Barivia team. |
|
|
54
|
+
|
|
55
|
+
### Migration notes
|
|
56
|
+
|
|
57
|
+
- **`send_feedback` → `barmesh_send_feedback` (0.3.0):** the feedback tool was renamed so it no longer collides with the `@barivia/barsom-mcp` tool of the same name when both servers are enabled in one client. Update any direct call sites; the behavior is unchanged.
|
|
53
58
|
|
|
54
59
|
## Data format (mesh_convergence)
|
|
55
60
|
|
|
@@ -63,6 +68,6 @@ cell-volume column (`V`). Use `barmesh_prepare_mesh_data` for the full recipe.
|
|
|
63
68
|
|----------|---------|---------|
|
|
64
69
|
| `BARIVIA_API_KEY` | (required) | Your Barivia API key. |
|
|
65
70
|
| `BARIVIA_API_URL` | `https://api.barivia.se` | API base URL. |
|
|
66
|
-
| `BARIVIA_FETCH_TIMEOUT_MS` | `
|
|
71
|
+
| `BARIVIA_FETCH_TIMEOUT_MS` | `60000` | Per-request timeout (raise for large uploads). |
|
|
67
72
|
| `BARIVIA_WORKSPACE_ROOT` | workspace/cwd | Root for resolving relative `file_path` uploads. |
|
|
68
73
|
| `BARIVIA_ENFORCE_WORKSPACE_SANDBOX` | `1` | Restrict uploads to the workspace; set `0` to allow absolute paths. |
|
package/dist/shared.js
CHANGED
|
@@ -16,11 +16,11 @@ import { logInfo } from "./logger.js";
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
export const API_URL = process.env.BARIVIA_API_URL ?? process.env.BARSOM_API_URL ?? "https://api.barivia.se";
|
|
18
18
|
export const API_KEY = process.env.BARIVIA_API_KEY ?? process.env.BARSOM_API_KEY ?? "";
|
|
19
|
-
export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ?? "
|
|
19
|
+
export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ?? "60000", 10);
|
|
20
20
|
export const MAX_RETRIES = 2;
|
|
21
21
|
export const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
22
22
|
/** Single source of truth for the proxy version. Keep in sync with package.json on bump. */
|
|
23
|
-
export const CLIENT_VERSION = "0.
|
|
23
|
+
export const CLIENT_VERSION = "0.3.0";
|
|
24
24
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
25
25
|
/** Large per-cell CSV uploads may exceed the default fetch timeout. */
|
|
26
26
|
export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
|
|
@@ -69,10 +69,12 @@ export async function putPresignedStream(url, srcPath, contentType, timeoutMs =
|
|
|
69
69
|
export function isTransientError(err, status) {
|
|
70
70
|
if (status !== undefined && RETRYABLE_STATUS.has(status))
|
|
71
71
|
return true;
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
// NOTE: a client-side timeout (AbortError) is deliberately NOT retried. Re-firing
|
|
73
|
+
// a slow/expensive request (e.g. mesh-convergence enqueue, large upload) while the
|
|
74
|
+
// server is still processing the first one only multiplies load and risks duplicates
|
|
75
|
+
// — raise BARIVIA_FETCH_TIMEOUT_MS or use the async (job_id + poll) path instead.
|
|
74
76
|
if (err instanceof TypeError)
|
|
75
|
-
return true;
|
|
77
|
+
return true; // network-level fetch failure
|
|
76
78
|
return false;
|
|
77
79
|
}
|
|
78
80
|
export async function fetchWithTimeout(url, init, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
@@ -277,6 +279,9 @@ export async function apiRawCall(pathPart, requestTimeoutMs) {
|
|
|
277
279
|
await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
|
|
278
280
|
continue;
|
|
279
281
|
}
|
|
282
|
+
if (err instanceof DOMException && err.name === "AbortError" && !err.httpStatus) {
|
|
283
|
+
throw new Error(`Image request timed out after ${effectiveTimeout}ms. Increase BARIVIA_FETCH_TIMEOUT_MS (e.g. 120000) for large images. (request id: ${requestId})`);
|
|
284
|
+
}
|
|
280
285
|
throw err;
|
|
281
286
|
}
|
|
282
287
|
}
|
package/dist/tools/cfd.js
CHANGED
|
@@ -10,7 +10,6 @@ BEST FOR: A mesh-refinement study where you want a field-level (not just scalar)
|
|
|
10
10
|
NOT FOR: Single scalar quantities of interest — use barmesh_richardson for classical GCI.
|
|
11
11
|
ASYNC: This is a queued job. It returns a job id immediately. Then poll barmesh_jobs(action=status) every 10-20s; datacenter-scale grids plus EMD can take a few minutes — keep polling. On completion call barmesh_results(action=get).
|
|
12
12
|
COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh label that is not present; forgetting the cell-volume column at upload time.`, {
|
|
13
|
-
action: z.enum(["submit"]).optional().describe("submit: enqueue the analysis (default)"),
|
|
14
13
|
dataset_id: z.string().describe("Dataset ID from barmesh_datasets(upload)"),
|
|
15
14
|
feature_columns: z.array(z.string()).describe("Per-cell numeric feature columns to train the SOM on (e.g. [\"p\",\"U_mag\",\"k\",\"log_epsilon\"])"),
|
|
16
15
|
preset: z.enum(["generic", "cavity", "pitz", "datacenter"]).optional().describe("Hyperparameter preset; defaults to generic. cavity/pitz/datacenter reproduce the published settings."),
|
|
@@ -29,8 +28,7 @@ COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh
|
|
|
29
28
|
figures: z.boolean().optional().describe("Generate publication figures (default true)"),
|
|
30
29
|
label: z.string().optional().describe("Optional job label"),
|
|
31
30
|
}, async (args) => {
|
|
32
|
-
const {
|
|
33
|
-
void action;
|
|
31
|
+
const { dataset_id, label, ...rest } = args;
|
|
34
32
|
const params = {};
|
|
35
33
|
for (const [k, v] of Object.entries(rest)) {
|
|
36
34
|
if (v !== undefined && v !== null)
|
|
@@ -53,7 +51,6 @@ BEST FOR: Scalar benchmarks (reattachment length, centerline values, probe readi
|
|
|
53
51
|
NOT FOR: High-dimensional field comparison — use barmesh_mesh_convergence (complementary).
|
|
54
52
|
ASYNC: queued job; poll barmesh_jobs(action=status), then barmesh_results(action=get).
|
|
55
53
|
COMMON MISTAKES: not providing h_column or n_cells_column; mixing QoIs with different mesh sets in one CSV.`, {
|
|
56
|
-
action: z.enum(["submit"]).optional().describe("submit: enqueue the GCI job (default)"),
|
|
57
54
|
dataset_id: z.string().describe("Dataset ID (one row per mesh)"),
|
|
58
55
|
qoi_columns: z.array(z.string()).describe("Scalar QoI column names to extrapolate"),
|
|
59
56
|
mesh_column: z.string().optional().describe("Mesh label column (default mesh_id)"),
|
|
@@ -63,8 +60,7 @@ COMMON MISTAKES: not providing h_column or n_cells_column; mixing QoIs with diff
|
|
|
63
60
|
safety_factor: z.number().optional().describe("GCI safety factor Fs (default 1.25, Roache)"),
|
|
64
61
|
label: z.string().optional().describe("Optional job label"),
|
|
65
62
|
}, async (args) => {
|
|
66
|
-
const {
|
|
67
|
-
void action;
|
|
63
|
+
const { dataset_id, label, ...rest } = args;
|
|
68
64
|
const params = {};
|
|
69
65
|
for (const [k, v] of Object.entries(rest)) {
|
|
70
66
|
if (v !== undefined && v !== null)
|
package/dist/tools/datasets.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { gzipSync } from "node:zlib";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
5
|
import path from "node:path";
|
|
@@ -30,6 +31,8 @@ ESCALATION: If preview shows a feature column as non-numeric, fix the extraction
|
|
|
30
31
|
throw new Error("barmesh_datasets(upload) requires name.");
|
|
31
32
|
let body;
|
|
32
33
|
if (file_path && file_path.length > 0) {
|
|
34
|
+
// Preflight: warm plan/limits and reject over-limit uploads before reading the file.
|
|
35
|
+
await apiCall("GET", "/v1/system/info");
|
|
33
36
|
const resolved = await resolveFilePathForUpload(file_path, server);
|
|
34
37
|
const ext = path.extname(resolved).toLowerCase();
|
|
35
38
|
if (ext !== ".csv" && ext !== ".tsv") {
|
|
@@ -59,7 +62,7 @@ ESCALATION: If preview shows a feature column as non-numeric, fix the extraction
|
|
|
59
62
|
return textResult({ id: datasetId, status: ready ? "ready" : "staging", job_id: jobId,
|
|
60
63
|
suggested_next_step: ready
|
|
61
64
|
? `barmesh_datasets(action=preview, dataset_id=${datasetId})`
|
|
62
|
-
: `Still staging; poll
|
|
65
|
+
: `Still staging; poll barmesh_jobs(action=status, job_id="${jobId}").` });
|
|
63
66
|
}
|
|
64
67
|
body = await fs.readFile(resolved, "utf-8");
|
|
65
68
|
}
|
|
@@ -70,7 +73,13 @@ ESCALATION: If preview shows a feature column as non-numeric, fix the extraction
|
|
|
70
73
|
throw new Error("barmesh_datasets(upload) requires file_path or csv_data. Prefer file_path.");
|
|
71
74
|
}
|
|
72
75
|
const GZIP_THRESHOLD = 1024 * 1024;
|
|
73
|
-
const uploadHeaders = {
|
|
76
|
+
const uploadHeaders = {
|
|
77
|
+
"X-Dataset-Name": name,
|
|
78
|
+
"Content-Type": "text/csv",
|
|
79
|
+
// Deterministic key so a timed-out retry of the SAME upload reconciles to
|
|
80
|
+
// the original dataset server-side instead of creating a duplicate.
|
|
81
|
+
"Idempotency-Key": createHash("sha256").update(`${name}\n`).update(body).digest("hex"),
|
|
82
|
+
};
|
|
74
83
|
let uploadBody = body;
|
|
75
84
|
if (Buffer.byteLength(body, "utf-8") > GZIP_THRESHOLD) {
|
|
76
85
|
uploadBody = gzipSync(Buffer.from(body, "utf-8"));
|
package/dist/tools/feedback.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { registerAuditedTool } from "../audit.js";
|
|
3
3
|
import { apiCall, API_URL, fetchWithTimeout, textResult } from "../shared.js";
|
|
4
4
|
export function registerFeedbackTool(server) {
|
|
5
|
-
registerAuditedTool(server, "
|
|
5
|
+
registerAuditedTool(server, "barmesh_send_feedback", `Send feedback or feature requests to Barivia developers (CFD mesh-convergence / barmesh workflow). Use when the user has suggestions, ran into issues, or wants something improved. Do NOT call without asking the user first — but after a mesh-convergence or Richardson session, you SHOULD prepare feedback based on the user's workflow or errors encountered, show it to them, and ask for permission to send it. Once they accept, call this tool.
|
|
6
6
|
|
|
7
7
|
For a substantial issue, prefer feedback_items: submit several focused instances (each max 1400 chars) covering, e.g., symptoms, exact reproduction steps, environment, and concrete asks — they are stored together as one batch so developers see the full picture. Use the single feedback field for a short one-off note.`, {
|
|
8
8
|
feedback: z.string().max(1400).optional().describe("Single feedback note (max 1400 characters). Use feedback_items instead for a multi-part report."),
|
|
@@ -12,7 +12,7 @@ For a substantial issue, prefer feedback_items: submit several focused instances
|
|
|
12
12
|
if (feedback && feedback.trim().length > 0)
|
|
13
13
|
items.unshift(feedback.trim());
|
|
14
14
|
if (items.length === 0) {
|
|
15
|
-
throw new Error("
|
|
15
|
+
throw new Error("barmesh_send_feedback requires feedback or feedback_items (at least one non-empty entry).");
|
|
16
16
|
}
|
|
17
17
|
const body = items.length === 1 ? { feedback: items[0] } : { feedback_items: items };
|
|
18
18
|
try {
|
package/dist/tools/guide.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { registerAuditedTool } from "../audit.js";
|
|
3
2
|
import { apiCall, textResult } from "../shared.js";
|
|
4
3
|
const OFFLINE_GUIDE = `barmesh: CFD mesh-convergence on the Barivia API.
|
|
5
4
|
Two tracks: barmesh_mesh_convergence (SOM fingerprint distances) and barmesh_richardson (classical GCI).
|
|
6
5
|
Workflow: barmesh_prepare_mesh_data -> barmesh_datasets(upload) -> barmesh_mesh_convergence / barmesh_richardson -> barmesh_jobs(status) -> barmesh_results(get).
|
|
7
6
|
(API unreachable; this is the offline summary. Set BARIVIA_API_KEY / BARIVIA_API_URL.)`;
|
|
7
|
+
const OFFLINE_PREP = `barmesh mesh-data prep (offline summary; API unreachable):
|
|
8
|
+
Build ONE combined per-cell CSV across all meshes of the refinement study:
|
|
9
|
+
- one row per cell; a mesh label column (mesh_id); the physical channels you want compared (e.g. p, U_mag, k, log_epsilon — log-compress turbulence quantities); and a cell-volume column (V) for fingerprint weighting.
|
|
10
|
+
- keep the SAME feature columns across every mesh; pick the finest mesh as the reference.
|
|
11
|
+
Then: barmesh_datasets(upload) -> barmesh_mesh_convergence. (Set BARIVIA_API_KEY / BARIVIA_API_URL.)`;
|
|
8
12
|
export function registerGuideTool(server) {
|
|
9
13
|
registerAuditedTool(server, "barmesh_guide_workflow", `Get the barmesh CFD mesh-convergence workflow and tool map from the API (tier-scoped).
|
|
10
14
|
|
|
11
15
|
BEST FOR: First call in a session — orients you on the two tracks (SOM fingerprint distances and Richardson/GCI), the upload->submit->poll->results flow, and what your plan allows.
|
|
12
16
|
NOT FOR: Step-by-step mesh-data preparation — use barmesh_prepare_mesh_data for that.
|
|
13
|
-
ESCALATION: If the response says your plan does not include CFD tools, the analysis tools will return 403; contact Barivia to enable the cfd entitlement.`, {
|
|
17
|
+
ESCALATION: If the response says your plan does not include CFD tools, the analysis tools will return 403; contact Barivia to enable the cfd entitlement.`, {}, async () => {
|
|
14
18
|
try {
|
|
15
19
|
const data = (await apiCall("GET", "/v1/cfd/guide"));
|
|
16
20
|
return textResult({
|
|
@@ -29,8 +33,15 @@ ESCALATION: If the response says your plan does not include CFD tools, the analy
|
|
|
29
33
|
|
|
30
34
|
BEST FOR: Before barmesh_datasets(upload) — tells you which physical channels to extract, how to label meshes (mesh_id), the cell-volume column (V), and how to pick the reference mesh.
|
|
31
35
|
NOT FOR: Submitting jobs — after preparing the CSV, use barmesh_datasets(upload) then barmesh_mesh_convergence.
|
|
32
|
-
COMMON MISTAKES: forgetting the per-cell cell-volume column; using different channels across meshes; not log-compressing turbulence quantities (k, epsilon, omega).`, {
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
COMMON MISTAKES: forgetting the per-cell cell-volume column; using different channels across meshes; not log-compressing turbulence quantities (k, epsilon, omega).`, {}, async () => {
|
|
37
|
+
try {
|
|
38
|
+
const data = (await apiCall("GET", "/v1/cfd/prep"));
|
|
39
|
+
return textResult({ recipe: data.recipe, entitled: data.entitled });
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (e?.httpStatus === 401 || e?.httpStatus === 403)
|
|
43
|
+
throw e;
|
|
44
|
+
return textResult(OFFLINE_PREP);
|
|
45
|
+
}
|
|
35
46
|
});
|
|
36
47
|
}
|