@barivia/barmesh-mcp 0.7.0 → 0.8.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/audit.js +3 -1
- package/dist/deferred_feedback.js +88 -0
- package/dist/index.js +1 -1
- package/dist/inventory_format.js +69 -0
- package/dist/job_monitor.js +38 -2
- package/dist/logger.js +3 -0
- package/dist/richardson_results.js +35 -0
- package/dist/shared.js +347 -45
- package/dist/tools/barmesh_results_explorer.js +44 -33
- package/dist/tools/cfd.js +20 -8
- package/dist/tools/datasets.js +61 -27
- package/dist/tools/feedback.js +71 -6
- package/dist/tools/guide.js +41 -7
- package/dist/tools/jobs.js +117 -11
- package/dist/tools/results.js +11 -5
- package/dist/tools/training_monitor.js +25 -20
- package/dist/training_monitor_curve.js +1 -1
- package/dist/ui-delivery.js +155 -0
- package/dist/views/src/views/barmesh-results-explorer/index.html +18 -16
- package/dist/views/src/views/barmesh-training-monitor/index.html +40 -28
- package/dist/viz_links.js +55 -0
- package/package.json +1 -1
package/dist/tools/results.js
CHANGED
|
@@ -2,8 +2,9 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { registerAuditedTool } from "../audit.js";
|
|
5
|
-
import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, textResult, tryAttachImage, resetInlineAttachBudget, pollUntilComplete, POLL_STAGE_MAX_MS, } from "../shared.js";
|
|
5
|
+
import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, textResult, tryAttachImage, resetInlineAttachBudget, pollUntilComplete, POLL_STAGE_MAX_MS, suggestAfterRender, } from "../shared.js";
|
|
6
6
|
import { formatConvergenceReading } from "../convergence_reading.js";
|
|
7
|
+
import { formatRichardsonResultsSummary, isRichardsonJob } from "../richardson_results.js";
|
|
7
8
|
const MESH_DEFAULT_FIGURES = ["combined", "overview_distances", "plot_vol_all_meshes", "plot_vol_steps", "learning_curve"];
|
|
8
9
|
function formatMeshResultsSummary(jobId, data, summary) {
|
|
9
10
|
const label = data.label != null && data.label !== "" ? String(data.label) : null;
|
|
@@ -39,13 +40,14 @@ const TEXT_ARTIFACTS = [
|
|
|
39
40
|
"distances_to_ref.txt",
|
|
40
41
|
"emd_stepwise.txt",
|
|
41
42
|
"cfd_metrics.json",
|
|
43
|
+
"richardson_gci.csv",
|
|
42
44
|
];
|
|
43
45
|
export function registerResultsTool(server) {
|
|
44
|
-
registerAuditedTool(server, "barmesh_results", `Fetch results of a completed CFD job: distances, convergence reading, and figures.
|
|
46
|
+
registerAuditedTool(server, "barmesh_results", `Fetch results of a completed CFD job: distances, convergence reading, and figures (mesh_convergence) or Richardson/GCI triplets (richardson).
|
|
45
47
|
|
|
46
48
|
| Action | Use when |
|
|
47
49
|
|--------|----------|
|
|
48
|
-
| get | Read the summary
|
|
50
|
+
| get | Read the summary. mesh_convergence: distances + inline figures. richardson: triplet p/GCI table + topology_warning; see richardson_gci.csv. |
|
|
49
51
|
| image | Download one figure by filename (e.g. KL_ref.png, combined.pdf). |
|
|
50
52
|
| render | Re-render the figures as publication PDFs (or SVG) on demand — PDFs are NOT generated by default. |
|
|
51
53
|
| download | Save figures and metrics to a local folder (headless / agent path). |
|
|
@@ -54,6 +56,7 @@ BEST FOR: After barmesh_jobs(action=status) shows completed (and finalize finish
|
|
|
54
56
|
FIGURES: Default bundle is combined.png, overview_distances.png (all KL/EMD panels), plot_vol_all_meshes.png, plot_vol_steps.png, and learning_curve.png — not redundant per-feature PNGs. action=get inlines the headline set; pass figures="all" for every artifact listed in summary.files, figures="none" for metrics only (recommended for agents).
|
|
55
57
|
PDFs ON DEMAND: vector PDFs are not produced by default. Use action=render (format=pdf) once, then action=image or action=download.
|
|
56
58
|
If GET /v1/results returns 404 shortly after status=completed, cfd_finalize may still be rendering — poll barmesh_jobs(status) until finalize_job_id completes.
|
|
59
|
+
UNAVAILABLE: If the mesh analysis API is temporarily unavailable, say so plainly and suggest retry later (~retry_after_sec). Figures already downloaded stay usable.
|
|
57
60
|
NOT FOR: Submitting jobs.`, {
|
|
58
61
|
action: z.enum(["get", "image", "render", "download"]).describe("get: summary + figures; image: one file; render: lazy PDF/SVG; download: save to disk"),
|
|
59
62
|
job_id: z.string().describe("Job ID"),
|
|
@@ -84,7 +87,7 @@ NOT FOR: Submitting jobs.`, {
|
|
|
84
87
|
status: "completed",
|
|
85
88
|
render_job_id: renderId,
|
|
86
89
|
format: fmt,
|
|
87
|
-
suggested_next_step:
|
|
90
|
+
suggested_next_step: suggestAfterRender(job_id, fmt),
|
|
88
91
|
});
|
|
89
92
|
}
|
|
90
93
|
if (action === "image") {
|
|
@@ -193,7 +196,10 @@ NOT FOR: Submitting jobs.`, {
|
|
|
193
196
|
throw err;
|
|
194
197
|
}
|
|
195
198
|
const summary = (data.summary ?? {});
|
|
196
|
-
const
|
|
199
|
+
const summaryText = isRichardsonJob(summary)
|
|
200
|
+
? formatRichardsonResultsSummary(job_id, data, summary)
|
|
201
|
+
: formatMeshResultsSummary(job_id, data, summary);
|
|
202
|
+
const content = [{ type: "text", text: summaryText }];
|
|
197
203
|
if (figures === "none")
|
|
198
204
|
return { content };
|
|
199
205
|
const allFiles = summary.files ?? [];
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
3
3
|
import { runMcpToolAudit } from "../audit.js";
|
|
4
|
-
import { apiCall,
|
|
4
|
+
import { apiCall, BARMESH_VIZ_VIEWS, buildStandaloneVizPageUrl, buildVizRelatedUrls, getVizPort, structuredTextResult, } from "../shared.js";
|
|
5
|
+
import { appendUiDeliveryContent, resolveUiDelivery, } from "../ui-delivery.js";
|
|
5
6
|
import { enrichWithTrainingLog, needsTrainingLogEnrichment } from "../training_review.js";
|
|
6
7
|
import { formatRunConfigTable } from "../run_config.js";
|
|
7
8
|
export const TRAINING_MONITOR_URI = "ui://barmesh/training-monitor";
|
|
8
9
|
export const TRAINING_MONITOR_REFRESH_MS = 5000;
|
|
9
|
-
function buildStructuredContent(job_id, data) {
|
|
10
|
+
function buildStructuredContent(job_id, data, port) {
|
|
10
11
|
const id = String(data.id ?? job_id);
|
|
12
|
+
const standaloneUrl = buildStandaloneVizPageUrl(port, BARMESH_VIZ_VIEWS.trainingMonitor, id);
|
|
11
13
|
return {
|
|
12
14
|
...data,
|
|
13
15
|
type: "barmesh-training-monitor",
|
|
@@ -15,6 +17,8 @@ function buildStructuredContent(job_id, data) {
|
|
|
15
17
|
id,
|
|
16
18
|
job_id: id,
|
|
17
19
|
refresh_interval_ms: TRAINING_MONITOR_REFRESH_MS,
|
|
20
|
+
standaloneUrl,
|
|
21
|
+
relatedUrls: buildVizRelatedUrls(port, id, BARMESH_VIZ_VIEWS),
|
|
18
22
|
};
|
|
19
23
|
}
|
|
20
24
|
export function registerTrainingMonitorTool(server) {
|
|
@@ -37,7 +41,7 @@ export function registerTrainingMonitorTool(server) {
|
|
|
37
41
|
if (fetch_training_log || needsTrainingLogEnrichment(data)) {
|
|
38
42
|
data = await enrichWithTrainingLog(job_id, data);
|
|
39
43
|
}
|
|
40
|
-
const structuredContent = buildStructuredContent(job_id, data);
|
|
44
|
+
const structuredContent = buildStructuredContent(job_id, data, getVizPort());
|
|
41
45
|
const progress = (data.progress ?? 0) * 100;
|
|
42
46
|
const etaSec = data.training_eta_sec != null ? Number(data.training_eta_sec) : null;
|
|
43
47
|
const elapsedSec = data.training_elapsed_sec != null ? Number(data.training_elapsed_sec) : null;
|
|
@@ -56,24 +60,25 @@ export function registerTrainingMonitorTool(server) {
|
|
|
56
60
|
const text = (runConfigNote ? `${runConfigNote}\n` : "") +
|
|
57
61
|
`Training monitor (visual MCP App, refreshes every ${TRAINING_MONITOR_REFRESH_MS / 1000}s): job ${job_id} — ${jobStatus} (${progress.toFixed(1)}%).${modeNote}${timingNote}`;
|
|
58
62
|
const content = [{ type: "text", text }];
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
63
|
+
const uiDelivery = resolveUiDelivery({
|
|
64
|
+
tool: "training_monitor",
|
|
65
|
+
jobId: job_id,
|
|
66
|
+
jobStatus,
|
|
67
|
+
});
|
|
68
|
+
appendUiDeliveryContent(content, uiDelivery, {
|
|
69
|
+
linkLabel: "Open training monitor",
|
|
70
|
+
primaryUrl: structuredContent.standaloneUrl,
|
|
71
|
+
});
|
|
72
|
+
if (jobStatus === "completed" && uiDelivery.urls?.length) {
|
|
73
|
+
const resultsUrl = uiDelivery.urls.find((u) => u.includes("results-explorer"));
|
|
74
|
+
if (resultsUrl) {
|
|
75
|
+
content.push({
|
|
76
|
+
type: "text",
|
|
77
|
+
text: `Job completed — browse figures: [Open results explorer](${resultsUrl})`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
76
80
|
}
|
|
81
|
+
structuredContent.ui_delivery = uiDelivery;
|
|
77
82
|
return {
|
|
78
83
|
...structuredTextResult(structuredContent, text, content),
|
|
79
84
|
_meta: { ui: { resourceUri: TRAINING_MONITOR_URI } },
|
|
@@ -16,7 +16,7 @@ export function isKernelTrainingComplete(data, status) {
|
|
|
16
16
|
return true;
|
|
17
17
|
return status === "completed";
|
|
18
18
|
}
|
|
19
|
-
export function teCurveLabel(base
|
|
19
|
+
export function teCurveLabel(base) {
|
|
20
20
|
return `${base} (panel)`;
|
|
21
21
|
}
|
|
22
22
|
export function formatCurveSourceNote(data) {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-capability adaptation for barmesh MCP App tools.
|
|
3
|
+
* Mirrors barsom ui-delivery; uses barmesh viz paths via viz_links.
|
|
4
|
+
*/
|
|
5
|
+
import { getClientSupportsMcpApps, getVizPort, } from "./shared.js";
|
|
6
|
+
import { buildBarmeshVizStandaloneUrl } from "./viz_links.js";
|
|
7
|
+
export function parseUiDeliveryOverride() {
|
|
8
|
+
const raw = (process.env.BARIVIA_UI_DELIVERY ?? "auto").trim().toLowerCase();
|
|
9
|
+
if (raw === "apps" || raw === "localhost" || raw === "inline" || raw === "auto") {
|
|
10
|
+
return raw;
|
|
11
|
+
}
|
|
12
|
+
return "auto";
|
|
13
|
+
}
|
|
14
|
+
function vizPortPinned() {
|
|
15
|
+
return Boolean(process.env.BARIVIA_VIZ_PORT?.trim());
|
|
16
|
+
}
|
|
17
|
+
function viewForTool(tool) {
|
|
18
|
+
return tool === "training_monitor" ? "barmesh-training-monitor" : "barmesh-results-explorer";
|
|
19
|
+
}
|
|
20
|
+
export function buildStandaloneVizUrl(view, jobId) {
|
|
21
|
+
return buildBarmeshVizStandaloneUrl(viewForTool(view), jobId);
|
|
22
|
+
}
|
|
23
|
+
export function buildHealthUrl(jobId) {
|
|
24
|
+
const port = getVizPort();
|
|
25
|
+
if (!port)
|
|
26
|
+
return undefined;
|
|
27
|
+
const base = `http://localhost:${port}/api/health`;
|
|
28
|
+
return jobId ? `${base}?job_id=${encodeURIComponent(jobId)}` : base;
|
|
29
|
+
}
|
|
30
|
+
function hintForTier(tier, tool, opts) {
|
|
31
|
+
const label = tool === "training_monitor" ? "training monitor" : "results explorer";
|
|
32
|
+
switch (tier) {
|
|
33
|
+
case "embedded":
|
|
34
|
+
return `The ${label} MCP App panel may embed. Always mention the standalone URL — some hosts list ui:// but never mount widgets.`;
|
|
35
|
+
case "localhost":
|
|
36
|
+
return opts.standaloneUrl
|
|
37
|
+
? `AGENT: Post a clickable markdown link (e.g. "[Open ${label}](${opts.standaloneUrl})") — do not bury it after metrics.`
|
|
38
|
+
: `AGENT: Viz server unavailable — use barmesh_jobs(status) and barmesh_results(get).`;
|
|
39
|
+
case "inline_image":
|
|
40
|
+
return `AGENT: Inline raster figure(s) are attached. Summarize metrics; offer barmesh_results_explorer for more figures.`;
|
|
41
|
+
case "text_only":
|
|
42
|
+
return `AGENT: No embedded panel or localhost viz — use barmesh_jobs(status) and barmesh_results(get).`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function resolveUiDelivery(options) {
|
|
46
|
+
const override = parseUiDeliveryOverride();
|
|
47
|
+
const mcpApps = getClientSupportsMcpApps();
|
|
48
|
+
const port = getVizPort() || null;
|
|
49
|
+
const standaloneUrl = buildStandaloneVizUrl(options.tool, options.jobId);
|
|
50
|
+
let delivery;
|
|
51
|
+
if (override === "apps") {
|
|
52
|
+
delivery = mcpApps ? "embedded" : port ? "localhost" : "text_only";
|
|
53
|
+
}
|
|
54
|
+
else if (override === "localhost") {
|
|
55
|
+
delivery = port ? "localhost" : options.inlineImageAttached ? "inline_image" : "text_only";
|
|
56
|
+
}
|
|
57
|
+
else if (override === "inline") {
|
|
58
|
+
delivery = options.inlineImageAttached ? "inline_image" : "text_only";
|
|
59
|
+
}
|
|
60
|
+
else if (mcpApps) {
|
|
61
|
+
delivery = "embedded";
|
|
62
|
+
}
|
|
63
|
+
else if (port) {
|
|
64
|
+
delivery = "localhost";
|
|
65
|
+
}
|
|
66
|
+
else if (options.inlineImageAttached) {
|
|
67
|
+
delivery = "inline_image";
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
delivery = "text_only";
|
|
71
|
+
}
|
|
72
|
+
const urls = [];
|
|
73
|
+
if (standaloneUrl && (delivery === "embedded" || delivery === "localhost")) {
|
|
74
|
+
urls.push(standaloneUrl);
|
|
75
|
+
}
|
|
76
|
+
const healthUrl = buildHealthUrl(options.jobId);
|
|
77
|
+
if (healthUrl && delivery === "localhost") {
|
|
78
|
+
urls.push(healthUrl);
|
|
79
|
+
}
|
|
80
|
+
const crossLink = options.tool === "training_monitor" && options.jobStatus === "completed"
|
|
81
|
+
? buildStandaloneVizUrl("results_explorer", options.jobId)
|
|
82
|
+
: options.tool === "results_explorer"
|
|
83
|
+
? buildStandaloneVizUrl("training_monitor", options.jobId)
|
|
84
|
+
: undefined;
|
|
85
|
+
if (crossLink && !urls.includes(crossLink)) {
|
|
86
|
+
urls.push(crossLink);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
delivery,
|
|
90
|
+
urls: urls.length > 0 ? urls : undefined,
|
|
91
|
+
hint_for_agent: hintForTier(delivery, options.tool, { standaloneUrl }),
|
|
92
|
+
mcp_apps_supported: mcpApps,
|
|
93
|
+
viz_port: port,
|
|
94
|
+
port_pinned: vizPortPinned(),
|
|
95
|
+
override,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Prepend prominent standalone URL + structured ui_delivery (Apps handshake may lie). */
|
|
99
|
+
export function appendUiDeliveryContent(content, block, options) {
|
|
100
|
+
const primary = options?.primaryUrl ?? block.urls?.[0];
|
|
101
|
+
const label = options?.linkLabel ?? "Open interactive view";
|
|
102
|
+
const prefix = [];
|
|
103
|
+
if (primary) {
|
|
104
|
+
const embedNote = block.delivery === "embedded"
|
|
105
|
+
? "Embedded panel if your host supports MCP Apps — otherwise open the standalone URL below (expected on some Cursor builds)."
|
|
106
|
+
: "Open the standalone URL below when no inline panel appears.";
|
|
107
|
+
prefix.push({
|
|
108
|
+
type: "text",
|
|
109
|
+
text: `${embedNote}\n` +
|
|
110
|
+
`Standalone URL (copy if Open is blocked):\n${primary}\n` +
|
|
111
|
+
`[${label}](${primary})\n` +
|
|
112
|
+
`${block.hint_for_agent}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
prefix.push({
|
|
116
|
+
type: "text",
|
|
117
|
+
text: "```json\n" + JSON.stringify({ ui_delivery: block }, null, 2) + "\n```",
|
|
118
|
+
});
|
|
119
|
+
if (primary && !block.port_pinned && block.delivery !== "text_only") {
|
|
120
|
+
prefix.push({
|
|
121
|
+
type: "text",
|
|
122
|
+
text: "Tip: set BARIVIA_VIZ_PORT in MCP env for a session-stable localhost port; re-run this tool if the page stops updating.",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
content.unshift(...prefix);
|
|
126
|
+
}
|
|
127
|
+
export function getMcpClientDiagnostics() {
|
|
128
|
+
const override = parseUiDeliveryOverride();
|
|
129
|
+
const mcpApps = getClientSupportsMcpApps();
|
|
130
|
+
const port = getVizPort() || null;
|
|
131
|
+
let activeTier = "text_only";
|
|
132
|
+
if (override === "apps") {
|
|
133
|
+
activeTier = mcpApps ? "embedded" : port ? "localhost" : "text_only";
|
|
134
|
+
}
|
|
135
|
+
else if (override === "localhost") {
|
|
136
|
+
activeTier = port ? "localhost" : "text_only";
|
|
137
|
+
}
|
|
138
|
+
else if (override === "inline") {
|
|
139
|
+
activeTier = "inline_image";
|
|
140
|
+
}
|
|
141
|
+
else if (mcpApps) {
|
|
142
|
+
activeTier = "embedded";
|
|
143
|
+
}
|
|
144
|
+
else if (port) {
|
|
145
|
+
activeTier = "localhost";
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
mcp_apps_supported: mcpApps,
|
|
149
|
+
viz_port: port,
|
|
150
|
+
viz_port_pinned: vizPortPinned(),
|
|
151
|
+
ui_delivery_override: override,
|
|
152
|
+
active_delivery_tier: activeTier,
|
|
153
|
+
health_url: buildHealthUrl(),
|
|
154
|
+
};
|
|
155
|
+
}
|