@barivia/barsom-mcp 0.10.2 → 0.10.3
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 +118 -0
- package/dist/logger.js +40 -0
- package/dist/shared.js +14 -4
- package/dist/tools/account.js +2 -1
- package/dist/tools/datasets.js +2 -1
- package/dist/tools/explore_map.js +4 -3
- package/dist/tools/feedback.js +2 -1
- package/dist/tools/guide_barsom.js +2 -1
- package/dist/tools/inference.js +2 -1
- package/dist/tools/jobs.js +2 -1
- package/dist/tools/results.js +2 -1
- package/dist/tools/training_guidance.js +2 -1
- package/dist/tools/training_monitor.js +4 -2
- package/dist/tools/training_prep.js +4 -3
- package/package.json +1 -1
package/dist/audit.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool audit wrapper — tool name, action, latency, outcome; no secrets.
|
|
3
|
+
*/
|
|
4
|
+
import { logAudit } from "./logger.js";
|
|
5
|
+
/** Keys safe to include in audit param summaries (no paths, keys, or bodies). */
|
|
6
|
+
const AUDIT_PARAM_KEYS = new Set([
|
|
7
|
+
"action",
|
|
8
|
+
"job_type",
|
|
9
|
+
"model",
|
|
10
|
+
"preset",
|
|
11
|
+
"backend",
|
|
12
|
+
"output_format",
|
|
13
|
+
"figures",
|
|
14
|
+
"job_id",
|
|
15
|
+
"dataset_id",
|
|
16
|
+
"compare",
|
|
17
|
+
"topology",
|
|
18
|
+
"viz_mode",
|
|
19
|
+
]);
|
|
20
|
+
function scrubParams(args) {
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const [k, v] of Object.entries(args)) {
|
|
23
|
+
if (!AUDIT_PARAM_KEYS.has(k))
|
|
24
|
+
continue;
|
|
25
|
+
if (v === undefined || v === null)
|
|
26
|
+
continue;
|
|
27
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
28
|
+
out[k] = v;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function extractIds(result) {
|
|
34
|
+
const ids = {};
|
|
35
|
+
if (result === null || typeof result !== "object")
|
|
36
|
+
return ids;
|
|
37
|
+
const r = result;
|
|
38
|
+
if (typeof r.job_id === "string")
|
|
39
|
+
ids.job_id = r.job_id;
|
|
40
|
+
if (typeof r.id === "string" && !ids.job_id)
|
|
41
|
+
ids.job_id = r.id;
|
|
42
|
+
if (typeof r.dataset_id === "string")
|
|
43
|
+
ids.dataset_id = r.dataset_id;
|
|
44
|
+
const sc = r.structuredContent;
|
|
45
|
+
if (sc && typeof sc === "object") {
|
|
46
|
+
const s = sc;
|
|
47
|
+
if (typeof s.jobId === "string")
|
|
48
|
+
ids.job_id = s.jobId;
|
|
49
|
+
if (typeof s.datasetId === "string")
|
|
50
|
+
ids.dataset_id = s.datasetId;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const text = JSON.stringify(result);
|
|
54
|
+
const jobMatch = text.match(/"job_id"\s*:\s*"([a-f0-9-]{36})"/i);
|
|
55
|
+
if (jobMatch && !ids.job_id)
|
|
56
|
+
ids.job_id = jobMatch[1];
|
|
57
|
+
const dsMatch = text.match(/"dataset_id"\s*:\s*"([a-f0-9-]{36})"/i);
|
|
58
|
+
if (dsMatch && !ids.dataset_id)
|
|
59
|
+
ids.dataset_id = dsMatch[1];
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* ignore */
|
|
63
|
+
}
|
|
64
|
+
return ids;
|
|
65
|
+
}
|
|
66
|
+
function errorCodeFrom(err) {
|
|
67
|
+
if (err && typeof err === "object") {
|
|
68
|
+
const e = err;
|
|
69
|
+
if (e.httpStatus !== undefined)
|
|
70
|
+
return `http_${e.httpStatus}`;
|
|
71
|
+
const m = e.message ?? "";
|
|
72
|
+
const codeMatch = m.match(/error_code:\s*(\w+)/);
|
|
73
|
+
if (codeMatch)
|
|
74
|
+
return codeMatch[1];
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Run a tool handler with structured audit logging.
|
|
80
|
+
*/
|
|
81
|
+
export async function runMcpToolAudit(tool, action, args, handler) {
|
|
82
|
+
const t0 = Date.now();
|
|
83
|
+
const params = scrubParams(args);
|
|
84
|
+
try {
|
|
85
|
+
const result = await handler();
|
|
86
|
+
const ids = extractIds(result);
|
|
87
|
+
logAudit({
|
|
88
|
+
tool,
|
|
89
|
+
action,
|
|
90
|
+
duration_ms: Date.now() - t0,
|
|
91
|
+
outcome: "ok",
|
|
92
|
+
...ids,
|
|
93
|
+
...(Object.keys(params).length > 0 ? { params } : {}),
|
|
94
|
+
});
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
logAudit({
|
|
99
|
+
tool,
|
|
100
|
+
action,
|
|
101
|
+
duration_ms: Date.now() - t0,
|
|
102
|
+
outcome: "error",
|
|
103
|
+
error_code: errorCodeFrom(err),
|
|
104
|
+
...(Object.keys(params).length > 0 ? { params } : {}),
|
|
105
|
+
});
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Register an MCP tool with structured audit logging on each invocation.
|
|
111
|
+
*/
|
|
112
|
+
export function registerAuditedTool(server, name, description, schema, handler) {
|
|
113
|
+
server.tool(name, description, schema, (async (args) => {
|
|
114
|
+
const rec = args;
|
|
115
|
+
const action = typeof rec.action === "string" && rec.action.length > 0 ? rec.action : "default";
|
|
116
|
+
return runMcpToolAudit(name, action, rec, () => handler(args));
|
|
117
|
+
}));
|
|
118
|
+
}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured stdout/stderr logging for the MCP proxy (Docker json-file friendly).
|
|
3
|
+
*/
|
|
4
|
+
const SERVICE_NAME = process.env.BARIVIA_SERVICE_NAME ?? process.env.BARSOM_SERVICE_NAME ?? "mcp-proxy";
|
|
5
|
+
const LOG_FORMAT = (process.env.BARIVIA_LOG_FORMAT ?? process.env.BARSOM_LOG_FORMAT ?? "text").toLowerCase();
|
|
6
|
+
function emit(fields) {
|
|
7
|
+
const level = fields.level ?? "info";
|
|
8
|
+
const payload = {
|
|
9
|
+
ts: new Date().toISOString(),
|
|
10
|
+
level,
|
|
11
|
+
service: SERVICE_NAME,
|
|
12
|
+
...fields,
|
|
13
|
+
};
|
|
14
|
+
delete payload.level;
|
|
15
|
+
if (LOG_FORMAT === "json") {
|
|
16
|
+
console.error(JSON.stringify(payload));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const parts = [`${payload.ts} ${level.toUpperCase().padEnd(5)} ${fields.msg}`];
|
|
20
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
21
|
+
if (k === "ts" || k === "msg" || k === "service")
|
|
22
|
+
continue;
|
|
23
|
+
if (v !== undefined && v !== null && v !== "") {
|
|
24
|
+
parts.push(`${k}=${String(v)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
console.error(parts.join(" | "));
|
|
28
|
+
}
|
|
29
|
+
export function logInfo(msg, fields = {}) {
|
|
30
|
+
emit({ ...fields, level: "info", msg });
|
|
31
|
+
}
|
|
32
|
+
export function logWarn(msg, fields = {}) {
|
|
33
|
+
emit({ ...fields, level: "warn", msg });
|
|
34
|
+
}
|
|
35
|
+
export function logError(msg, fields = {}) {
|
|
36
|
+
emit({ ...fields, level: "error", msg });
|
|
37
|
+
}
|
|
38
|
+
export function logAudit(fields) {
|
|
39
|
+
emit({ ...fields, event: "mcp_tool_call", level: "info", msg: "mcp_tool_call" });
|
|
40
|
+
}
|
package/dist/shared.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { logInfo } from "./logger.js";
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// Config
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
@@ -19,7 +20,7 @@ export const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
|
19
20
|
* X-Barsom-Client-Version so the server can annotate tool guidance with the
|
|
20
21
|
* wrapper version each action requires. Keep in sync with package.json on bump.
|
|
21
22
|
*/
|
|
22
|
-
export const CLIENT_VERSION = "0.10.
|
|
23
|
+
export const CLIENT_VERSION = "0.10.3";
|
|
23
24
|
/** User-facing links; keep aligned with barivia.se / api.barivia.se. */
|
|
24
25
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
25
26
|
/** Poll window for datasets(add_expression) / derive jobs (server-side work can exceed 30s). */
|
|
@@ -277,7 +278,7 @@ export async function apiCall(method, path, body, extraHeaders, requestTimeoutMs
|
|
|
277
278
|
}
|
|
278
279
|
const effectiveTimeout = requestTimeoutMs ?? FETCH_TIMEOUT_MS;
|
|
279
280
|
const t0 = Date.now();
|
|
280
|
-
|
|
281
|
+
logInfo("API request", { rid: requestId, method, path });
|
|
281
282
|
let lastError;
|
|
282
283
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
283
284
|
try {
|
|
@@ -292,10 +293,19 @@ export async function apiCall(method, path, body, extraHeaders, requestTimeoutMs
|
|
|
292
293
|
await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
|
|
293
294
|
continue;
|
|
294
295
|
}
|
|
295
|
-
|
|
296
|
+
logInfo("API response", {
|
|
297
|
+
rid: requestId,
|
|
298
|
+
outcome: "error",
|
|
299
|
+
duration_ms: Date.now() - t0,
|
|
300
|
+
error_code: `http_${resp.status}`,
|
|
301
|
+
});
|
|
296
302
|
throwApiError(resp.status, text, requestId);
|
|
297
303
|
}
|
|
298
|
-
|
|
304
|
+
logInfo("API response", {
|
|
305
|
+
rid: requestId,
|
|
306
|
+
outcome: "ok",
|
|
307
|
+
duration_ms: Date.now() - t0,
|
|
308
|
+
});
|
|
299
309
|
return JSON.parse(text);
|
|
300
310
|
}
|
|
301
311
|
catch (err) {
|
package/dist/tools/account.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { registerAuditedTool } from "../audit.js";
|
|
2
3
|
import { apiCall } from "../shared.js";
|
|
3
4
|
export function registerAccountTool(server) {
|
|
4
|
-
server
|
|
5
|
+
registerAuditedTool(server, "account", `Manage your Barivia account — check plan/license info, request cloud burst compute, view billing history.
|
|
5
6
|
|
|
6
7
|
| Action | Use when |
|
|
7
8
|
|--------|----------|
|
package/dist/tools/datasets.js
CHANGED
|
@@ -2,9 +2,10 @@ import path from "node:path";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { gzipSync } from "node:zlib";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { registerAuditedTool } from "../audit.js";
|
|
5
6
|
import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, pollUntilComplete, POLL_DERIVE_MAX_MS, UPLOAD_DATASET_TIMEOUT_MS, } from "../shared.js";
|
|
6
7
|
export function registerDatasetsTool(server) {
|
|
7
|
-
server
|
|
8
|
+
registerAuditedTool(server, "datasets", `Manage datasets: upload, preview, list, subset, add_expression, or delete.
|
|
8
9
|
|
|
9
10
|
| Action | Use when |
|
|
10
11
|
|--------|----------|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
3
|
+
import { registerAuditedTool, runMcpToolAudit } from "../audit.js";
|
|
3
4
|
import { apiCall, apiRawCall, getClientSupportsMcpApps, getVizPort, getCaptionForImage, getResultsImagesToFetch, mimeForFilename, structuredTextResult, tryAttachImage, } from "../shared.js";
|
|
4
5
|
export const RESULTS_EXPLORER_URI = "ui://barsom/results-explorer";
|
|
5
6
|
export const MAP_EXPLORER_URI = RESULTS_EXPLORER_URI;
|
|
@@ -164,13 +165,13 @@ export function registerExploreMapTool(server) {
|
|
|
164
165
|
},
|
|
165
166
|
_meta: { ui: { resourceUri: RESULTS_EXPLORER_URI } },
|
|
166
167
|
};
|
|
167
|
-
registerAppTool(server, "results_explorer", toolConfig, async (
|
|
168
|
+
registerAppTool(server, "results_explorer", toolConfig, async (args) => runMcpToolAudit("results_explorer", "default", args, () => handleResultsExplorer(String(args.job_id))));
|
|
168
169
|
registerAppTool(server, "explore_map", {
|
|
169
170
|
...toolConfig,
|
|
170
171
|
title: "Results Explorer",
|
|
171
172
|
description: `${toolConfig.description} Deprecated alias for results_explorer — migrate configs to results_explorer.`,
|
|
172
|
-
}, async (
|
|
173
|
-
server
|
|
173
|
+
}, async (args) => runMcpToolAudit("explore_map", "default", args, () => handleResultsExplorer(String(args.job_id))));
|
|
174
|
+
registerAuditedTool(server, "_fetch_figure", "Host / MCP App use only — do NOT invoke from agent chat. Results Explorer calls this to load one raster figure as base64; agents should use results(action=get) or results_explorer instead.", {
|
|
174
175
|
job_id: z.string(),
|
|
175
176
|
filename: z.string(),
|
|
176
177
|
}, async ({ job_id, filename }) => {
|
package/dist/tools/feedback.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { registerAuditedTool } from "../audit.js";
|
|
2
3
|
import { apiCall, API_URL, fetchWithTimeout, textResult } from "../shared.js";
|
|
3
4
|
export function registerFeedbackTool(server) {
|
|
4
|
-
server
|
|
5
|
+
registerAuditedTool(server, "send_feedback", `Send feedback or feature requests to Barivia developers (max 1400 characters, ~190 words). Use when the user has suggestions, ran into issues, or wants something improved. Do NOT call without asking the user first — but after any group of actions or downloading of results, you SHOULD prepare some 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.`, { feedback: z.string().max(1400).describe("Feedback text (max 1400 characters)") }, async ({ feedback }) => {
|
|
5
6
|
try {
|
|
6
7
|
const data = await apiCall("POST", "/v1/feedback", { feedback });
|
|
7
8
|
return textResult(data);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerAuditedTool } from "../audit.js";
|
|
1
2
|
import { fetchWorkflowGuideFromApi } from "../shared.js";
|
|
2
3
|
/** Minimal orientation when the API is unreachable; full SOP lives on GET /v1/docs/workflow. */
|
|
3
4
|
const OFFLINE_STUB = `## Offline / API unavailable
|
|
@@ -8,7 +9,7 @@ Configure \`BARIVIA_API_KEY\` and optional \`BARIVIA_API_URL\`, then call **guid
|
|
|
8
9
|
|
|
9
10
|
**Parameter hints:** call \`training_guidance\` (also API-scoped). **Async:** poll \`jobs(action=status)\` every 10–15s after submit.`;
|
|
10
11
|
export function registerGuideBarsomTool(server) {
|
|
11
|
-
server
|
|
12
|
+
registerAuditedTool(server, "guide_barsom_workflow", "Plan-scoped orientation: proxy model, tool categories, async rules, training modes, and step-by-step SOP — loaded from the Barivia API when online. Call at the start of mapping work. Offline: short stub. For field-level parameters, use training_guidance; for a narrative pre-train checklist, use the prepare_training prompt or training_prep.", {}, async () => {
|
|
12
13
|
const md = await fetchWorkflowGuideFromApi();
|
|
13
14
|
if (md) {
|
|
14
15
|
const text = "Plan-scoped workflow (from Barivia API). Text below reflects your API key / plan.\n\n" + md;
|
package/dist/tools/inference.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { registerAuditedTool } from "../audit.js";
|
|
2
3
|
import { apiCall, pollUntilComplete, tryAttachImage } from "../shared.js";
|
|
3
4
|
const PREDICT_PREVIEW_ROW_CAP = 10;
|
|
4
5
|
/** One line per scored row when worker embedded `predictions_preview` (n_rows ≤ cap). Exported for tests. */
|
|
@@ -30,7 +31,7 @@ export function formatPredictPreviewLines(summary) {
|
|
|
30
31
|
return lines;
|
|
31
32
|
}
|
|
32
33
|
export function registerInferenceTool(server) {
|
|
33
|
-
server
|
|
34
|
+
registerAuditedTool(server, "inference", `Use a trained map as a persistent inference artifact — score data, annotate the source CSV, compare datasets, project columns, or generate a report manifest.
|
|
34
35
|
|
|
35
36
|
| Action | Use when | Timing |
|
|
36
37
|
|--------|----------|--------|
|
package/dist/tools/jobs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { registerAuditedTool } from "../audit.js";
|
|
2
3
|
import { apiCall, textResult } from "../shared.js";
|
|
3
4
|
export const JOBS_DESCRIPTION_BASE = `Manage and inspect jobs.
|
|
4
5
|
|
|
@@ -224,7 +225,7 @@ export function buildTrainMapParams(args, presets) {
|
|
|
224
225
|
return { params, paramSummary, effectiveGrid };
|
|
225
226
|
}
|
|
226
227
|
export function registerJobsTool(server, description) {
|
|
227
|
-
server
|
|
228
|
+
registerAuditedTool(server, "jobs", description, {
|
|
228
229
|
action: z
|
|
229
230
|
.enum(["status", "list", "compare", "cancel", "delete", "train_map", "train_impute", "train_siom_map", "train_floop_siom", "train_floop_chain", "batch_predict", "run_baseline_study"])
|
|
230
231
|
.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"),
|
package/dist/tools/results.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { registerAuditedTool } from "../audit.js";
|
|
4
5
|
import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, pollUntilComplete, tryAttachImage, getResultsImagesToFetch, getCaptionForImage, mimeForFilename, } from "../shared.js";
|
|
5
6
|
export function registerResultsTool(server) {
|
|
6
|
-
server
|
|
7
|
+
registerAuditedTool(server, "results", `Retrieve, recolor, download, export, or run temporal flow on a completed map job.
|
|
7
8
|
|
|
8
9
|
| Action | Use when | Sync/Async |
|
|
9
10
|
|--------|----------|------------|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { registerAuditedTool } from "../audit.js";
|
|
1
2
|
import { fetchTrainingGuidanceFromApi } from "../shared.js";
|
|
2
3
|
export function registerTrainingGuidanceTool(server) {
|
|
3
|
-
server
|
|
4
|
+
registerAuditedTool(server, "training_guidance", "Structured parameter guidance for training (presets, grid/epochs/batch, model, periodic, SIOM/FLooP fields where your plan allows, categorical baselines). Served from the API and filtered to allowed_job_types. Prep ladder: prepare_training prompt = narrative checklist; this tool = JSON/hints; training_prep = interactive UI + submit_prepared_training. For full orientation and SOP, call guide_barsom_workflow first. Optional UIs: training_prep, results_explorer—not required; jobs + results suffice.", {}, async () => {
|
|
4
5
|
const text = await fetchTrainingGuidanceFromApi();
|
|
5
6
|
return { content: [{ type: "text", text }] };
|
|
6
7
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
3
|
+
import { runMcpToolAudit } from "../audit.js";
|
|
3
4
|
import { apiCall, getClientSupportsMcpApps, getVizPort, structuredTextResult, } from "../shared.js";
|
|
4
5
|
export const TRAINING_MONITOR_URI = "ui://barsom/training-monitor";
|
|
5
6
|
function buildStructuredContent(job_id, data) {
|
|
@@ -20,7 +21,8 @@ export function registerTrainingMonitorTool(server) {
|
|
|
20
21
|
job_id: z.string().describe("Training job ID to monitor"),
|
|
21
22
|
},
|
|
22
23
|
_meta: { ui: { resourceUri: TRAINING_MONITOR_URI } },
|
|
23
|
-
}, async (
|
|
24
|
+
}, async (args) => runMcpToolAudit("training_monitor", "default", args, async () => {
|
|
25
|
+
const job_id = String(args.job_id);
|
|
24
26
|
const data = (await apiCall("GET", `/v1/jobs/${job_id}`));
|
|
25
27
|
const structuredContent = buildStructuredContent(job_id, data);
|
|
26
28
|
const status = String(data.status ?? "unknown");
|
|
@@ -39,5 +41,5 @@ export function registerTrainingMonitorTool(server) {
|
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
43
|
return structuredTextResult(structuredContent, text, content);
|
|
42
|
-
});
|
|
44
|
+
}));
|
|
43
45
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
3
|
+
import { registerAuditedTool, runMcpToolAudit } from "../audit.js";
|
|
3
4
|
import { apiCall, fetchTrainingGuidanceFromApi, getClientSupportsMcpApps, getVizPort, structuredTextResult, } from "../shared.js";
|
|
4
5
|
import { buildTrainMapParams, fetchTrainingPresets, } from "./jobs.js";
|
|
5
6
|
import { attachTrainingReviewPayload, consumeTrainingReview, getTrainingReview, storeTrainingReview, } from "./training_review_store.js";
|
|
@@ -324,11 +325,11 @@ export function registerTrainingPrepTools(server) {
|
|
|
324
325
|
description: "Interactive training prep UI + guarded submit (submit_prepared_training). Prep ladder: prepare_training prompt = narrative checklist; training_guidance tool = JSON/presets; this tool = visual review. Opens inline review of variables, transforms, encodings, and hyperparameters.",
|
|
325
326
|
inputSchema: trainPrepSchema,
|
|
326
327
|
_meta: { ui: { resourceUri: TRAINING_PREP_URI } },
|
|
327
|
-
}, async ({ dataset_id, ...args }) => {
|
|
328
|
+
}, async ({ dataset_id, ...args }) => runMcpToolAudit("training_prep", "default", { dataset_id, ...args }, async () => {
|
|
328
329
|
const payload = await buildTrainingPrepPayload(dataset_id, args);
|
|
329
330
|
return structuredTextResult(payload, undefined, buildTrainingPrepContent(payload));
|
|
330
|
-
});
|
|
331
|
-
server
|
|
331
|
+
}));
|
|
332
|
+
registerAuditedTool(server, "submit_prepared_training", "Submit a previously reviewed training-prep configuration. Requires an unexpired review token and explicit confirmation.", {
|
|
332
333
|
review_token: z.string().describe("Review token returned inside training_prep structured content."),
|
|
333
334
|
explicit_confirm: z.boolean().describe("Must be true to submit the reviewed training job."),
|
|
334
335
|
}, async ({ review_token, explicit_confirm }) => {
|