@vornrun/mcp 0.5.4 → 0.5.6
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.js +358 -9
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -415,6 +415,9 @@ function createSchema() {
|
|
|
415
415
|
project_path TEXT,
|
|
416
416
|
approved_at TEXT,
|
|
417
417
|
diagnostics TEXT,
|
|
418
|
+
output TEXT,
|
|
419
|
+
structured_output TEXT,
|
|
420
|
+
iteration INTEGER,
|
|
418
421
|
FOREIGN KEY (run_id) REFERENCES workflow_runs(id) ON DELETE CASCADE
|
|
419
422
|
);
|
|
420
423
|
|
|
@@ -894,7 +897,19 @@ function verifySchema(d) {
|
|
|
894
897
|
{
|
|
895
898
|
column: "diagnostics",
|
|
896
899
|
ddl: "ALTER TABLE workflow_run_nodes ADD COLUMN diagnostics TEXT"
|
|
897
|
-
}
|
|
900
|
+
},
|
|
901
|
+
// A step's result was held only in renderer memory. After a reload the
|
|
902
|
+
// typed verdict was gone, {{steps.<slug>.<field>}} silently fell back to
|
|
903
|
+
// raw logs, and verdictOf reported "completed" for a run whose agent had
|
|
904
|
+
// said otherwise — worst on a run parked at an approval gate, which is
|
|
905
|
+
// exactly the case that outlives a restart.
|
|
906
|
+
{ column: "output", ddl: "ALTER TABLE workflow_run_nodes ADD COLUMN output TEXT" },
|
|
907
|
+
{
|
|
908
|
+
column: "structured_output",
|
|
909
|
+
ddl: "ALTER TABLE workflow_run_nodes ADD COLUMN structured_output TEXT"
|
|
910
|
+
},
|
|
911
|
+
// Which pass of a loop produced this row.
|
|
912
|
+
{ column: "iteration", ddl: "ALTER TABLE workflow_run_nodes ADD COLUMN iteration INTEGER" }
|
|
898
913
|
],
|
|
899
914
|
tasks: [
|
|
900
915
|
{
|
|
@@ -1554,6 +1569,7 @@ function rowToWorkspace(r) {
|
|
|
1554
1569
|
};
|
|
1555
1570
|
}
|
|
1556
1571
|
function mapNodeRow(n) {
|
|
1572
|
+
const structured = n.structured_output != null ? parseStructuredOutput(n.structured_output) : void 0;
|
|
1557
1573
|
return {
|
|
1558
1574
|
nodeId: n.node_id,
|
|
1559
1575
|
status: n.status,
|
|
@@ -1568,9 +1584,20 @@ function mapNodeRow(n) {
|
|
|
1568
1584
|
...n.project_name != null && { projectName: n.project_name },
|
|
1569
1585
|
...n.project_path != null && { projectPath: n.project_path },
|
|
1570
1586
|
...n.approved_at != null && { approvedAt: n.approved_at },
|
|
1571
|
-
...n.diagnostics != null && { diagnostics: n.diagnostics }
|
|
1587
|
+
...n.diagnostics != null && { diagnostics: n.diagnostics },
|
|
1588
|
+
...n.output != null && { output: n.output },
|
|
1589
|
+
...structured !== void 0 && { structuredOutput: structured },
|
|
1590
|
+
...n.iteration != null && { iteration: n.iteration }
|
|
1572
1591
|
};
|
|
1573
1592
|
}
|
|
1593
|
+
function parseStructuredOutput(raw) {
|
|
1594
|
+
try {
|
|
1595
|
+
const parsed = JSON.parse(raw);
|
|
1596
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
1597
|
+
} catch {
|
|
1598
|
+
return void 0;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1574
1601
|
function fetchNodesByRunIds(d, runIds) {
|
|
1575
1602
|
if (runIds.length === 0) return /* @__PURE__ */ new Map();
|
|
1576
1603
|
const placeholders = runIds.map(() => "?").join(",");
|
|
@@ -2709,6 +2736,103 @@ function registerSessionTools(server) {
|
|
|
2709
2736
|
// src/tools/workflows.ts
|
|
2710
2737
|
import crypto2 from "crypto";
|
|
2711
2738
|
import { z as z5 } from "zod";
|
|
2739
|
+
|
|
2740
|
+
// src/workflow-portability.ts
|
|
2741
|
+
var PROJECT_PATH_TOKEN = "{{project.path}}";
|
|
2742
|
+
var PROJECT_NAME_TOKEN = "{{project.name}}";
|
|
2743
|
+
var PORTABLE_FORMAT_VERSION = 1;
|
|
2744
|
+
function importedWorkflowId(bundle, slug) {
|
|
2745
|
+
return `import:${bundle}:${slug}`;
|
|
2746
|
+
}
|
|
2747
|
+
function slugify(name) {
|
|
2748
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "workflow";
|
|
2749
|
+
}
|
|
2750
|
+
function portabilityBlockers(workflow) {
|
|
2751
|
+
const blockers = [];
|
|
2752
|
+
for (const node of workflow.nodes) {
|
|
2753
|
+
const config = node.config;
|
|
2754
|
+
if (node.type === "trigger" && config.triggerType === "connectorPoll") {
|
|
2755
|
+
blockers.push(`the trigger polls a connector connection, which exists only on this machine`);
|
|
2756
|
+
}
|
|
2757
|
+
if (node.type === "callConnectorAction") {
|
|
2758
|
+
blockers.push(`step "${node.label}" calls a connector action bound to a local connection`);
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
return blockers;
|
|
2762
|
+
}
|
|
2763
|
+
function toPortable(workflow, projectPath) {
|
|
2764
|
+
const slug = slugify(workflow.name);
|
|
2765
|
+
const nodes = workflow.nodes.map((node) => {
|
|
2766
|
+
const config = { ...node.config };
|
|
2767
|
+
if (node.type === "launchAgent" || node.type === "script") {
|
|
2768
|
+
for (const key of ["projectPath", "cwd", "existingWorktreePath"]) {
|
|
2769
|
+
const value = config[key];
|
|
2770
|
+
if (typeof value === "string" && value) {
|
|
2771
|
+
config[key] = replacePath(value, projectPath);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
if (typeof config.projectName === "string" && config.projectName) {
|
|
2775
|
+
config[`projectName`] = PROJECT_NAME_TOKEN;
|
|
2776
|
+
}
|
|
2777
|
+
delete config.remoteHostId;
|
|
2778
|
+
}
|
|
2779
|
+
return { ...node, config };
|
|
2780
|
+
});
|
|
2781
|
+
return {
|
|
2782
|
+
version: PORTABLE_FORMAT_VERSION,
|
|
2783
|
+
slug,
|
|
2784
|
+
name: workflow.name,
|
|
2785
|
+
...workflow.icon && { icon: workflow.icon },
|
|
2786
|
+
...workflow.iconColor && { iconColor: workflow.iconColor },
|
|
2787
|
+
...workflow.staggerDelayMs !== void 0 && { staggerDelayMs: workflow.staggerDelayMs },
|
|
2788
|
+
nodes,
|
|
2789
|
+
edges: workflow.edges
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
function normalizeForCompare(p) {
|
|
2793
|
+
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2794
|
+
}
|
|
2795
|
+
function replacePath(value, projectPath) {
|
|
2796
|
+
const v = normalizeForCompare(value);
|
|
2797
|
+
const root = normalizeForCompare(projectPath);
|
|
2798
|
+
if (v === root) return PROJECT_PATH_TOKEN;
|
|
2799
|
+
if (v.startsWith(`${root}/`)) return `${PROJECT_PATH_TOKEN}/${v.slice(root.length + 1)}`;
|
|
2800
|
+
return value;
|
|
2801
|
+
}
|
|
2802
|
+
function fromPortable(portable, bundle, project) {
|
|
2803
|
+
const nodes = portable.nodes.map((node) => {
|
|
2804
|
+
const config = { ...node.config };
|
|
2805
|
+
for (const [key, value] of Object.entries(config)) {
|
|
2806
|
+
if (typeof value !== "string") continue;
|
|
2807
|
+
config[key] = value.split(PROJECT_PATH_TOKEN).join(project.path.replace(/[/\\]+$/, "")).split(PROJECT_NAME_TOKEN).join(project.name);
|
|
2808
|
+
}
|
|
2809
|
+
return { ...node, config };
|
|
2810
|
+
});
|
|
2811
|
+
return {
|
|
2812
|
+
id: importedWorkflowId(bundle, portable.slug),
|
|
2813
|
+
name: portable.name,
|
|
2814
|
+
icon: portable.icon ?? "Zap",
|
|
2815
|
+
iconColor: portable.iconColor ?? "#6366f1",
|
|
2816
|
+
enabled: true,
|
|
2817
|
+
...portable.staggerDelayMs !== void 0 && { staggerDelayMs: portable.staggerDelayMs },
|
|
2818
|
+
nodes,
|
|
2819
|
+
edges: portable.edges
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
var MACHINE_PATH = /(^|["'\s])(\/(Users|home)\/|[A-Za-z]:[\\/]|\\\\[^\\/\s]+[\\/])/;
|
|
2823
|
+
function residualAbsolutePaths(portable) {
|
|
2824
|
+
const found = [];
|
|
2825
|
+
for (const node of portable.nodes) {
|
|
2826
|
+
for (const [key, value] of Object.entries(node.config)) {
|
|
2827
|
+
if (typeof value === "string" && MACHINE_PATH.test(value)) {
|
|
2828
|
+
found.push(`${node.id}.${key}`);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
return found;
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
// src/tools/workflows.ts
|
|
2712
2836
|
var launchAgentConfigSchema = z5.object({
|
|
2713
2837
|
agentType: z5.enum(["claude", "copilot", "codex", "opencode", "gemini"]),
|
|
2714
2838
|
projectName: V.name,
|
|
@@ -2825,7 +2949,8 @@ var nodeSchema = z5.object({
|
|
|
2825
2949
|
"condition",
|
|
2826
2950
|
"approval",
|
|
2827
2951
|
"createTaskFromItem",
|
|
2828
|
-
"callConnectorAction"
|
|
2952
|
+
"callConnectorAction",
|
|
2953
|
+
"loop"
|
|
2829
2954
|
]),
|
|
2830
2955
|
label: V.shortText,
|
|
2831
2956
|
// Referenced by typed step vars as `{{steps.<slug>.<field>}}`. Set one on any
|
|
@@ -2833,7 +2958,43 @@ var nodeSchema = z5.object({
|
|
|
2833
2958
|
slug: V.shortText.optional(),
|
|
2834
2959
|
config: z5.record(z5.string(), z5.unknown()),
|
|
2835
2960
|
position: z5.object({ x: z5.number(), y: z5.number() })
|
|
2961
|
+
}).superRefine((node, ctx) => {
|
|
2962
|
+
if (node.type !== "loop") return;
|
|
2963
|
+
const config = node.config;
|
|
2964
|
+
if (!Array.isArray(config.bodyNodeIds) || config.bodyNodeIds.length === 0) {
|
|
2965
|
+
ctx.addIssue({
|
|
2966
|
+
code: "custom",
|
|
2967
|
+
path: ["config", "bodyNodeIds"],
|
|
2968
|
+
message: `loop "${node.id}" must list at least one body step in bodyNodeIds`
|
|
2969
|
+
});
|
|
2970
|
+
}
|
|
2971
|
+
const max = config.maxIterations;
|
|
2972
|
+
if (typeof max !== "number" || !Number.isInteger(max) || max < 1 || max > MAX_LOOP_ITERATIONS) {
|
|
2973
|
+
ctx.addIssue({
|
|
2974
|
+
code: "custom",
|
|
2975
|
+
path: ["config", "maxIterations"],
|
|
2976
|
+
message: `loop "${node.id}" needs maxIterations as a whole number from 1 to ${MAX_LOOP_ITERATIONS}`
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2836
2979
|
});
|
|
2980
|
+
var MAX_LOOP_ITERATIONS = 10;
|
|
2981
|
+
function validateLoopBodies(nodes) {
|
|
2982
|
+
const ids = new Set(nodes.map((n) => n.id));
|
|
2983
|
+
const errors = [];
|
|
2984
|
+
for (const node of nodes) {
|
|
2985
|
+
if (node.type !== "loop") continue;
|
|
2986
|
+
const body = node.config.bodyNodeIds ?? [];
|
|
2987
|
+
for (const id of body) {
|
|
2988
|
+
if (!ids.has(id)) {
|
|
2989
|
+
errors.push(`loop "${node.label || node.id}" references unknown body step "${id}"`);
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
if (body.includes(node.id)) {
|
|
2993
|
+
errors.push(`loop "${node.label || node.id}" lists itself as a body step`);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
return errors;
|
|
2997
|
+
}
|
|
2837
2998
|
var edgeSchema = z5.object({
|
|
2838
2999
|
id: V.id,
|
|
2839
3000
|
source: V.id,
|
|
@@ -2978,6 +3139,15 @@ function registerWorkflowTools(server) {
|
|
|
2978
3139
|
if (args.nodes && args.edges) {
|
|
2979
3140
|
nodes = args.nodes;
|
|
2980
3141
|
edges = args.edges;
|
|
3142
|
+
const loopErrors = validateLoopBodies(
|
|
3143
|
+
nodes
|
|
3144
|
+
);
|
|
3145
|
+
if (loopErrors.length > 0) {
|
|
3146
|
+
return {
|
|
3147
|
+
content: [{ type: "text", text: `Error: ${loopErrors.join("; ")}` }],
|
|
3148
|
+
isError: true
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
2981
3151
|
} else {
|
|
2982
3152
|
const trigger = args.trigger ?? { triggerType: "manual" };
|
|
2983
3153
|
const actions = args.actions ?? [];
|
|
@@ -3029,7 +3199,18 @@ function registerWorkflowTools(server) {
|
|
|
3029
3199
|
}
|
|
3030
3200
|
const updates = {};
|
|
3031
3201
|
if (args.name !== void 0) updates.name = args.name;
|
|
3032
|
-
if (args.nodes !== void 0)
|
|
3202
|
+
if (args.nodes !== void 0) {
|
|
3203
|
+
const loopErrors = validateLoopBodies(
|
|
3204
|
+
args.nodes
|
|
3205
|
+
);
|
|
3206
|
+
if (loopErrors.length > 0) {
|
|
3207
|
+
return {
|
|
3208
|
+
content: [{ type: "text", text: `Error: ${loopErrors.join("; ")}` }],
|
|
3209
|
+
isError: true
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
updates.nodes = args.nodes;
|
|
3213
|
+
}
|
|
3033
3214
|
if (args.edges !== void 0) updates.edges = args.edges;
|
|
3034
3215
|
if (args.icon !== void 0) updates.icon = args.icon;
|
|
3035
3216
|
if (args.icon_color !== void 0) updates.iconColor = args.icon_color;
|
|
@@ -3191,6 +3372,164 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3191
3372
|
};
|
|
3192
3373
|
}
|
|
3193
3374
|
);
|
|
3375
|
+
server.tool(
|
|
3376
|
+
"export_workflow",
|
|
3377
|
+
"Export a workflow as a portable file you can commit beside the code it drives. Absolute paths become {{project.path}} and the local remote-host binding is dropped, so it runs on another machine after import. Refuses a workflow bound to a connector connection, whose id means nothing elsewhere.",
|
|
3378
|
+
{
|
|
3379
|
+
workflow_id: V.id.optional().describe("Workflow ID (from list_workflows)"),
|
|
3380
|
+
id: V.id.optional().describe("Deprecated alias for workflow_id")
|
|
3381
|
+
},
|
|
3382
|
+
async (args) => {
|
|
3383
|
+
const resolved = resolveWorkflowId(args);
|
|
3384
|
+
if ("error" in resolved) {
|
|
3385
|
+
return { content: [{ type: "text", text: `Error: ${resolved.error}` }], isError: true };
|
|
3386
|
+
}
|
|
3387
|
+
const workflow = dbListWorkflows().find((w) => w.id === resolved.id);
|
|
3388
|
+
if (!workflow) {
|
|
3389
|
+
return {
|
|
3390
|
+
content: [{ type: "text", text: `Error: workflow "${resolved.id}" not found` }],
|
|
3391
|
+
isError: true
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
const blockers = portabilityBlockers(workflow);
|
|
3395
|
+
if (blockers.length > 0) {
|
|
3396
|
+
return {
|
|
3397
|
+
content: [
|
|
3398
|
+
{
|
|
3399
|
+
type: "text",
|
|
3400
|
+
text: `Error: "${workflow.name}" cannot be exported portably because ` + blockers.join("; ") + ". Rebuild those steps without the connection, or keep this workflow local."
|
|
3401
|
+
}
|
|
3402
|
+
],
|
|
3403
|
+
isError: true
|
|
3404
|
+
};
|
|
3405
|
+
}
|
|
3406
|
+
const projects = dbListProjects();
|
|
3407
|
+
const projectName = workflow.nodes.map((n) => n.config.projectName).find((name) => typeof name === "string" && name.length > 0);
|
|
3408
|
+
const project = projects.find((p) => p.name === projectName);
|
|
3409
|
+
if (!project) {
|
|
3410
|
+
return {
|
|
3411
|
+
content: [
|
|
3412
|
+
{
|
|
3413
|
+
type: "text",
|
|
3414
|
+
text: `Error: no project named "${projectName ?? "(none)"}" is registered, so this workflow's paths cannot be made relative to anything.`
|
|
3415
|
+
}
|
|
3416
|
+
],
|
|
3417
|
+
isError: true
|
|
3418
|
+
};
|
|
3419
|
+
}
|
|
3420
|
+
const portable = toPortable(workflow, project.path);
|
|
3421
|
+
const residual = residualAbsolutePaths(portable);
|
|
3422
|
+
return {
|
|
3423
|
+
content: [
|
|
3424
|
+
{
|
|
3425
|
+
type: "text",
|
|
3426
|
+
text: JSON.stringify(portable, null, 2) + (residual.length > 0 ? `
|
|
3427
|
+
|
|
3428
|
+
Warning: these still hold a machine-specific path and will not travel: ${residual.join(", ")}` : "")
|
|
3429
|
+
}
|
|
3430
|
+
]
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
);
|
|
3434
|
+
server.tool(
|
|
3435
|
+
"import_workflow",
|
|
3436
|
+
"Import a workflow exported by export_workflow, resolving {{project.path}} and {{project.name}} against a registered project. The id is derived from the bundle and the workflow's slug, so importing the same file again updates it in place instead of creating a duplicate.",
|
|
3437
|
+
{
|
|
3438
|
+
workflow: z5.string().max(5e5).describe("The exported workflow JSON"),
|
|
3439
|
+
project_name: V.name.describe("Registered project to resolve paths against"),
|
|
3440
|
+
bundle: V.name.optional().describe("Namespace for the derived id (default: the project name)")
|
|
3441
|
+
},
|
|
3442
|
+
async (args) => {
|
|
3443
|
+
let parsed;
|
|
3444
|
+
try {
|
|
3445
|
+
parsed = JSON.parse(args.workflow);
|
|
3446
|
+
} catch (err) {
|
|
3447
|
+
return {
|
|
3448
|
+
content: [
|
|
3449
|
+
{
|
|
3450
|
+
type: "text",
|
|
3451
|
+
text: `Error: workflow is not valid JSON \u2014 ${String(err).slice(0, 200)}`
|
|
3452
|
+
}
|
|
3453
|
+
],
|
|
3454
|
+
isError: true
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
if (parsed?.version !== PORTABLE_FORMAT_VERSION) {
|
|
3458
|
+
return {
|
|
3459
|
+
content: [
|
|
3460
|
+
{
|
|
3461
|
+
type: "text",
|
|
3462
|
+
text: `Error: unsupported format version ${parsed?.version}; this build reads version ${PORTABLE_FORMAT_VERSION}`
|
|
3463
|
+
}
|
|
3464
|
+
],
|
|
3465
|
+
isError: true
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
if (!Array.isArray(parsed.nodes) || !Array.isArray(parsed.edges) || !parsed.name) {
|
|
3469
|
+
return {
|
|
3470
|
+
content: [{ type: "text", text: "Error: workflow is missing name, nodes or edges" }],
|
|
3471
|
+
isError: true
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
const project = dbListProjects().find((p) => p.name === args.project_name);
|
|
3475
|
+
if (!project) {
|
|
3476
|
+
return {
|
|
3477
|
+
content: [
|
|
3478
|
+
{
|
|
3479
|
+
type: "text",
|
|
3480
|
+
text: `Error: no project named "${args.project_name}". Create it first so its path is known.`
|
|
3481
|
+
}
|
|
3482
|
+
],
|
|
3483
|
+
isError: true
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
const loopErrors = validateLoopBodies(
|
|
3487
|
+
parsed.nodes
|
|
3488
|
+
);
|
|
3489
|
+
if (loopErrors.length > 0) {
|
|
3490
|
+
return {
|
|
3491
|
+
content: [{ type: "text", text: `Error: ${loopErrors.join("; ")}` }],
|
|
3492
|
+
isError: true
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3495
|
+
const bundle = args.bundle ?? slugify(project.name);
|
|
3496
|
+
const definition = fromPortable(
|
|
3497
|
+
{ ...parsed, slug: parsed.slug ?? slugify(parsed.name) },
|
|
3498
|
+
bundle,
|
|
3499
|
+
{
|
|
3500
|
+
name: project.name,
|
|
3501
|
+
path: project.path
|
|
3502
|
+
}
|
|
3503
|
+
);
|
|
3504
|
+
const blockers = portabilityBlockers(definition);
|
|
3505
|
+
if (blockers.length > 0) {
|
|
3506
|
+
return {
|
|
3507
|
+
content: [
|
|
3508
|
+
{
|
|
3509
|
+
type: "text",
|
|
3510
|
+
text: `Error: this workflow cannot be imported because ${blockers.join("; ")}.`
|
|
3511
|
+
}
|
|
3512
|
+
],
|
|
3513
|
+
isError: true
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3516
|
+
const existing = dbListWorkflows().find((w) => w.id === definition.id);
|
|
3517
|
+
if (existing) {
|
|
3518
|
+
dbUpdateWorkflow(definition.id, definition);
|
|
3519
|
+
} else {
|
|
3520
|
+
dbInsertWorkflow(definition);
|
|
3521
|
+
}
|
|
3522
|
+
dbSignalChange();
|
|
3523
|
+
return {
|
|
3524
|
+
content: [
|
|
3525
|
+
{
|
|
3526
|
+
type: "text",
|
|
3527
|
+
text: `${existing ? "Updated" : "Imported"} "${definition.name}" as ${definition.id}, resolved against ${project.path}`
|
|
3528
|
+
}
|
|
3529
|
+
]
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
);
|
|
3194
3533
|
}
|
|
3195
3534
|
|
|
3196
3535
|
// src/tools/config.ts
|
|
@@ -3301,6 +3640,9 @@ var failure = (message) => ({
|
|
|
3301
3640
|
content: [{ type: "text", text: `Error: ${message}` }],
|
|
3302
3641
|
isError: true
|
|
3303
3642
|
});
|
|
3643
|
+
function summarize(entry) {
|
|
3644
|
+
return { type: entry.type, label: entry.label };
|
|
3645
|
+
}
|
|
3304
3646
|
function registerConnectorTools(server) {
|
|
3305
3647
|
server.tool(
|
|
3306
3648
|
"list_connectors",
|
|
@@ -3309,7 +3651,7 @@ function registerConnectorTools(server) {
|
|
|
3309
3651
|
installable_only: z7.boolean().optional().describe("Only connectors that are not set up yet")
|
|
3310
3652
|
},
|
|
3311
3653
|
async (args) => {
|
|
3312
|
-
const [builtIns,
|
|
3654
|
+
const [builtIns, snapshot, connections, statuses] = await Promise.all([
|
|
3313
3655
|
rpcCall("connector:list"),
|
|
3314
3656
|
rpcCall("connector:catalog"),
|
|
3315
3657
|
rpcCall("connection:list", { connectorId: void 0 }),
|
|
@@ -3331,15 +3673,22 @@ function registerConnectorTools(server) {
|
|
|
3331
3673
|
...statusFor(c.id).message && { authMessage: statusFor(c.id).message }
|
|
3332
3674
|
}
|
|
3333
3675
|
})),
|
|
3334
|
-
...
|
|
3676
|
+
...snapshot.items.map((entry) => ({
|
|
3335
3677
|
id: entry.id,
|
|
3336
3678
|
name: entry.name,
|
|
3337
3679
|
source: "package",
|
|
3338
3680
|
description: entry.description,
|
|
3339
3681
|
package: entry.packageName,
|
|
3682
|
+
...entry.version && { version: entry.version },
|
|
3340
3683
|
capabilities: entry.capabilities,
|
|
3341
3684
|
connections: countFor(entry.id),
|
|
3342
|
-
...entry.auth && { auth: entry.auth }
|
|
3685
|
+
...entry.auth && { auth: entry.auth },
|
|
3686
|
+
// Generated upstream from the connector's own manifest, so an agent
|
|
3687
|
+
// can tell whether a connector is worth installing without launching
|
|
3688
|
+
// it — which for a list of twenty would be twenty npx processes.
|
|
3689
|
+
...entry.triggers && { triggers: entry.triggers.map(summarize) },
|
|
3690
|
+
...entry.actions && { actions: entry.actions.map(summarize) },
|
|
3691
|
+
...entry.env && { env: entry.env.map((e) => e.name) }
|
|
3343
3692
|
}))
|
|
3344
3693
|
];
|
|
3345
3694
|
return json(args.installable_only ? entries.filter((e) => e.connections === 0) : entries);
|
|
@@ -3417,7 +3766,7 @@ function registerConnectorTools(server) {
|
|
|
3417
3766
|
sync_interval_minutes: z7.number().int().min(1).max(1440).optional()
|
|
3418
3767
|
},
|
|
3419
3768
|
async (args) => {
|
|
3420
|
-
const catalog = await rpcCall("connector:catalog");
|
|
3769
|
+
const { items: catalog } = await rpcCall("connector:catalog");
|
|
3421
3770
|
const entry = args.connector_id ? catalog.find((c) => c.id === args.connector_id) : void 0;
|
|
3422
3771
|
if (args.connector_id && !entry) {
|
|
3423
3772
|
return failure(
|
|
@@ -3556,7 +3905,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
3556
3905
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
3557
3906
|
async function main() {
|
|
3558
3907
|
configManager.init();
|
|
3559
|
-
const version = true ? "0.5.
|
|
3908
|
+
const version = true ? "0.5.6" : createRequire(import.meta.url)("../package.json").version;
|
|
3560
3909
|
const server = createMcpServer(version);
|
|
3561
3910
|
const transport = new StdioServerTransport();
|
|
3562
3911
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vornrun/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Vorn MCP server — task management, git, and workflow tools for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"zod": "^4.4.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@vornrun/server": "0.5.
|
|
42
|
-
"@vornrun/shared": "0.5.
|
|
41
|
+
"@vornrun/server": "0.5.6",
|
|
42
|
+
"@vornrun/shared": "0.5.6",
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
44
|
"tsx": "^4.23.1",
|
|
45
45
|
"typescript": "^6.0.3"
|