@milaboratories/pl-mcp-server 0.2.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/index.cjs +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/server.cjs +171 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.ts +83 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +171 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/await.cjs +89 -0
- package/dist/tools/await.cjs.map +1 -0
- package/dist/tools/await.js +89 -0
- package/dist/tools/await.js.map +1 -0
- package/dist/tools/block-state.cjs +71 -0
- package/dist/tools/block-state.cjs.map +1 -0
- package/dist/tools/block-state.js +71 -0
- package/dist/tools/block-state.js.map +1 -0
- package/dist/tools/blocks.cjs +123 -0
- package/dist/tools/blocks.cjs.map +1 -0
- package/dist/tools/blocks.js +123 -0
- package/dist/tools/blocks.js.map +1 -0
- package/dist/tools/connection.cjs +33 -0
- package/dist/tools/connection.cjs.map +1 -0
- package/dist/tools/connection.js +33 -0
- package/dist/tools/connection.js.map +1 -0
- package/dist/tools/data-query.cjs +186 -0
- package/dist/tools/data-query.cjs.map +1 -0
- package/dist/tools/data-query.js +186 -0
- package/dist/tools/data-query.js.map +1 -0
- package/dist/tools/logs.cjs +57 -0
- package/dist/tools/logs.cjs.map +1 -0
- package/dist/tools/logs.js +57 -0
- package/dist/tools/logs.js.map +1 -0
- package/dist/tools/ping.cjs +14 -0
- package/dist/tools/ping.cjs.map +1 -0
- package/dist/tools/ping.js +14 -0
- package/dist/tools/ping.js.map +1 -0
- package/dist/tools/projects.cjs +56 -0
- package/dist/tools/projects.cjs.map +1 -0
- package/dist/tools/projects.js +56 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/sandbox.cjs +51 -0
- package/dist/tools/sandbox.cjs.map +1 -0
- package/dist/tools/sandbox.js +51 -0
- package/dist/tools/sandbox.js.map +1 -0
- package/dist/tools/screenshot.cjs +35 -0
- package/dist/tools/screenshot.cjs.map +1 -0
- package/dist/tools/screenshot.js +35 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/tokens.cjs +82 -0
- package/dist/tools/tokens.cjs.map +1 -0
- package/dist/tools/tokens.js +82 -0
- package/dist/tools/tokens.js.map +1 -0
- package/dist/tools/types.cjs +22 -0
- package/dist/tools/types.cjs.map +1 -0
- package/dist/tools/types.js +21 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/ui-interaction.cjs +117 -0
- package/dist/tools/ui-interaction.cjs.map +1 -0
- package/dist/tools/ui-interaction.js +117 -0
- package/dist/tools/ui-interaction.js.map +1 -0
- package/package.json +56 -0
- package/src/index.ts +7 -0
- package/src/server.ts +271 -0
- package/src/tools/await.ts +151 -0
- package/src/tools/block-state.ts +115 -0
- package/src/tools/blocks.ts +222 -0
- package/src/tools/connection.ts +63 -0
- package/src/tools/data-query.ts +308 -0
- package/src/tools/logs.ts +97 -0
- package/src/tools/ping.ts +9 -0
- package/src/tools/projects.ts +84 -0
- package/src/tools/sandbox.ts +62 -0
- package/src/tools/screenshot.ts +48 -0
- package/src/tools/tokens.test.ts +239 -0
- package/src/tools/tokens.ts +84 -0
- package/src/tools/types.ts +34 -0
- package/src/tools/ui-interaction.ts +156 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { errorResult, textResult } from "./types.js";
|
|
2
|
+
import { summarizeOutputs } from "./tokens.js";
|
|
3
|
+
import { safeEval } from "./sandbox.js";
|
|
4
|
+
import { isTimeoutError } from "@milaboratories/pl-middle-layer";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { deriveDataFromStorage } from "@platforma-sdk/model";
|
|
7
|
+
//#region src/tools/await.ts
|
|
8
|
+
function registerAwaitTools(server, ctx) {
|
|
9
|
+
server.registerTool("await_block_done", {
|
|
10
|
+
description: "Wait for a block to finish computation and outputs to stabilize. Returns block status, data, and concise output summary with token estimates. Use `transform` to extract specific data server-side on completion.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
projectId: z.string().describe("Project ID"),
|
|
13
|
+
blockId: z.string().describe("Block ID to wait for"),
|
|
14
|
+
timeout: z.number().optional().default(12e4).describe("Timeout in ms (default 120000)"),
|
|
15
|
+
transform: z.string().optional().describe("JS expression evaluated server-side when block completes. Available variables: `data` (block args), `outputs` (raw outputs), `block` (status info). Omit for default concise summary."),
|
|
16
|
+
transformTimeout: z.number().optional().default(5e3).describe("Timeout in ms for transform evaluation (default 5000).")
|
|
17
|
+
}
|
|
18
|
+
}, async ({ projectId, blockId, timeout, transform, transformTimeout }) => {
|
|
19
|
+
const project = await ctx.getOpenedProject(projectId);
|
|
20
|
+
const deadline = Date.now() + timeout;
|
|
21
|
+
while (Date.now() < deadline) {
|
|
22
|
+
const overview = await project.overview.getValue();
|
|
23
|
+
if (!overview) continue;
|
|
24
|
+
const block = overview.blocks.find((b) => b.id === blockId);
|
|
25
|
+
if (!block) return errorResult(`Block ${blockId} not found in project ${projectId}.`, "Use get_project_overview to list all block IDs in this project.");
|
|
26
|
+
if (block.calculationStatus === "Limbo") return errorResult("Block entered Limbo state (upstream failed or was stopped).", "Check upstream blocks with get_project_overview. Fix or re-run the failed upstream, then retry.");
|
|
27
|
+
if (block.calculationStatus === "NotCalculated") return errorResult("Block has not been started.", "Use run_block to start it first, then call await_block_done.");
|
|
28
|
+
if (block.calculationStatus === "Done") {
|
|
29
|
+
const remaining = Math.max(deadline - Date.now(), 1e3);
|
|
30
|
+
let state;
|
|
31
|
+
try {
|
|
32
|
+
state = await project.getBlockState(blockId).awaitStableValue(AbortSignal.timeout(remaining));
|
|
33
|
+
} catch (e) {
|
|
34
|
+
if (isTimeoutError(e)) return textResult({
|
|
35
|
+
timedOut: true,
|
|
36
|
+
status: "Done",
|
|
37
|
+
note: "Computation done but outputs did not stabilize in time. Retry with a longer timeout."
|
|
38
|
+
});
|
|
39
|
+
return errorResult(`Failed to get block state: ${e instanceof Error ? e.message : String(e)}`);
|
|
40
|
+
}
|
|
41
|
+
const data = deriveDataFromStorage(state.blockStorage);
|
|
42
|
+
const blockInfo = {
|
|
43
|
+
id: block.id,
|
|
44
|
+
title: block.title ?? block.label,
|
|
45
|
+
calculationStatus: block.calculationStatus,
|
|
46
|
+
canRun: block.canRun,
|
|
47
|
+
stale: block.stale,
|
|
48
|
+
outputErrors: block.outputErrors
|
|
49
|
+
};
|
|
50
|
+
if (transform) try {
|
|
51
|
+
return textResult({
|
|
52
|
+
status: "Done",
|
|
53
|
+
block: blockInfo,
|
|
54
|
+
result: await safeEval(transform, {
|
|
55
|
+
data,
|
|
56
|
+
outputs: state.outputs,
|
|
57
|
+
block: blockInfo
|
|
58
|
+
}, transformTimeout)
|
|
59
|
+
});
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return errorResult(`Transform failed: ${e instanceof Error ? e.message : String(e)}`, "Check your JS expression syntax. Available variables: data, outputs, block.");
|
|
62
|
+
}
|
|
63
|
+
return textResult({
|
|
64
|
+
status: "Done",
|
|
65
|
+
block: blockInfo,
|
|
66
|
+
data,
|
|
67
|
+
outputs: summarizeOutputs(state.outputs)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const pollStart = Date.now();
|
|
71
|
+
try {
|
|
72
|
+
const result = await project.overview.getFullValue();
|
|
73
|
+
await project.overview.awaitChange(AbortSignal.timeout(5e3), result.uTag);
|
|
74
|
+
} catch {}
|
|
75
|
+
const elapsed = Date.now() - pollStart;
|
|
76
|
+
if (elapsed < 500) await new Promise((r) => setTimeout(r, 500 - elapsed));
|
|
77
|
+
}
|
|
78
|
+
const block = (await project.overview.getValue())?.blocks.find((b) => b.id === blockId);
|
|
79
|
+
return textResult({
|
|
80
|
+
timedOut: true,
|
|
81
|
+
status: block?.calculationStatus ?? "Unknown",
|
|
82
|
+
hint: "The block is still running. Call await_block_done again with a longer timeout."
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { registerAwaitTools };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=await.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"await.js","names":[],"sources":["../../src/tools/await.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { isTimeoutError } from \"@milaboratories/pl-middle-layer\";\nimport { deriveDataFromStorage } from \"@platforma-sdk/model\";\nimport { z } from \"zod\";\nimport type { ToolContext } from \"./types\";\nimport { summarizeOutputs } from \"./tokens\";\nimport { safeEval } from \"./sandbox\";\nimport { errorResult, textResult } from \"./types\";\n\nexport function registerAwaitTools(server: McpServer, ctx: ToolContext): void {\n server.registerTool(\n \"await_block_done\",\n {\n description:\n \"Wait for a block to finish computation and outputs to stabilize. \" +\n \"Returns block status, data, and concise output summary with token estimates. \" +\n \"Use `transform` to extract specific data server-side on completion.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to wait for\"),\n timeout: z.number().optional().default(120000).describe(\"Timeout in ms (default 120000)\"),\n transform: z\n .string()\n .optional()\n .describe(\n \"JS expression evaluated server-side when block completes. \" +\n \"Available variables: `data` (block args), `outputs` (raw outputs), `block` (status info). \" +\n \"Omit for default concise summary.\",\n ),\n transformTimeout: z\n .number()\n .optional()\n .default(5000)\n .describe(\"Timeout in ms for transform evaluation (default 5000).\"),\n },\n },\n async ({ projectId, blockId, timeout, transform, transformTimeout }) => {\n const project = await ctx.getOpenedProject(projectId);\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const overview = await project.overview.getValue();\n if (!overview) continue;\n const block = overview.blocks.find((b) => b.id === blockId);\n if (!block)\n return errorResult(\n `Block ${blockId} not found in project ${projectId}.`,\n \"Use get_project_overview to list all block IDs in this project.\",\n );\n\n // Terminal error states — return immediately\n if (block.calculationStatus === \"Limbo\") {\n return errorResult(\n \"Block entered Limbo state (upstream failed or was stopped).\",\n \"Check upstream blocks with get_project_overview. Fix or re-run the failed upstream, then retry.\",\n );\n }\n\n if (block.calculationStatus === \"NotCalculated\") {\n return errorResult(\n \"Block has not been started.\",\n \"Use run_block to start it first, then call await_block_done.\",\n );\n }\n\n if (block.calculationStatus === \"Done\") {\n // Await stable block state with remaining time budget\n const remaining = Math.max(deadline - Date.now(), 1000);\n let state;\n try {\n state = await project\n .getBlockState(blockId)\n .awaitStableValue(AbortSignal.timeout(remaining));\n } catch (e: unknown) {\n if (isTimeoutError(e)) {\n return textResult({\n timedOut: true,\n status: \"Done\",\n note: \"Computation done but outputs did not stabilize in time. Retry with a longer timeout.\",\n });\n }\n return errorResult(\n `Failed to get block state: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n\n const data = deriveDataFromStorage(state.blockStorage);\n\n const blockInfo = {\n id: block.id,\n title: block.title ?? block.label,\n calculationStatus: block.calculationStatus,\n canRun: block.canRun,\n stale: block.stale,\n outputErrors: block.outputErrors,\n };\n\n if (transform) {\n try {\n const result = await safeEval(\n transform,\n {\n data,\n outputs: state.outputs,\n block: blockInfo,\n },\n transformTimeout,\n );\n return textResult({ status: \"Done\", block: blockInfo, result });\n } catch (e: unknown) {\n return errorResult(\n `Transform failed: ${e instanceof Error ? e.message : String(e)}`,\n \"Check your JS expression syntax. Available variables: data, outputs, block.\",\n );\n }\n }\n\n return textResult({\n status: \"Done\",\n block: blockInfo,\n data,\n outputs: summarizeOutputs(state.outputs as Record<string, unknown> | undefined),\n });\n }\n\n // Still running — wait up to 5s for overview to change, then re-poll.\n // Minimum 500ms delay to avoid busy-looping if awaitChange resolves immediately.\n const pollStart = Date.now();\n try {\n const result = await project.overview.getFullValue();\n await project.overview.awaitChange(AbortSignal.timeout(5000), result.uTag);\n } catch {\n // timeout or abort — just re-poll\n }\n const elapsed = Date.now() - pollStart;\n if (elapsed < 500) {\n await new Promise((r) => setTimeout(r, 500 - elapsed));\n }\n }\n\n // Timed out while running\n const overview = await project.overview.getValue();\n const block = overview?.blocks.find((b) => b.id === blockId);\n return textResult({\n timedOut: true,\n status: block?.calculationStatus ?? \"Unknown\",\n hint: \"The block is still running. Call await_block_done again with a longer timeout.\",\n });\n },\n );\n}\n"],"mappings":";;;;;;;AASA,SAAgB,mBAAmB,QAAmB,KAAwB;AAC5E,QAAO,aACL,oBACA;EACE,aACE;EAGF,aAAa;GACX,WAAW,EAAE,QAAQ,CAAC,SAAS,aAAa;GAC5C,SAAS,EAAE,QAAQ,CAAC,SAAS,uBAAuB;GACpD,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,KAAO,CAAC,SAAS,iCAAiC;GACzF,WAAW,EACR,QAAQ,CACR,UAAU,CACV,SACC,wLAGD;GACH,kBAAkB,EACf,QAAQ,CACR,UAAU,CACV,QAAQ,IAAK,CACb,SAAS,yDAAyD;GACtE;EACF,EACD,OAAO,EAAE,WAAW,SAAS,SAAS,WAAW,uBAAuB;EACtE,MAAM,UAAU,MAAM,IAAI,iBAAiB,UAAU;EACrD,MAAM,WAAW,KAAK,KAAK,GAAG;AAE9B,SAAO,KAAK,KAAK,GAAG,UAAU;GAC5B,MAAM,WAAW,MAAM,QAAQ,SAAS,UAAU;AAClD,OAAI,CAAC,SAAU;GACf,MAAM,QAAQ,SAAS,OAAO,MAAM,MAAM,EAAE,OAAO,QAAQ;AAC3D,OAAI,CAAC,MACH,QAAO,YACL,SAAS,QAAQ,wBAAwB,UAAU,IACnD,kEACD;AAGH,OAAI,MAAM,sBAAsB,QAC9B,QAAO,YACL,+DACA,kGACD;AAGH,OAAI,MAAM,sBAAsB,gBAC9B,QAAO,YACL,+BACA,+DACD;AAGH,OAAI,MAAM,sBAAsB,QAAQ;IAEtC,MAAM,YAAY,KAAK,IAAI,WAAW,KAAK,KAAK,EAAE,IAAK;IACvD,IAAI;AACJ,QAAI;AACF,aAAQ,MAAM,QACX,cAAc,QAAQ,CACtB,iBAAiB,YAAY,QAAQ,UAAU,CAAC;aAC5C,GAAY;AACnB,SAAI,eAAe,EAAE,CACnB,QAAO,WAAW;MAChB,UAAU;MACV,QAAQ;MACR,MAAM;MACP,CAAC;AAEJ,YAAO,YACL,8BAA8B,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,GACzE;;IAGH,MAAM,OAAO,sBAAsB,MAAM,aAAa;IAEtD,MAAM,YAAY;KAChB,IAAI,MAAM;KACV,OAAO,MAAM,SAAS,MAAM;KAC5B,mBAAmB,MAAM;KACzB,QAAQ,MAAM;KACd,OAAO,MAAM;KACb,cAAc,MAAM;KACrB;AAED,QAAI,UACF,KAAI;AAUF,YAAO,WAAW;MAAE,QAAQ;MAAQ,OAAO;MAAW,QATvC,MAAM,SACnB,WACA;OACE;OACA,SAAS,MAAM;OACf,OAAO;OACR,EACD,iBACD;MAC6D,CAAC;aACxD,GAAY;AACnB,YAAO,YACL,qBAAqB,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,IAC/D,8EACD;;AAIL,WAAO,WAAW;KAChB,QAAQ;KACR,OAAO;KACP;KACA,SAAS,iBAAiB,MAAM,QAA+C;KAChF,CAAC;;GAKJ,MAAM,YAAY,KAAK,KAAK;AAC5B,OAAI;IACF,MAAM,SAAS,MAAM,QAAQ,SAAS,cAAc;AACpD,UAAM,QAAQ,SAAS,YAAY,YAAY,QAAQ,IAAK,EAAE,OAAO,KAAK;WACpE;GAGR,MAAM,UAAU,KAAK,KAAK,GAAG;AAC7B,OAAI,UAAU,IACZ,OAAM,IAAI,SAAS,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC;;EAM1D,MAAM,SADW,MAAM,QAAQ,SAAS,UAAU,GAC1B,OAAO,MAAM,MAAM,EAAE,OAAO,QAAQ;AAC5D,SAAO,WAAW;GAChB,UAAU;GACV,QAAQ,OAAO,qBAAqB;GACpC,MAAM;GACP,CAAC;GAEL"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const require_types = require("./types.cjs");
|
|
2
|
+
const require_tokens = require("./tokens.cjs");
|
|
3
|
+
const require_sandbox = require("./sandbox.cjs");
|
|
4
|
+
let zod = require("zod");
|
|
5
|
+
let _platforma_sdk_model = require("@platforma-sdk/model");
|
|
6
|
+
//#region src/tools/block-state.ts
|
|
7
|
+
function registerBlockStateTools(server, ctx) {
|
|
8
|
+
server.registerTool("get_project_overview", {
|
|
9
|
+
description: "Get project overview with all blocks and their statuses (calculationStatus, canRun, stale, errors, upstreams/downstreams)",
|
|
10
|
+
inputSchema: { projectId: zod.z.string().describe("Project ID (must be opened)") }
|
|
11
|
+
}, async ({ projectId }) => {
|
|
12
|
+
const overview = await (await ctx.getOpenedProject(projectId)).overview.getValue();
|
|
13
|
+
if (!overview) return require_types.errorResult("Project overview not available yet.");
|
|
14
|
+
return require_types.textResult({
|
|
15
|
+
label: overview.meta.label,
|
|
16
|
+
blocks: overview.blocks.map((b) => ({
|
|
17
|
+
id: b.id,
|
|
18
|
+
title: b.title ?? b.label,
|
|
19
|
+
calculationStatus: b.calculationStatus,
|
|
20
|
+
canRun: b.canRun,
|
|
21
|
+
stale: b.stale,
|
|
22
|
+
inputsValid: b.inputsValid,
|
|
23
|
+
outputErrors: b.outputErrors,
|
|
24
|
+
upstreams: b.upstreams,
|
|
25
|
+
downstreams: b.downstreams
|
|
26
|
+
}))
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
server.registerTool("get_block_state", {
|
|
30
|
+
description: "Get block state. Returns block args (data) and a concise output summary with token estimates by default. Use `transform` to extract specific data server-side without loading full outputs into context.\n\nDefault: returns `{ data, outputs: [{ key, ok, hasValue, tokensEstimate }] }`\n\nTransform examples:\n- `outputs.logs?.value` — get one specific output value\n- `data` — get only block args\n- `({ preset: outputs.preset?.value, qc: outputs.qc?.value })` — get specific outputs",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
33
|
+
blockId: zod.z.string().describe("Block ID"),
|
|
34
|
+
transform: zod.z.string().optional().describe("JS expression evaluated server-side against full block state. Available variables: `data` (block args), `outputs` (raw outputs object). Omit for default concise summary."),
|
|
35
|
+
transformTimeout: zod.z.number().optional().default(5e3).describe("Timeout in ms for transform evaluation (default 5000).")
|
|
36
|
+
}
|
|
37
|
+
}, async ({ projectId, blockId, transform, transformTimeout }) => {
|
|
38
|
+
const state = await (await ctx.getOpenedProject(projectId)).getBlockState(blockId).getValue();
|
|
39
|
+
const data = (0, _platforma_sdk_model.deriveDataFromStorage)(state.blockStorage);
|
|
40
|
+
if (transform) try {
|
|
41
|
+
return require_types.textResult(await require_sandbox.safeEval(transform, {
|
|
42
|
+
data,
|
|
43
|
+
outputs: state.outputs
|
|
44
|
+
}, transformTimeout));
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return require_types.errorResult(`Transform failed: ${e instanceof Error ? e.message : String(e)}`, "Check your JS expression syntax. Available variables: data, outputs.");
|
|
47
|
+
}
|
|
48
|
+
return require_types.textResult({
|
|
49
|
+
data,
|
|
50
|
+
outputs: require_tokens.summarizeOutputs(state.outputs)
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
server.registerTool("set_block_data", {
|
|
54
|
+
description: "Set the user-facing data of a block (triggers args derivation and staging)",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
57
|
+
blockId: zod.z.string().describe("Block ID"),
|
|
58
|
+
data: zod.z.record(zod.z.unknown()).describe("Block data object")
|
|
59
|
+
}
|
|
60
|
+
}, async ({ projectId, blockId, data }) => {
|
|
61
|
+
await (await ctx.getOpenedProject(projectId)).mutateBlockStorage(blockId, {
|
|
62
|
+
operation: "update-block-data",
|
|
63
|
+
value: data
|
|
64
|
+
}, ctx.getAuthorMarker());
|
|
65
|
+
return require_types.textResult({ ok: true });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
exports.registerBlockStateTools = registerBlockStateTools;
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=block-state.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-state.cjs","names":["z","errorResult","textResult","safeEval","summarizeOutputs"],"sources":["../../src/tools/block-state.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { deriveDataFromStorage } from \"@platforma-sdk/model\";\nimport { z } from \"zod\";\nimport type { ToolContext } from \"./types\";\nimport { summarizeOutputs } from \"./tokens\";\nimport { safeEval } from \"./sandbox\";\nimport { errorResult, textResult } from \"./types\";\n\nexport function registerBlockStateTools(server: McpServer, ctx: ToolContext): void {\n server.registerTool(\n \"get_project_overview\",\n {\n description:\n \"Get project overview with all blocks and their statuses (calculationStatus, canRun, stale, errors, upstreams/downstreams)\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID (must be opened)\"),\n },\n },\n async ({ projectId }) => {\n const project = await ctx.getOpenedProject(projectId);\n const overview = await project.overview.getValue();\n if (!overview) return errorResult(\"Project overview not available yet.\");\n return textResult({\n label: overview.meta.label,\n blocks: overview.blocks.map((b) => ({\n id: b.id,\n title: b.title ?? b.label,\n calculationStatus: b.calculationStatus,\n canRun: b.canRun,\n stale: b.stale,\n inputsValid: b.inputsValid,\n outputErrors: b.outputErrors,\n upstreams: b.upstreams,\n downstreams: b.downstreams,\n })),\n });\n },\n );\n\n server.registerTool(\n \"get_block_state\",\n {\n description:\n \"Get block state. Returns block args (data) and a concise output summary with token estimates by default. \" +\n \"Use `transform` to extract specific data server-side without loading full outputs into context.\\n\\n\" +\n \"Default: returns `{ data, outputs: [{ key, ok, hasValue, tokensEstimate }] }`\\n\\n\" +\n \"Transform examples:\\n\" +\n \"- `outputs.logs?.value` — get one specific output value\\n\" +\n \"- `data` — get only block args\\n\" +\n \"- `({ preset: outputs.preset?.value, qc: outputs.qc?.value })` — get specific outputs\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID\"),\n transform: z\n .string()\n .optional()\n .describe(\n \"JS expression evaluated server-side against full block state. \" +\n \"Available variables: `data` (block args), `outputs` (raw outputs object). \" +\n \"Omit for default concise summary.\",\n ),\n transformTimeout: z\n .number()\n .optional()\n .default(5000)\n .describe(\"Timeout in ms for transform evaluation (default 5000).\"),\n },\n },\n async ({ projectId, blockId, transform, transformTimeout }) => {\n const project = await ctx.getOpenedProject(projectId);\n const state = await project.getBlockState(blockId).getValue();\n const data = deriveDataFromStorage(state.blockStorage);\n if (transform) {\n try {\n const result = await safeEval(\n transform,\n { data, outputs: state.outputs },\n transformTimeout,\n );\n return textResult(result);\n } catch (e: unknown) {\n return errorResult(\n `Transform failed: ${e instanceof Error ? e.message : String(e)}`,\n \"Check your JS expression syntax. Available variables: data, outputs.\",\n );\n }\n }\n return textResult({\n data,\n outputs: summarizeOutputs(state.outputs as Record<string, unknown> | undefined),\n });\n },\n );\n\n server.registerTool(\n \"set_block_data\",\n {\n description: \"Set the user-facing data of a block (triggers args derivation and staging)\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID\"),\n data: z.record(z.unknown()).describe(\"Block data object\"),\n },\n },\n async ({ projectId, blockId, data }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.mutateBlockStorage(\n blockId,\n { operation: \"update-block-data\", value: data },\n ctx.getAuthorMarker(),\n );\n return textResult({ ok: true });\n },\n );\n}\n"],"mappings":";;;;;;AAQA,SAAgB,wBAAwB,QAAmB,KAAwB;AACjF,QAAO,aACL,wBACA;EACE,aACE;EACF,aAAa,EACX,WAAWA,IAAAA,EAAE,QAAQ,CAAC,SAAS,8BAA8B,EAC9D;EACF,EACD,OAAO,EAAE,gBAAgB;EAEvB,MAAM,WAAW,OADD,MAAM,IAAI,iBAAiB,UAAU,EACtB,SAAS,UAAU;AAClD,MAAI,CAAC,SAAU,QAAOC,cAAAA,YAAY,sCAAsC;AACxE,SAAOC,cAAAA,WAAW;GAChB,OAAO,SAAS,KAAK;GACrB,QAAQ,SAAS,OAAO,KAAK,OAAO;IAClC,IAAI,EAAE;IACN,OAAO,EAAE,SAAS,EAAE;IACpB,mBAAmB,EAAE;IACrB,QAAQ,EAAE;IACV,OAAO,EAAE;IACT,aAAa,EAAE;IACf,cAAc,EAAE;IAChB,WAAW,EAAE;IACb,aAAa,EAAE;IAChB,EAAE;GACJ,CAAC;GAEL;AAED,QAAO,aACL,mBACA;EACE,aACE;EAOF,aAAa;GACX,WAAWF,IAAAA,EAAE,QAAQ,CAAC,SAAS,aAAa;GAC5C,SAASA,IAAAA,EAAE,QAAQ,CAAC,SAAS,WAAW;GACxC,WAAWA,IAAAA,EACR,QAAQ,CACR,UAAU,CACV,SACC,4KAGD;GACH,kBAAkBA,IAAAA,EACf,QAAQ,CACR,UAAU,CACV,QAAQ,IAAK,CACb,SAAS,yDAAyD;GACtE;EACF,EACD,OAAO,EAAE,WAAW,SAAS,WAAW,uBAAuB;EAE7D,MAAM,QAAQ,OADE,MAAM,IAAI,iBAAiB,UAAU,EACzB,cAAc,QAAQ,CAAC,UAAU;EAC7D,MAAM,QAAA,GAAA,qBAAA,uBAA6B,MAAM,aAAa;AACtD,MAAI,UACF,KAAI;AAMF,UAAOE,cAAAA,WALQ,MAAMC,gBAAAA,SACnB,WACA;IAAE;IAAM,SAAS,MAAM;IAAS,EAChC,iBACD,CACwB;WAClB,GAAY;AACnB,UAAOF,cAAAA,YACL,qBAAqB,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,IAC/D,uEACD;;AAGL,SAAOC,cAAAA,WAAW;GAChB;GACA,SAASE,eAAAA,iBAAiB,MAAM,QAA+C;GAChF,CAAC;GAEL;AAED,QAAO,aACL,kBACA;EACE,aAAa;EACb,aAAa;GACX,WAAWJ,IAAAA,EAAE,QAAQ,CAAC,SAAS,aAAa;GAC5C,SAASA,IAAAA,EAAE,QAAQ,CAAC,SAAS,WAAW;GACxC,MAAMA,IAAAA,EAAE,OAAOA,IAAAA,EAAE,SAAS,CAAC,CAAC,SAAS,oBAAoB;GAC1D;EACF,EACD,OAAO,EAAE,WAAW,SAAS,WAAW;AAEtC,SADgB,MAAM,IAAI,iBAAiB,UAAU,EACvC,mBACZ,SACA;GAAE,WAAW;GAAqB,OAAO;GAAM,EAC/C,IAAI,iBAAiB,CACtB;AACD,SAAOE,cAAAA,WAAW,EAAE,IAAI,MAAM,CAAC;GAElC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { errorResult, textResult } from "./types.js";
|
|
2
|
+
import { summarizeOutputs } from "./tokens.js";
|
|
3
|
+
import { safeEval } from "./sandbox.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { deriveDataFromStorage } from "@platforma-sdk/model";
|
|
6
|
+
//#region src/tools/block-state.ts
|
|
7
|
+
function registerBlockStateTools(server, ctx) {
|
|
8
|
+
server.registerTool("get_project_overview", {
|
|
9
|
+
description: "Get project overview with all blocks and their statuses (calculationStatus, canRun, stale, errors, upstreams/downstreams)",
|
|
10
|
+
inputSchema: { projectId: z.string().describe("Project ID (must be opened)") }
|
|
11
|
+
}, async ({ projectId }) => {
|
|
12
|
+
const overview = await (await ctx.getOpenedProject(projectId)).overview.getValue();
|
|
13
|
+
if (!overview) return errorResult("Project overview not available yet.");
|
|
14
|
+
return textResult({
|
|
15
|
+
label: overview.meta.label,
|
|
16
|
+
blocks: overview.blocks.map((b) => ({
|
|
17
|
+
id: b.id,
|
|
18
|
+
title: b.title ?? b.label,
|
|
19
|
+
calculationStatus: b.calculationStatus,
|
|
20
|
+
canRun: b.canRun,
|
|
21
|
+
stale: b.stale,
|
|
22
|
+
inputsValid: b.inputsValid,
|
|
23
|
+
outputErrors: b.outputErrors,
|
|
24
|
+
upstreams: b.upstreams,
|
|
25
|
+
downstreams: b.downstreams
|
|
26
|
+
}))
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
server.registerTool("get_block_state", {
|
|
30
|
+
description: "Get block state. Returns block args (data) and a concise output summary with token estimates by default. Use `transform` to extract specific data server-side without loading full outputs into context.\n\nDefault: returns `{ data, outputs: [{ key, ok, hasValue, tokensEstimate }] }`\n\nTransform examples:\n- `outputs.logs?.value` — get one specific output value\n- `data` — get only block args\n- `({ preset: outputs.preset?.value, qc: outputs.qc?.value })` — get specific outputs",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
projectId: z.string().describe("Project ID"),
|
|
33
|
+
blockId: z.string().describe("Block ID"),
|
|
34
|
+
transform: z.string().optional().describe("JS expression evaluated server-side against full block state. Available variables: `data` (block args), `outputs` (raw outputs object). Omit for default concise summary."),
|
|
35
|
+
transformTimeout: z.number().optional().default(5e3).describe("Timeout in ms for transform evaluation (default 5000).")
|
|
36
|
+
}
|
|
37
|
+
}, async ({ projectId, blockId, transform, transformTimeout }) => {
|
|
38
|
+
const state = await (await ctx.getOpenedProject(projectId)).getBlockState(blockId).getValue();
|
|
39
|
+
const data = deriveDataFromStorage(state.blockStorage);
|
|
40
|
+
if (transform) try {
|
|
41
|
+
return textResult(await safeEval(transform, {
|
|
42
|
+
data,
|
|
43
|
+
outputs: state.outputs
|
|
44
|
+
}, transformTimeout));
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return errorResult(`Transform failed: ${e instanceof Error ? e.message : String(e)}`, "Check your JS expression syntax. Available variables: data, outputs.");
|
|
47
|
+
}
|
|
48
|
+
return textResult({
|
|
49
|
+
data,
|
|
50
|
+
outputs: summarizeOutputs(state.outputs)
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
server.registerTool("set_block_data", {
|
|
54
|
+
description: "Set the user-facing data of a block (triggers args derivation and staging)",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
projectId: z.string().describe("Project ID"),
|
|
57
|
+
blockId: z.string().describe("Block ID"),
|
|
58
|
+
data: z.record(z.unknown()).describe("Block data object")
|
|
59
|
+
}
|
|
60
|
+
}, async ({ projectId, blockId, data }) => {
|
|
61
|
+
await (await ctx.getOpenedProject(projectId)).mutateBlockStorage(blockId, {
|
|
62
|
+
operation: "update-block-data",
|
|
63
|
+
value: data
|
|
64
|
+
}, ctx.getAuthorMarker());
|
|
65
|
+
return textResult({ ok: true });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { registerBlockStateTools };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=block-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-state.js","names":[],"sources":["../../src/tools/block-state.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { deriveDataFromStorage } from \"@platforma-sdk/model\";\nimport { z } from \"zod\";\nimport type { ToolContext } from \"./types\";\nimport { summarizeOutputs } from \"./tokens\";\nimport { safeEval } from \"./sandbox\";\nimport { errorResult, textResult } from \"./types\";\n\nexport function registerBlockStateTools(server: McpServer, ctx: ToolContext): void {\n server.registerTool(\n \"get_project_overview\",\n {\n description:\n \"Get project overview with all blocks and their statuses (calculationStatus, canRun, stale, errors, upstreams/downstreams)\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID (must be opened)\"),\n },\n },\n async ({ projectId }) => {\n const project = await ctx.getOpenedProject(projectId);\n const overview = await project.overview.getValue();\n if (!overview) return errorResult(\"Project overview not available yet.\");\n return textResult({\n label: overview.meta.label,\n blocks: overview.blocks.map((b) => ({\n id: b.id,\n title: b.title ?? b.label,\n calculationStatus: b.calculationStatus,\n canRun: b.canRun,\n stale: b.stale,\n inputsValid: b.inputsValid,\n outputErrors: b.outputErrors,\n upstreams: b.upstreams,\n downstreams: b.downstreams,\n })),\n });\n },\n );\n\n server.registerTool(\n \"get_block_state\",\n {\n description:\n \"Get block state. Returns block args (data) and a concise output summary with token estimates by default. \" +\n \"Use `transform` to extract specific data server-side without loading full outputs into context.\\n\\n\" +\n \"Default: returns `{ data, outputs: [{ key, ok, hasValue, tokensEstimate }] }`\\n\\n\" +\n \"Transform examples:\\n\" +\n \"- `outputs.logs?.value` — get one specific output value\\n\" +\n \"- `data` — get only block args\\n\" +\n \"- `({ preset: outputs.preset?.value, qc: outputs.qc?.value })` — get specific outputs\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID\"),\n transform: z\n .string()\n .optional()\n .describe(\n \"JS expression evaluated server-side against full block state. \" +\n \"Available variables: `data` (block args), `outputs` (raw outputs object). \" +\n \"Omit for default concise summary.\",\n ),\n transformTimeout: z\n .number()\n .optional()\n .default(5000)\n .describe(\"Timeout in ms for transform evaluation (default 5000).\"),\n },\n },\n async ({ projectId, blockId, transform, transformTimeout }) => {\n const project = await ctx.getOpenedProject(projectId);\n const state = await project.getBlockState(blockId).getValue();\n const data = deriveDataFromStorage(state.blockStorage);\n if (transform) {\n try {\n const result = await safeEval(\n transform,\n { data, outputs: state.outputs },\n transformTimeout,\n );\n return textResult(result);\n } catch (e: unknown) {\n return errorResult(\n `Transform failed: ${e instanceof Error ? e.message : String(e)}`,\n \"Check your JS expression syntax. Available variables: data, outputs.\",\n );\n }\n }\n return textResult({\n data,\n outputs: summarizeOutputs(state.outputs as Record<string, unknown> | undefined),\n });\n },\n );\n\n server.registerTool(\n \"set_block_data\",\n {\n description: \"Set the user-facing data of a block (triggers args derivation and staging)\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID\"),\n data: z.record(z.unknown()).describe(\"Block data object\"),\n },\n },\n async ({ projectId, blockId, data }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.mutateBlockStorage(\n blockId,\n { operation: \"update-block-data\", value: data },\n ctx.getAuthorMarker(),\n );\n return textResult({ ok: true });\n },\n );\n}\n"],"mappings":";;;;;;AAQA,SAAgB,wBAAwB,QAAmB,KAAwB;AACjF,QAAO,aACL,wBACA;EACE,aACE;EACF,aAAa,EACX,WAAW,EAAE,QAAQ,CAAC,SAAS,8BAA8B,EAC9D;EACF,EACD,OAAO,EAAE,gBAAgB;EAEvB,MAAM,WAAW,OADD,MAAM,IAAI,iBAAiB,UAAU,EACtB,SAAS,UAAU;AAClD,MAAI,CAAC,SAAU,QAAO,YAAY,sCAAsC;AACxE,SAAO,WAAW;GAChB,OAAO,SAAS,KAAK;GACrB,QAAQ,SAAS,OAAO,KAAK,OAAO;IAClC,IAAI,EAAE;IACN,OAAO,EAAE,SAAS,EAAE;IACpB,mBAAmB,EAAE;IACrB,QAAQ,EAAE;IACV,OAAO,EAAE;IACT,aAAa,EAAE;IACf,cAAc,EAAE;IAChB,WAAW,EAAE;IACb,aAAa,EAAE;IAChB,EAAE;GACJ,CAAC;GAEL;AAED,QAAO,aACL,mBACA;EACE,aACE;EAOF,aAAa;GACX,WAAW,EAAE,QAAQ,CAAC,SAAS,aAAa;GAC5C,SAAS,EAAE,QAAQ,CAAC,SAAS,WAAW;GACxC,WAAW,EACR,QAAQ,CACR,UAAU,CACV,SACC,4KAGD;GACH,kBAAkB,EACf,QAAQ,CACR,UAAU,CACV,QAAQ,IAAK,CACb,SAAS,yDAAyD;GACtE;EACF,EACD,OAAO,EAAE,WAAW,SAAS,WAAW,uBAAuB;EAE7D,MAAM,QAAQ,OADE,MAAM,IAAI,iBAAiB,UAAU,EACzB,cAAc,QAAQ,CAAC,UAAU;EAC7D,MAAM,OAAO,sBAAsB,MAAM,aAAa;AACtD,MAAI,UACF,KAAI;AAMF,UAAO,WALQ,MAAM,SACnB,WACA;IAAE;IAAM,SAAS,MAAM;IAAS,EAChC,iBACD,CACwB;WAClB,GAAY;AACnB,UAAO,YACL,qBAAqB,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,IAC/D,uEACD;;AAGL,SAAO,WAAW;GAChB;GACA,SAAS,iBAAiB,MAAM,QAA+C;GAChF,CAAC;GAEL;AAED,QAAO,aACL,kBACA;EACE,aAAa;EACb,aAAa;GACX,WAAW,EAAE,QAAQ,CAAC,SAAS,aAAa;GAC5C,SAAS,EAAE,QAAQ,CAAC,SAAS,WAAW;GACxC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,oBAAoB;GAC1D;EACF,EACD,OAAO,EAAE,WAAW,SAAS,WAAW;AAEtC,SADgB,MAAM,IAAI,iBAAiB,UAAU,EACvC,mBACZ,SACA;GAAE,WAAW;GAAqB,OAAO;GAAM,EAC/C,IAAI,iBAAiB,CACtB;AACD,SAAO,WAAW,EAAE,IAAI,MAAM,CAAC;GAElC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const require_types = require("./types.cjs");
|
|
2
|
+
let zod = require("zod");
|
|
3
|
+
//#region src/tools/blocks.ts
|
|
4
|
+
function registerBlockTools(server, ctx) {
|
|
5
|
+
server.registerTool("add_block", {
|
|
6
|
+
description: "Add a block to an opened project. Spec can be from-registry-v2 (for published blocks) or dev-v2 (for local dev blocks).",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
projectId: zod.z.string().describe("Project ID (must be opened)"),
|
|
9
|
+
label: zod.z.string().describe("Block label"),
|
|
10
|
+
spec: zod.z.union([zod.z.object({
|
|
11
|
+
type: zod.z.literal("from-registry-v2"),
|
|
12
|
+
registryUrl: zod.z.string().describe("Registry URL"),
|
|
13
|
+
id: zod.z.object({
|
|
14
|
+
organization: zod.z.string(),
|
|
15
|
+
name: zod.z.string(),
|
|
16
|
+
version: zod.z.string()
|
|
17
|
+
})
|
|
18
|
+
}), zod.z.object({
|
|
19
|
+
type: zod.z.literal("dev-v2"),
|
|
20
|
+
folder: zod.z.string().describe("Path to block folder")
|
|
21
|
+
})]).describe("Block pack specification")
|
|
22
|
+
}
|
|
23
|
+
}, async ({ projectId, label, spec }) => {
|
|
24
|
+
return require_types.textResult({ blockId: await (await ctx.getOpenedProject(projectId)).addBlock(label, spec, void 0, ctx.getAuthorMarker()) });
|
|
25
|
+
});
|
|
26
|
+
server.registerTool("update_block", {
|
|
27
|
+
description: "Update an existing block's pack (reload from registry or dev folder). Use after rebuilding a dev block to pick up changes without removing/re-adding it.",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
projectId: zod.z.string().describe("Project ID (must be opened)"),
|
|
30
|
+
blockId: zod.z.string().describe("Block ID to update"),
|
|
31
|
+
spec: zod.z.union([zod.z.object({
|
|
32
|
+
type: zod.z.literal("from-registry-v2"),
|
|
33
|
+
registryUrl: zod.z.string().describe("Registry URL"),
|
|
34
|
+
id: zod.z.object({
|
|
35
|
+
organization: zod.z.string(),
|
|
36
|
+
name: zod.z.string(),
|
|
37
|
+
version: zod.z.string()
|
|
38
|
+
})
|
|
39
|
+
}), zod.z.object({
|
|
40
|
+
type: zod.z.literal("dev-v2"),
|
|
41
|
+
folder: zod.z.string().describe("Path to block folder")
|
|
42
|
+
})]).describe("Block pack specification"),
|
|
43
|
+
resetArgs: zod.z.boolean().optional().describe("Reset block arguments to initial values (default: false)")
|
|
44
|
+
}
|
|
45
|
+
}, async ({ projectId, blockId, spec, resetArgs }) => {
|
|
46
|
+
await (await ctx.getOpenedProject(projectId)).updateBlockPack(blockId, spec, resetArgs ?? false, ctx.getAuthorMarker());
|
|
47
|
+
return require_types.textResult({ ok: true });
|
|
48
|
+
});
|
|
49
|
+
server.registerTool("remove_block", {
|
|
50
|
+
description: "Remove a block from an opened project",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
53
|
+
blockId: zod.z.string().describe("Block ID to remove")
|
|
54
|
+
}
|
|
55
|
+
}, async ({ projectId, blockId }) => {
|
|
56
|
+
await (await ctx.getOpenedProject(projectId)).deleteBlock(blockId, ctx.getAuthorMarker());
|
|
57
|
+
return require_types.textResult({ ok: true });
|
|
58
|
+
});
|
|
59
|
+
server.registerTool("run_block", {
|
|
60
|
+
description: "Run a block. Stale upstream blocks are started automatically.",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
63
|
+
blockId: zod.z.string().describe("Block ID to run")
|
|
64
|
+
}
|
|
65
|
+
}, async ({ projectId, blockId }) => {
|
|
66
|
+
await (await ctx.getOpenedProject(projectId)).runBlock(blockId);
|
|
67
|
+
return require_types.textResult({ ok: true });
|
|
68
|
+
});
|
|
69
|
+
server.registerTool("stop_block", {
|
|
70
|
+
description: "Stop a running block",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
73
|
+
blockId: zod.z.string().describe("Block ID to stop")
|
|
74
|
+
}
|
|
75
|
+
}, async ({ projectId, blockId }) => {
|
|
76
|
+
await (await ctx.getOpenedProject(projectId)).stopBlock(blockId);
|
|
77
|
+
return require_types.textResult({ ok: true });
|
|
78
|
+
});
|
|
79
|
+
server.registerTool("reorder_blocks", {
|
|
80
|
+
description: "Reorder blocks in a project. Must provide ALL block IDs in the desired order.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
83
|
+
blockIds: zod.z.array(zod.z.string()).describe("All block IDs in the desired order")
|
|
84
|
+
}
|
|
85
|
+
}, async ({ projectId, blockIds }) => {
|
|
86
|
+
await (await ctx.getOpenedProject(projectId)).reorderBlocks(blockIds);
|
|
87
|
+
return require_types.textResult({ ok: true });
|
|
88
|
+
});
|
|
89
|
+
server.registerTool("list_available_blocks", {
|
|
90
|
+
description: "List available blocks from configured registries. Optional query to filter by name.",
|
|
91
|
+
inputSchema: { query: zod.z.string().optional().describe("Filter blocks by name (case-insensitive substring match)") }
|
|
92
|
+
}, async ({ query }) => {
|
|
93
|
+
if (!ctx.callbacks.listAvailableBlocks) return require_types.errorResult("Block registry is not available.", "This usually means the desktop app encounters a problem connecting block registry. Check Settings > Override Main Registry find which registry really in use and check connection.");
|
|
94
|
+
return require_types.textResult(await ctx.callbacks.listAvailableBlocks(query));
|
|
95
|
+
});
|
|
96
|
+
server.registerTool("get_block_info", {
|
|
97
|
+
description: "Get detailed info about a specific block package from the registry. Use list_available_blocks first to find the block.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
registryUrl: zod.z.string().describe("Registry URL (from list_available_blocks)"),
|
|
100
|
+
organization: zod.z.string().describe("Organization name"),
|
|
101
|
+
name: zod.z.string().describe("Block package name"),
|
|
102
|
+
version: zod.z.string().describe("Block version")
|
|
103
|
+
}
|
|
104
|
+
}, async ({ registryUrl, organization, name, version }) => {
|
|
105
|
+
if (!ctx.callbacks.getBlockInfo) return require_types.errorResult("Block info is not available in this environment.", "Maybe the name of the block was written incerrectly. Use list_available_blocks to browse blocks instead. Or ask user to check \"Additional Registries\" in Settings panel");
|
|
106
|
+
return require_types.textResult(await ctx.callbacks.getBlockInfo(registryUrl, organization, name, version));
|
|
107
|
+
});
|
|
108
|
+
server.registerTool("select_block", {
|
|
109
|
+
description: "Navigate the desktop UI to show a specific block's interface",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
projectId: zod.z.string().describe("Project ID"),
|
|
112
|
+
blockId: zod.z.string().describe("Block ID to display")
|
|
113
|
+
}
|
|
114
|
+
}, async ({ projectId, blockId }) => {
|
|
115
|
+
if (!ctx.callbacks.selectBlock) return require_types.errorResult("Failed to select the block.", "This feature requires server connected and open project. Use get_connection_status and list_projects to check. If there are no connection, use list_connections and ask user which should be used.");
|
|
116
|
+
await ctx.callbacks.selectBlock(projectId, blockId);
|
|
117
|
+
return require_types.textResult({ ok: true });
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
exports.registerBlockTools = registerBlockTools;
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=blocks.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.cjs","names":["z","textResult","errorResult"],"sources":["../../src/tools/blocks.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlockPackSpecAny } from \"@milaboratories/pl-middle-layer\";\nimport { z } from \"zod\";\nimport type { ToolContext } from \"./types\";\nimport { errorResult, textResult } from \"./types\";\n\nexport function registerBlockTools(server: McpServer, ctx: ToolContext): void {\n server.registerTool(\n \"add_block\",\n {\n description:\n \"Add a block to an opened project. Spec can be from-registry-v2 (for published blocks) or dev-v2 (for local dev blocks).\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID (must be opened)\"),\n label: z.string().describe(\"Block label\"),\n spec: z\n .union([\n z.object({\n type: z.literal(\"from-registry-v2\"),\n registryUrl: z.string().describe(\"Registry URL\"),\n id: z.object({\n organization: z.string(),\n name: z.string(),\n version: z.string(),\n }),\n }),\n z.object({\n type: z.literal(\"dev-v2\"),\n folder: z.string().describe(\"Path to block folder\"),\n }),\n ])\n .describe(\"Block pack specification\"),\n },\n },\n async ({ projectId, label, spec }) => {\n const project = await ctx.getOpenedProject(projectId);\n const blockId = await project.addBlock(\n label,\n spec as BlockPackSpecAny,\n undefined,\n ctx.getAuthorMarker(),\n );\n return textResult({ blockId });\n },\n );\n\n server.registerTool(\n \"update_block\",\n {\n description:\n \"Update an existing block's pack (reload from registry or dev folder). Use after rebuilding a dev block to pick up changes without removing/re-adding it.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID (must be opened)\"),\n blockId: z.string().describe(\"Block ID to update\"),\n spec: z\n .union([\n z.object({\n type: z.literal(\"from-registry-v2\"),\n registryUrl: z.string().describe(\"Registry URL\"),\n id: z.object({\n organization: z.string(),\n name: z.string(),\n version: z.string(),\n }),\n }),\n z.object({\n type: z.literal(\"dev-v2\"),\n folder: z.string().describe(\"Path to block folder\"),\n }),\n ])\n .describe(\"Block pack specification\"),\n resetArgs: z\n .boolean()\n .optional()\n .describe(\"Reset block arguments to initial values (default: false)\"),\n },\n },\n async ({ projectId, blockId, spec, resetArgs }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.updateBlockPack(\n blockId,\n spec as BlockPackSpecAny,\n resetArgs ?? false,\n ctx.getAuthorMarker(),\n );\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"remove_block\",\n {\n description: \"Remove a block from an opened project\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to remove\"),\n },\n },\n async ({ projectId, blockId }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.deleteBlock(blockId, ctx.getAuthorMarker());\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"run_block\",\n {\n description: \"Run a block. Stale upstream blocks are started automatically.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to run\"),\n },\n },\n async ({ projectId, blockId }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.runBlock(blockId);\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"stop_block\",\n {\n description: \"Stop a running block\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to stop\"),\n },\n },\n async ({ projectId, blockId }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.stopBlock(blockId);\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"reorder_blocks\",\n {\n description: \"Reorder blocks in a project. Must provide ALL block IDs in the desired order.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockIds: z.array(z.string()).describe(\"All block IDs in the desired order\"),\n },\n },\n async ({ projectId, blockIds }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.reorderBlocks(blockIds);\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"list_available_blocks\",\n {\n description:\n \"List available blocks from configured registries. Optional query to filter by name.\",\n inputSchema: {\n query: z\n .string()\n .optional()\n .describe(\"Filter blocks by name (case-insensitive substring match)\"),\n },\n },\n async ({ query }) => {\n if (!ctx.callbacks.listAvailableBlocks) {\n return errorResult(\n \"Block registry is not available.\",\n \"This usually means the desktop app encounters a problem connecting block registry. Check Settings > Override Main Registry find which registry really in use and check connection.\",\n );\n }\n const blocks = await ctx.callbacks.listAvailableBlocks(query);\n return textResult(blocks);\n },\n );\n\n server.registerTool(\n \"get_block_info\",\n {\n description:\n \"Get detailed info about a specific block package from the registry. Use list_available_blocks first to find the block.\",\n inputSchema: {\n registryUrl: z.string().describe(\"Registry URL (from list_available_blocks)\"),\n organization: z.string().describe(\"Organization name\"),\n name: z.string().describe(\"Block package name\"),\n version: z.string().describe(\"Block version\"),\n },\n },\n async ({ registryUrl, organization, name, version }) => {\n if (!ctx.callbacks.getBlockInfo) {\n return errorResult(\n \"Block info is not available in this environment.\",\n 'Maybe the name of the block was written incerrectly. Use list_available_blocks to browse blocks instead. Or ask user to check \"Additional Registries\" in Settings panel',\n );\n }\n const info = await ctx.callbacks.getBlockInfo(registryUrl, organization, name, version);\n return textResult(info);\n },\n );\n\n server.registerTool(\n \"select_block\",\n {\n description: \"Navigate the desktop UI to show a specific block's interface\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to display\"),\n },\n },\n async ({ projectId, blockId }) => {\n if (!ctx.callbacks.selectBlock) {\n return errorResult(\n \"Failed to select the block.\",\n \"This feature requires server connected and open project. Use get_connection_status and list_projects to check. If there are no connection, use list_connections and ask user which should be used.\",\n );\n }\n await ctx.callbacks.selectBlock(projectId, blockId);\n return textResult({ ok: true });\n },\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { errorResult, textResult } from "./types.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/tools/blocks.ts
|
|
4
|
+
function registerBlockTools(server, ctx) {
|
|
5
|
+
server.registerTool("add_block", {
|
|
6
|
+
description: "Add a block to an opened project. Spec can be from-registry-v2 (for published blocks) or dev-v2 (for local dev blocks).",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
projectId: z.string().describe("Project ID (must be opened)"),
|
|
9
|
+
label: z.string().describe("Block label"),
|
|
10
|
+
spec: z.union([z.object({
|
|
11
|
+
type: z.literal("from-registry-v2"),
|
|
12
|
+
registryUrl: z.string().describe("Registry URL"),
|
|
13
|
+
id: z.object({
|
|
14
|
+
organization: z.string(),
|
|
15
|
+
name: z.string(),
|
|
16
|
+
version: z.string()
|
|
17
|
+
})
|
|
18
|
+
}), z.object({
|
|
19
|
+
type: z.literal("dev-v2"),
|
|
20
|
+
folder: z.string().describe("Path to block folder")
|
|
21
|
+
})]).describe("Block pack specification")
|
|
22
|
+
}
|
|
23
|
+
}, async ({ projectId, label, spec }) => {
|
|
24
|
+
return textResult({ blockId: await (await ctx.getOpenedProject(projectId)).addBlock(label, spec, void 0, ctx.getAuthorMarker()) });
|
|
25
|
+
});
|
|
26
|
+
server.registerTool("update_block", {
|
|
27
|
+
description: "Update an existing block's pack (reload from registry or dev folder). Use after rebuilding a dev block to pick up changes without removing/re-adding it.",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
projectId: z.string().describe("Project ID (must be opened)"),
|
|
30
|
+
blockId: z.string().describe("Block ID to update"),
|
|
31
|
+
spec: z.union([z.object({
|
|
32
|
+
type: z.literal("from-registry-v2"),
|
|
33
|
+
registryUrl: z.string().describe("Registry URL"),
|
|
34
|
+
id: z.object({
|
|
35
|
+
organization: z.string(),
|
|
36
|
+
name: z.string(),
|
|
37
|
+
version: z.string()
|
|
38
|
+
})
|
|
39
|
+
}), z.object({
|
|
40
|
+
type: z.literal("dev-v2"),
|
|
41
|
+
folder: z.string().describe("Path to block folder")
|
|
42
|
+
})]).describe("Block pack specification"),
|
|
43
|
+
resetArgs: z.boolean().optional().describe("Reset block arguments to initial values (default: false)")
|
|
44
|
+
}
|
|
45
|
+
}, async ({ projectId, blockId, spec, resetArgs }) => {
|
|
46
|
+
await (await ctx.getOpenedProject(projectId)).updateBlockPack(blockId, spec, resetArgs ?? false, ctx.getAuthorMarker());
|
|
47
|
+
return textResult({ ok: true });
|
|
48
|
+
});
|
|
49
|
+
server.registerTool("remove_block", {
|
|
50
|
+
description: "Remove a block from an opened project",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
projectId: z.string().describe("Project ID"),
|
|
53
|
+
blockId: z.string().describe("Block ID to remove")
|
|
54
|
+
}
|
|
55
|
+
}, async ({ projectId, blockId }) => {
|
|
56
|
+
await (await ctx.getOpenedProject(projectId)).deleteBlock(blockId, ctx.getAuthorMarker());
|
|
57
|
+
return textResult({ ok: true });
|
|
58
|
+
});
|
|
59
|
+
server.registerTool("run_block", {
|
|
60
|
+
description: "Run a block. Stale upstream blocks are started automatically.",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
projectId: z.string().describe("Project ID"),
|
|
63
|
+
blockId: z.string().describe("Block ID to run")
|
|
64
|
+
}
|
|
65
|
+
}, async ({ projectId, blockId }) => {
|
|
66
|
+
await (await ctx.getOpenedProject(projectId)).runBlock(blockId);
|
|
67
|
+
return textResult({ ok: true });
|
|
68
|
+
});
|
|
69
|
+
server.registerTool("stop_block", {
|
|
70
|
+
description: "Stop a running block",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
projectId: z.string().describe("Project ID"),
|
|
73
|
+
blockId: z.string().describe("Block ID to stop")
|
|
74
|
+
}
|
|
75
|
+
}, async ({ projectId, blockId }) => {
|
|
76
|
+
await (await ctx.getOpenedProject(projectId)).stopBlock(blockId);
|
|
77
|
+
return textResult({ ok: true });
|
|
78
|
+
});
|
|
79
|
+
server.registerTool("reorder_blocks", {
|
|
80
|
+
description: "Reorder blocks in a project. Must provide ALL block IDs in the desired order.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
projectId: z.string().describe("Project ID"),
|
|
83
|
+
blockIds: z.array(z.string()).describe("All block IDs in the desired order")
|
|
84
|
+
}
|
|
85
|
+
}, async ({ projectId, blockIds }) => {
|
|
86
|
+
await (await ctx.getOpenedProject(projectId)).reorderBlocks(blockIds);
|
|
87
|
+
return textResult({ ok: true });
|
|
88
|
+
});
|
|
89
|
+
server.registerTool("list_available_blocks", {
|
|
90
|
+
description: "List available blocks from configured registries. Optional query to filter by name.",
|
|
91
|
+
inputSchema: { query: z.string().optional().describe("Filter blocks by name (case-insensitive substring match)") }
|
|
92
|
+
}, async ({ query }) => {
|
|
93
|
+
if (!ctx.callbacks.listAvailableBlocks) return errorResult("Block registry is not available.", "This usually means the desktop app encounters a problem connecting block registry. Check Settings > Override Main Registry find which registry really in use and check connection.");
|
|
94
|
+
return textResult(await ctx.callbacks.listAvailableBlocks(query));
|
|
95
|
+
});
|
|
96
|
+
server.registerTool("get_block_info", {
|
|
97
|
+
description: "Get detailed info about a specific block package from the registry. Use list_available_blocks first to find the block.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
registryUrl: z.string().describe("Registry URL (from list_available_blocks)"),
|
|
100
|
+
organization: z.string().describe("Organization name"),
|
|
101
|
+
name: z.string().describe("Block package name"),
|
|
102
|
+
version: z.string().describe("Block version")
|
|
103
|
+
}
|
|
104
|
+
}, async ({ registryUrl, organization, name, version }) => {
|
|
105
|
+
if (!ctx.callbacks.getBlockInfo) return errorResult("Block info is not available in this environment.", "Maybe the name of the block was written incerrectly. Use list_available_blocks to browse blocks instead. Or ask user to check \"Additional Registries\" in Settings panel");
|
|
106
|
+
return textResult(await ctx.callbacks.getBlockInfo(registryUrl, organization, name, version));
|
|
107
|
+
});
|
|
108
|
+
server.registerTool("select_block", {
|
|
109
|
+
description: "Navigate the desktop UI to show a specific block's interface",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
projectId: z.string().describe("Project ID"),
|
|
112
|
+
blockId: z.string().describe("Block ID to display")
|
|
113
|
+
}
|
|
114
|
+
}, async ({ projectId, blockId }) => {
|
|
115
|
+
if (!ctx.callbacks.selectBlock) return errorResult("Failed to select the block.", "This feature requires server connected and open project. Use get_connection_status and list_projects to check. If there are no connection, use list_connections and ask user which should be used.");
|
|
116
|
+
await ctx.callbacks.selectBlock(projectId, blockId);
|
|
117
|
+
return textResult({ ok: true });
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { registerBlockTools };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.js","names":[],"sources":["../../src/tools/blocks.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { BlockPackSpecAny } from \"@milaboratories/pl-middle-layer\";\nimport { z } from \"zod\";\nimport type { ToolContext } from \"./types\";\nimport { errorResult, textResult } from \"./types\";\n\nexport function registerBlockTools(server: McpServer, ctx: ToolContext): void {\n server.registerTool(\n \"add_block\",\n {\n description:\n \"Add a block to an opened project. Spec can be from-registry-v2 (for published blocks) or dev-v2 (for local dev blocks).\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID (must be opened)\"),\n label: z.string().describe(\"Block label\"),\n spec: z\n .union([\n z.object({\n type: z.literal(\"from-registry-v2\"),\n registryUrl: z.string().describe(\"Registry URL\"),\n id: z.object({\n organization: z.string(),\n name: z.string(),\n version: z.string(),\n }),\n }),\n z.object({\n type: z.literal(\"dev-v2\"),\n folder: z.string().describe(\"Path to block folder\"),\n }),\n ])\n .describe(\"Block pack specification\"),\n },\n },\n async ({ projectId, label, spec }) => {\n const project = await ctx.getOpenedProject(projectId);\n const blockId = await project.addBlock(\n label,\n spec as BlockPackSpecAny,\n undefined,\n ctx.getAuthorMarker(),\n );\n return textResult({ blockId });\n },\n );\n\n server.registerTool(\n \"update_block\",\n {\n description:\n \"Update an existing block's pack (reload from registry or dev folder). Use after rebuilding a dev block to pick up changes without removing/re-adding it.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID (must be opened)\"),\n blockId: z.string().describe(\"Block ID to update\"),\n spec: z\n .union([\n z.object({\n type: z.literal(\"from-registry-v2\"),\n registryUrl: z.string().describe(\"Registry URL\"),\n id: z.object({\n organization: z.string(),\n name: z.string(),\n version: z.string(),\n }),\n }),\n z.object({\n type: z.literal(\"dev-v2\"),\n folder: z.string().describe(\"Path to block folder\"),\n }),\n ])\n .describe(\"Block pack specification\"),\n resetArgs: z\n .boolean()\n .optional()\n .describe(\"Reset block arguments to initial values (default: false)\"),\n },\n },\n async ({ projectId, blockId, spec, resetArgs }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.updateBlockPack(\n blockId,\n spec as BlockPackSpecAny,\n resetArgs ?? false,\n ctx.getAuthorMarker(),\n );\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"remove_block\",\n {\n description: \"Remove a block from an opened project\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to remove\"),\n },\n },\n async ({ projectId, blockId }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.deleteBlock(blockId, ctx.getAuthorMarker());\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"run_block\",\n {\n description: \"Run a block. Stale upstream blocks are started automatically.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to run\"),\n },\n },\n async ({ projectId, blockId }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.runBlock(blockId);\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"stop_block\",\n {\n description: \"Stop a running block\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to stop\"),\n },\n },\n async ({ projectId, blockId }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.stopBlock(blockId);\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"reorder_blocks\",\n {\n description: \"Reorder blocks in a project. Must provide ALL block IDs in the desired order.\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockIds: z.array(z.string()).describe(\"All block IDs in the desired order\"),\n },\n },\n async ({ projectId, blockIds }) => {\n const project = await ctx.getOpenedProject(projectId);\n await project.reorderBlocks(blockIds);\n return textResult({ ok: true });\n },\n );\n\n server.registerTool(\n \"list_available_blocks\",\n {\n description:\n \"List available blocks from configured registries. Optional query to filter by name.\",\n inputSchema: {\n query: z\n .string()\n .optional()\n .describe(\"Filter blocks by name (case-insensitive substring match)\"),\n },\n },\n async ({ query }) => {\n if (!ctx.callbacks.listAvailableBlocks) {\n return errorResult(\n \"Block registry is not available.\",\n \"This usually means the desktop app encounters a problem connecting block registry. Check Settings > Override Main Registry find which registry really in use and check connection.\",\n );\n }\n const blocks = await ctx.callbacks.listAvailableBlocks(query);\n return textResult(blocks);\n },\n );\n\n server.registerTool(\n \"get_block_info\",\n {\n description:\n \"Get detailed info about a specific block package from the registry. Use list_available_blocks first to find the block.\",\n inputSchema: {\n registryUrl: z.string().describe(\"Registry URL (from list_available_blocks)\"),\n organization: z.string().describe(\"Organization name\"),\n name: z.string().describe(\"Block package name\"),\n version: z.string().describe(\"Block version\"),\n },\n },\n async ({ registryUrl, organization, name, version }) => {\n if (!ctx.callbacks.getBlockInfo) {\n return errorResult(\n \"Block info is not available in this environment.\",\n 'Maybe the name of the block was written incerrectly. Use list_available_blocks to browse blocks instead. Or ask user to check \"Additional Registries\" in Settings panel',\n );\n }\n const info = await ctx.callbacks.getBlockInfo(registryUrl, organization, name, version);\n return textResult(info);\n },\n );\n\n server.registerTool(\n \"select_block\",\n {\n description: \"Navigate the desktop UI to show a specific block's interface\",\n inputSchema: {\n projectId: z.string().describe(\"Project ID\"),\n blockId: z.string().describe(\"Block ID to display\"),\n },\n },\n async ({ projectId, blockId }) => {\n if (!ctx.callbacks.selectBlock) {\n return errorResult(\n \"Failed to select the block.\",\n \"This feature requires server connected and open project. Use get_connection_status and list_projects to check. If there are no connection, use list_connections and ask user which should be used.\",\n );\n }\n await ctx.callbacks.selectBlock(projectId, blockId);\n return textResult({ ok: true });\n },\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const require_types = require("./types.cjs");
|
|
2
|
+
let zod = require("zod");
|
|
3
|
+
//#region src/tools/connection.ts
|
|
4
|
+
function registerConnectionTools(server, ctx) {
|
|
5
|
+
server.registerTool("get_connection_status", { description: "Get current server connection status" }, async () => {
|
|
6
|
+
if (!ctx.callbacks.getConnectionStatus) return require_types.textResult({ connected: !!ctx.getMl() });
|
|
7
|
+
return require_types.textResult(await ctx.callbacks.getConnectionStatus());
|
|
8
|
+
});
|
|
9
|
+
server.registerTool("list_connections", { description: "List saved server connections" }, async () => {
|
|
10
|
+
if (!ctx.callbacks.listConnections) return require_types.errorResult("Connection management is not available.", "The desktop app integration may not support this feature.");
|
|
11
|
+
return require_types.textResult(await ctx.callbacks.listConnections());
|
|
12
|
+
});
|
|
13
|
+
server.registerTool("connect_to_server", {
|
|
14
|
+
description: "Connect to a Platforma server. Use list_connections to see saved servers.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
addr: zod.z.string().describe("Server address (e.g. https://pl6.demo2.platforma.bio:6346)"),
|
|
17
|
+
login: zod.z.string().describe("Username"),
|
|
18
|
+
password: zod.z.string().optional().describe("Password (uses saved token if omitted)")
|
|
19
|
+
}
|
|
20
|
+
}, async ({ addr, login, password }) => {
|
|
21
|
+
if (!ctx.callbacks.connectToServer) return require_types.errorResult("Failed to connect to Platforma Server.", "Check that provided URL is available and accepts connecitons.");
|
|
22
|
+
return require_types.textResult(await ctx.callbacks.connectToServer(addr, login, password));
|
|
23
|
+
});
|
|
24
|
+
server.registerTool("disconnect", { description: "Disconnect from current server" }, async () => {
|
|
25
|
+
if (!ctx.callbacks.disconnect) return require_types.errorResult("Failed to disconnect.", "More likely it's because connection is already closed. Could check it with 'get_connection_status' tool.");
|
|
26
|
+
await ctx.callbacks.disconnect();
|
|
27
|
+
return require_types.textResult({ ok: true });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
exports.registerConnectionTools = registerConnectionTools;
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=connection.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.cjs","names":["textResult","errorResult","z"],"sources":["../../src/tools/connection.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport type { ToolContext } from \"./types\";\nimport { errorResult, textResult } from \"./types\";\n\nexport function registerConnectionTools(server: McpServer, ctx: ToolContext): void {\n server.registerTool(\n \"get_connection_status\",\n { description: \"Get current server connection status\" },\n async () => {\n if (!ctx.callbacks.getConnectionStatus) {\n return textResult({ connected: !!ctx.getMl() });\n }\n return textResult(await ctx.callbacks.getConnectionStatus());\n },\n );\n\n server.registerTool(\n \"list_connections\",\n { description: \"List saved server connections\" },\n async () => {\n if (!ctx.callbacks.listConnections) {\n return errorResult(\n \"Connection management is not available.\",\n \"The desktop app integration may not support this feature.\",\n );\n }\n return textResult(await ctx.callbacks.listConnections());\n },\n );\n\n server.registerTool(\n \"connect_to_server\",\n {\n description: \"Connect to a Platforma server. Use list_connections to see saved servers.\",\n inputSchema: {\n addr: z.string().describe(\"Server address (e.g. https://pl6.demo2.platforma.bio:6346)\"),\n login: z.string().describe(\"Username\"),\n password: z.string().optional().describe(\"Password (uses saved token if omitted)\"),\n },\n },\n async ({ addr, login, password }) => {\n if (!ctx.callbacks.connectToServer) {\n return errorResult(\n \"Failed to connect to Platforma Server.\",\n \"Check that provided URL is available and accepts connecitons.\",\n );\n }\n return textResult(await ctx.callbacks.connectToServer(addr, login, password));\n },\n );\n\n server.registerTool(\"disconnect\", { description: \"Disconnect from current server\" }, async () => {\n if (!ctx.callbacks.disconnect) {\n return errorResult(\n \"Failed to disconnect.\",\n \"More likely it's because connection is already closed. Could check it with 'get_connection_status' tool.\",\n );\n }\n await ctx.callbacks.disconnect();\n return textResult({ ok: true });\n });\n}\n"],"mappings":";;;AAKA,SAAgB,wBAAwB,QAAmB,KAAwB;AACjF,QAAO,aACL,yBACA,EAAE,aAAa,wCAAwC,EACvD,YAAY;AACV,MAAI,CAAC,IAAI,UAAU,oBACjB,QAAOA,cAAAA,WAAW,EAAE,WAAW,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;AAEjD,SAAOA,cAAAA,WAAW,MAAM,IAAI,UAAU,qBAAqB,CAAC;GAE/D;AAED,QAAO,aACL,oBACA,EAAE,aAAa,iCAAiC,EAChD,YAAY;AACV,MAAI,CAAC,IAAI,UAAU,gBACjB,QAAOC,cAAAA,YACL,2CACA,4DACD;AAEH,SAAOD,cAAAA,WAAW,MAAM,IAAI,UAAU,iBAAiB,CAAC;GAE3D;AAED,QAAO,aACL,qBACA;EACE,aAAa;EACb,aAAa;GACX,MAAME,IAAAA,EAAE,QAAQ,CAAC,SAAS,6DAA6D;GACvF,OAAOA,IAAAA,EAAE,QAAQ,CAAC,SAAS,WAAW;GACtC,UAAUA,IAAAA,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,yCAAyC;GACnF;EACF,EACD,OAAO,EAAE,MAAM,OAAO,eAAe;AACnC,MAAI,CAAC,IAAI,UAAU,gBACjB,QAAOD,cAAAA,YACL,0CACA,gEACD;AAEH,SAAOD,cAAAA,WAAW,MAAM,IAAI,UAAU,gBAAgB,MAAM,OAAO,SAAS,CAAC;GAEhF;AAED,QAAO,aAAa,cAAc,EAAE,aAAa,kCAAkC,EAAE,YAAY;AAC/F,MAAI,CAAC,IAAI,UAAU,WACjB,QAAOC,cAAAA,YACL,yBACA,2GACD;AAEH,QAAM,IAAI,UAAU,YAAY;AAChC,SAAOD,cAAAA,WAAW,EAAE,IAAI,MAAM,CAAC;GAC/B"}
|