@vornrun/mcp 0.5.3 → 0.5.4
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 +217 -32
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -50,28 +50,6 @@ var logger_default = log;
|
|
|
50
50
|
import { execFileSync, execFile } from "child_process";
|
|
51
51
|
import fs from "fs";
|
|
52
52
|
import path from "path";
|
|
53
|
-
function getUserShellEnv() {
|
|
54
|
-
if (process.platform === "win32") return { ...process.env };
|
|
55
|
-
try {
|
|
56
|
-
const shell = process.env.SHELL || "/bin/zsh";
|
|
57
|
-
const output = execFileSync(shell, ["-ilc", "env"], {
|
|
58
|
-
encoding: "utf-8",
|
|
59
|
-
timeout: 5e3,
|
|
60
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
61
|
-
});
|
|
62
|
-
const env = {};
|
|
63
|
-
for (const line of output.split("\n")) {
|
|
64
|
-
const idx = line.indexOf("=");
|
|
65
|
-
if (idx > 0) {
|
|
66
|
-
env[line.substring(0, idx)] = line.substring(idx + 1);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return env;
|
|
70
|
-
} catch {
|
|
71
|
-
return { ...process.env };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
var resolvedEnv = getUserShellEnv();
|
|
75
53
|
function getDefaultShell(configured) {
|
|
76
54
|
const chosen = configured?.trim();
|
|
77
55
|
if (chosen) return chosen;
|
|
@@ -95,6 +73,8 @@ function findWindowsShell() {
|
|
|
95
73
|
if (fs.existsSync(windowsPowerShell)) return windowsPowerShell;
|
|
96
74
|
return process.env.COMSPEC || "cmd.exe";
|
|
97
75
|
}
|
|
76
|
+
var STRIP_ENV_KEYS = ["CLAUDECODE"];
|
|
77
|
+
var STRIP_ENV_KEYS_UPPER = STRIP_ENV_KEYS.map((k) => k.toUpperCase());
|
|
98
78
|
|
|
99
79
|
// ../shared/src/types.ts
|
|
100
80
|
var DEFAULT_WORKSPACE = {
|
|
@@ -994,6 +974,15 @@ function loadDefaults(d) {
|
|
|
994
974
|
hasSeenOnboarding: map.hasSeenOnboarding
|
|
995
975
|
},
|
|
996
976
|
...map.reopenSessions !== void 0 && { reopenSessions: map.reopenSessions },
|
|
977
|
+
// Saving iterates over every key in defaults, but loading is this explicit
|
|
978
|
+
// list — so a key missing here round-trips to nothing and its feature is
|
|
979
|
+
// silently inert.
|
|
980
|
+
// Array-checked rather than cast: the value came from JSON.parse of a row a
|
|
981
|
+
// user can edit, and broadcasting a non-array under a string[] type would
|
|
982
|
+
// break any consumer that trusts the declaration.
|
|
983
|
+
...Array.isArray(map.envPassthrough) && {
|
|
984
|
+
envPassthrough: map.envPassthrough.filter((k) => typeof k === "string")
|
|
985
|
+
},
|
|
997
986
|
// Terminal block rendering. Default on; the key only appears once the
|
|
998
987
|
// user has toggled it, so absence means "not yet decided", not "off".
|
|
999
988
|
domBlockRendering: map.domBlockRendering ?? true,
|
|
@@ -2748,10 +2737,68 @@ var launchAgentConfigSchema = z5.object({
|
|
|
2748
2737
|
message: "outputSchema requires headless: true",
|
|
2749
2738
|
path: ["outputSchema"]
|
|
2750
2739
|
});
|
|
2740
|
+
var workflowInputDefSchema = z5.object({
|
|
2741
|
+
key: z5.string().regex(
|
|
2742
|
+
/^[A-Za-z_][A-Za-z0-9_]*$/,
|
|
2743
|
+
"key must be a valid identifier \u2014 it becomes {{inputs.<key>}}"
|
|
2744
|
+
).max(100),
|
|
2745
|
+
label: V.shortText,
|
|
2746
|
+
type: z5.enum(["text", "textarea", "number", "select", "boolean", "project", "branch"]),
|
|
2747
|
+
required: z5.boolean().optional(),
|
|
2748
|
+
defaultValue: V.shortText.optional(),
|
|
2749
|
+
options: z5.array(z5.object({ value: V.shortText, label: V.shortText })).optional(),
|
|
2750
|
+
placeholder: V.shortText.optional(),
|
|
2751
|
+
description: V.shortText.optional()
|
|
2752
|
+
}).superRefine((def, ctx) => {
|
|
2753
|
+
const options = def.options ?? [];
|
|
2754
|
+
if (def.type === "select" && options.length === 0) {
|
|
2755
|
+
ctx.addIssue({
|
|
2756
|
+
code: "custom",
|
|
2757
|
+
path: ["options"],
|
|
2758
|
+
message: `select input "${def.key}" declares no options, so the run dialog could offer nothing`
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2761
|
+
if (def.defaultValue === void 0) return;
|
|
2762
|
+
if (def.type === "number" && !Number.isFinite(Number(def.defaultValue))) {
|
|
2763
|
+
ctx.addIssue({
|
|
2764
|
+
code: "custom",
|
|
2765
|
+
path: ["defaultValue"],
|
|
2766
|
+
message: `default "${def.defaultValue}" for number input "${def.key}" is not a finite number`
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
if (def.type === "boolean" && !["true", "false"].includes(def.defaultValue)) {
|
|
2770
|
+
ctx.addIssue({
|
|
2771
|
+
code: "custom",
|
|
2772
|
+
path: ["defaultValue"],
|
|
2773
|
+
message: `default "${def.defaultValue}" for boolean input "${def.key}" must be "true" or "false"`
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
if (def.type === "select" && options.length > 0 && !options.some((o) => o.value === def.defaultValue)) {
|
|
2777
|
+
ctx.addIssue({
|
|
2778
|
+
code: "custom",
|
|
2779
|
+
path: ["defaultValue"],
|
|
2780
|
+
message: `default "${def.defaultValue}" for select input "${def.key}" is not one of its options`
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
});
|
|
2784
|
+
var workflowInputsSchema = z5.array(workflowInputDefSchema).superRefine((inputs, ctx) => {
|
|
2785
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2786
|
+
inputs.forEach((def, index) => {
|
|
2787
|
+
if (seen.has(def.key)) {
|
|
2788
|
+
ctx.addIssue({
|
|
2789
|
+
code: "custom",
|
|
2790
|
+
path: [index, "key"],
|
|
2791
|
+
message: `duplicate input key "${def.key}" \u2014 only one value can survive under {{inputs.${def.key}}}`
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
seen.add(def.key);
|
|
2795
|
+
});
|
|
2796
|
+
});
|
|
2751
2797
|
var triggerConfigSchema = z5.union([
|
|
2752
2798
|
z5.object({
|
|
2753
2799
|
triggerType: z5.literal("manual"),
|
|
2754
|
-
contextual: z5.boolean().optional()
|
|
2800
|
+
contextual: z5.boolean().optional(),
|
|
2801
|
+
inputs: workflowInputsSchema.optional()
|
|
2755
2802
|
}),
|
|
2756
2803
|
z5.object({ triggerType: z5.literal("once"), runAt: V.shortText }),
|
|
2757
2804
|
z5.object({
|
|
@@ -2827,6 +2874,75 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
2827
2874
|
}
|
|
2828
2875
|
return { nodes, edges };
|
|
2829
2876
|
}
|
|
2877
|
+
function resolveWorkflowInputs(defs, supplied) {
|
|
2878
|
+
const errors = [];
|
|
2879
|
+
const known = new Set(defs.map((d) => d.key));
|
|
2880
|
+
for (const key of Object.keys(supplied)) {
|
|
2881
|
+
if (!known.has(key)) {
|
|
2882
|
+
errors.push(
|
|
2883
|
+
`unknown input "${key}" \u2014 this workflow declares: ${Array.from(known).join(", ") || "(none)"}`
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
const values = {};
|
|
2888
|
+
for (const def of defs) {
|
|
2889
|
+
const provided = Object.prototype.hasOwnProperty.call(supplied, def.key);
|
|
2890
|
+
const raw = provided ? supplied[def.key] : def.defaultValue;
|
|
2891
|
+
if (def.type === "boolean") {
|
|
2892
|
+
if (raw === void 0) {
|
|
2893
|
+
values[def.key] = false;
|
|
2894
|
+
} else if (typeof raw === "boolean") {
|
|
2895
|
+
values[def.key] = raw;
|
|
2896
|
+
} else if (raw === "true" || raw === "false") {
|
|
2897
|
+
values[def.key] = raw === "true";
|
|
2898
|
+
} else {
|
|
2899
|
+
errors.push(`input "${def.key}" must be a boolean, got ${JSON.stringify(raw)}`);
|
|
2900
|
+
}
|
|
2901
|
+
continue;
|
|
2902
|
+
}
|
|
2903
|
+
if (raw === void 0 || typeof raw === "string" && raw.trim() === "") {
|
|
2904
|
+
if (def.required) errors.push(`missing required input "${def.key}" (${def.label})`);
|
|
2905
|
+
continue;
|
|
2906
|
+
}
|
|
2907
|
+
switch (def.type) {
|
|
2908
|
+
case "number": {
|
|
2909
|
+
if (typeof raw === "boolean") {
|
|
2910
|
+
errors.push(`input "${def.key}" must be a number, got ${JSON.stringify(raw)}`);
|
|
2911
|
+
break;
|
|
2912
|
+
}
|
|
2913
|
+
const n = typeof raw === "number" ? raw : Number(String(raw).trim());
|
|
2914
|
+
if (!Number.isFinite(n)) {
|
|
2915
|
+
errors.push(`input "${def.key}" must be a finite number, got ${JSON.stringify(raw)}`);
|
|
2916
|
+
} else {
|
|
2917
|
+
values[def.key] = n;
|
|
2918
|
+
}
|
|
2919
|
+
break;
|
|
2920
|
+
}
|
|
2921
|
+
case "select": {
|
|
2922
|
+
const allowed = (def.options ?? []).map((o) => o.value);
|
|
2923
|
+
if (allowed.length === 0) {
|
|
2924
|
+
errors.push(`input "${def.key}" is a select but declares no options`);
|
|
2925
|
+
} else if (!allowed.includes(String(raw))) {
|
|
2926
|
+
errors.push(`input "${def.key}" must be one of: ${allowed.join(", ")}`);
|
|
2927
|
+
} else {
|
|
2928
|
+
values[def.key] = String(raw);
|
|
2929
|
+
}
|
|
2930
|
+
break;
|
|
2931
|
+
}
|
|
2932
|
+
default:
|
|
2933
|
+
values[def.key] = String(raw);
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
return { values, errors };
|
|
2937
|
+
}
|
|
2938
|
+
function resolveWorkflowId(args) {
|
|
2939
|
+
const id = args.workflow_id ?? args.id;
|
|
2940
|
+
if (!id) return { error: "provide workflow_id" };
|
|
2941
|
+
if (args.workflow_id && args.id && args.workflow_id !== args.id) {
|
|
2942
|
+
return { error: "workflow_id and id disagree \u2014 pass only workflow_id" };
|
|
2943
|
+
}
|
|
2944
|
+
return { id };
|
|
2945
|
+
}
|
|
2830
2946
|
function registerWorkflowTools(server) {
|
|
2831
2947
|
server.tool(
|
|
2832
2948
|
"list_workflows",
|
|
@@ -2888,7 +3004,8 @@ function registerWorkflowTools(server) {
|
|
|
2888
3004
|
"update_workflow",
|
|
2889
3005
|
"Update a workflow's properties",
|
|
2890
3006
|
{
|
|
2891
|
-
|
|
3007
|
+
workflow_id: V.id.optional().describe("Workflow ID (from list_workflows)"),
|
|
3008
|
+
id: V.id.optional().describe("Deprecated alias for workflow_id"),
|
|
2892
3009
|
name: V.title.optional(),
|
|
2893
3010
|
nodes: z5.array(nodeSchema).optional(),
|
|
2894
3011
|
edges: z5.array(edgeSchema).optional(),
|
|
@@ -2898,11 +3015,15 @@ function registerWorkflowTools(server) {
|
|
|
2898
3015
|
stagger_delay_ms: z5.number().optional()
|
|
2899
3016
|
},
|
|
2900
3017
|
async (args) => {
|
|
3018
|
+
const resolved = resolveWorkflowId(args);
|
|
3019
|
+
if ("error" in resolved) {
|
|
3020
|
+
return { content: [{ type: "text", text: `Error: ${resolved.error}` }], isError: true };
|
|
3021
|
+
}
|
|
2901
3022
|
const workflows = dbListWorkflows();
|
|
2902
|
-
const workflow = workflows.find((w) => w.id ===
|
|
3023
|
+
const workflow = workflows.find((w) => w.id === resolved.id);
|
|
2903
3024
|
if (!workflow) {
|
|
2904
3025
|
return {
|
|
2905
|
-
content: [{ type: "text", text: `Error: workflow "${
|
|
3026
|
+
content: [{ type: "text", text: `Error: workflow "${resolved.id}" not found` }],
|
|
2906
3027
|
isError: true
|
|
2907
3028
|
};
|
|
2908
3029
|
}
|
|
@@ -2914,7 +3035,7 @@ function registerWorkflowTools(server) {
|
|
|
2914
3035
|
if (args.icon_color !== void 0) updates.iconColor = args.icon_color;
|
|
2915
3036
|
if (args.enabled !== void 0) updates.enabled = args.enabled;
|
|
2916
3037
|
if (args.stagger_delay_ms !== void 0) updates.staggerDelayMs = args.stagger_delay_ms;
|
|
2917
|
-
dbUpdateWorkflow(
|
|
3038
|
+
dbUpdateWorkflow(resolved.id, updates);
|
|
2918
3039
|
dbSignalChange();
|
|
2919
3040
|
return {
|
|
2920
3041
|
content: [{ type: "text", text: JSON.stringify({ ...workflow, ...updates }, null, 2) }]
|
|
@@ -2924,17 +3045,24 @@ function registerWorkflowTools(server) {
|
|
|
2924
3045
|
server.tool(
|
|
2925
3046
|
"delete_workflow",
|
|
2926
3047
|
"Delete a workflow",
|
|
2927
|
-
{
|
|
3048
|
+
{
|
|
3049
|
+
workflow_id: V.id.optional().describe("Workflow ID (from list_workflows)"),
|
|
3050
|
+
id: V.id.optional().describe("Deprecated alias for workflow_id")
|
|
3051
|
+
},
|
|
2928
3052
|
async (args) => {
|
|
3053
|
+
const resolved = resolveWorkflowId(args);
|
|
3054
|
+
if ("error" in resolved) {
|
|
3055
|
+
return { content: [{ type: "text", text: `Error: ${resolved.error}` }], isError: true };
|
|
3056
|
+
}
|
|
2929
3057
|
const workflows = dbListWorkflows();
|
|
2930
|
-
const workflow = workflows.find((w) => w.id ===
|
|
3058
|
+
const workflow = workflows.find((w) => w.id === resolved.id);
|
|
2931
3059
|
if (!workflow) {
|
|
2932
3060
|
return {
|
|
2933
|
-
content: [{ type: "text", text: `Error: workflow "${
|
|
3061
|
+
content: [{ type: "text", text: `Error: workflow "${resolved.id}" not found` }],
|
|
2934
3062
|
isError: true
|
|
2935
3063
|
};
|
|
2936
3064
|
}
|
|
2937
|
-
dbDeleteWorkflow(
|
|
3065
|
+
dbDeleteWorkflow(resolved.id);
|
|
2938
3066
|
dbSignalChange();
|
|
2939
3067
|
return { content: [{ type: "text", text: `Deleted workflow: ${workflow.name}` }] };
|
|
2940
3068
|
}
|
|
@@ -3006,6 +3134,63 @@ function registerWorkflowTools(server) {
|
|
|
3006
3134
|
}
|
|
3007
3135
|
}
|
|
3008
3136
|
);
|
|
3137
|
+
server.tool(
|
|
3138
|
+
"execute_workflow",
|
|
3139
|
+
"Run a workflow now, as if triggered manually. Supply values for any parameters the workflow declares (see the trigger node's inputs); declared defaults fill in anything omitted. Requires the Vorn app to be running. Returns as soon as the run is queued \u2014 poll list_workflow_runs for the outcome.",
|
|
3140
|
+
{
|
|
3141
|
+
workflow_id: V.id.describe("Workflow ID (from list_workflows)"),
|
|
3142
|
+
inputs: z5.record(z5.string(), z5.union([z5.string(), z5.number(), z5.boolean()])).optional().describe("Values for the declared parameters, keyed by input key ({{inputs.<key>}})")
|
|
3143
|
+
},
|
|
3144
|
+
async (args) => {
|
|
3145
|
+
const workflow = dbListWorkflows().find((w) => w.id === args.workflow_id);
|
|
3146
|
+
if (!workflow) {
|
|
3147
|
+
return {
|
|
3148
|
+
content: [{ type: "text", text: `Error: workflow "${args.workflow_id}" not found` }],
|
|
3149
|
+
isError: true
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
const trigger = workflow.nodes.find((n) => n.type === "trigger")?.config;
|
|
3153
|
+
const defs = trigger?.triggerType === "manual" ? trigger.inputs ?? [] : [];
|
|
3154
|
+
const supplied = args.inputs ?? {};
|
|
3155
|
+
let inputs;
|
|
3156
|
+
if (defs.length > 0) {
|
|
3157
|
+
const { values, errors } = resolveWorkflowInputs(defs, supplied);
|
|
3158
|
+
if (errors.length > 0) {
|
|
3159
|
+
return {
|
|
3160
|
+
content: [
|
|
3161
|
+
{ type: "text", text: `Error: invalid inputs
|
|
3162
|
+
- ${errors.join("\n - ")}` }
|
|
3163
|
+
],
|
|
3164
|
+
isError: true
|
|
3165
|
+
};
|
|
3166
|
+
}
|
|
3167
|
+
inputs = values;
|
|
3168
|
+
} else if (Object.keys(supplied).length > 0) {
|
|
3169
|
+
inputs = supplied;
|
|
3170
|
+
}
|
|
3171
|
+
try {
|
|
3172
|
+
await rpcCall("workflow:runManual", { workflowId: args.workflow_id, inputs });
|
|
3173
|
+
} catch (err) {
|
|
3174
|
+
return {
|
|
3175
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
3176
|
+
isError: true
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
const disabled = workflow.enabled === false ? " (workflow is disabled; manual runs still execute)" : "";
|
|
3180
|
+
const shown = inputs ? `
|
|
3181
|
+
inputs: ${JSON.stringify(inputs, null, 2)}` : "\nno inputs";
|
|
3182
|
+
return {
|
|
3183
|
+
content: [
|
|
3184
|
+
{
|
|
3185
|
+
type: "text",
|
|
3186
|
+
text: `Queued "${workflow.name}"${disabled}${shown}
|
|
3187
|
+
|
|
3188
|
+
Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
3189
|
+
}
|
|
3190
|
+
]
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
);
|
|
3009
3194
|
}
|
|
3010
3195
|
|
|
3011
3196
|
// src/tools/config.ts
|
|
@@ -3371,7 +3556,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
3371
3556
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
3372
3557
|
async function main() {
|
|
3373
3558
|
configManager.init();
|
|
3374
|
-
const version = true ? "0.5.
|
|
3559
|
+
const version = true ? "0.5.4" : createRequire(import.meta.url)("../package.json").version;
|
|
3375
3560
|
const server = createMcpServer(version);
|
|
3376
3561
|
const transport = new StdioServerTransport();
|
|
3377
3562
|
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.4",
|
|
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.4",
|
|
42
|
+
"@vornrun/shared": "0.5.4",
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
44
|
"tsx": "^4.23.1",
|
|
45
45
|
"typescript": "^6.0.3"
|