@vornrun/mcp 0.7.0-beta.7 → 0.7.0-beta.9
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 +205 -88
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -50,8 +50,32 @@ import fs from "fs";
|
|
|
50
50
|
import path from "path";
|
|
51
51
|
|
|
52
52
|
// ../shared/src/protocol.ts
|
|
53
|
+
var BOOTSTRAP_ENV_VAR = "SECRET_VORN_BOOTSTRAP_TOKEN";
|
|
53
54
|
var LOCAL_TOKEN_FILENAME = "local-token";
|
|
54
55
|
|
|
56
|
+
// ../shared/src/types.ts
|
|
57
|
+
var DEFAULT_WORKSPACE = {
|
|
58
|
+
id: "personal",
|
|
59
|
+
name: "Personal",
|
|
60
|
+
icon: "User",
|
|
61
|
+
iconColor: "#6b7280",
|
|
62
|
+
order: 0
|
|
63
|
+
};
|
|
64
|
+
function isTerminalTaskStatus(status) {
|
|
65
|
+
return status === "done" || status === "cancelled";
|
|
66
|
+
}
|
|
67
|
+
var SDK_FILTER_KEYS = {
|
|
68
|
+
connectorId: "sdkConnectorId",
|
|
69
|
+
version: "sdkVersion",
|
|
70
|
+
icon: "sdkIcon",
|
|
71
|
+
implicit: "implicit"
|
|
72
|
+
};
|
|
73
|
+
var NEVER_BORROWED_ENV = { keys: ["CLAUDECODE"], prefixes: ["CLAUDE_CODE_"] };
|
|
74
|
+
function connectionConnectorId(connection2) {
|
|
75
|
+
const packaged = connection2.filters?.[SDK_FILTER_KEYS.connectorId];
|
|
76
|
+
return typeof packaged === "string" && packaged !== "" ? packaged : connection2.connectorId;
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
// ../server/src/process-utils.ts
|
|
56
80
|
function getDefaultShell(configured) {
|
|
57
81
|
const chosen = configured?.trim();
|
|
@@ -76,30 +100,10 @@ function findWindowsShell() {
|
|
|
76
100
|
if (fs.existsSync(windowsPowerShell)) return windowsPowerShell;
|
|
77
101
|
return process.env.COMSPEC || "cmd.exe";
|
|
78
102
|
}
|
|
79
|
-
var STRIP_ENV_KEYS =
|
|
103
|
+
var STRIP_ENV_KEYS = NEVER_BORROWED_ENV.keys;
|
|
104
|
+
var STRIP_ENV_PREFIXES = [...NEVER_BORROWED_ENV.prefixes, BOOTSTRAP_ENV_VAR];
|
|
80
105
|
var STRIP_ENV_KEYS_UPPER = STRIP_ENV_KEYS.map((k) => k.toUpperCase());
|
|
81
106
|
|
|
82
|
-
// ../shared/src/types.ts
|
|
83
|
-
var DEFAULT_WORKSPACE = {
|
|
84
|
-
id: "personal",
|
|
85
|
-
name: "Personal",
|
|
86
|
-
icon: "User",
|
|
87
|
-
iconColor: "#6b7280",
|
|
88
|
-
order: 0
|
|
89
|
-
};
|
|
90
|
-
function isTerminalTaskStatus(status) {
|
|
91
|
-
return status === "done" || status === "cancelled";
|
|
92
|
-
}
|
|
93
|
-
var SDK_FILTER_KEYS = {
|
|
94
|
-
connectorId: "sdkConnectorId",
|
|
95
|
-
version: "sdkVersion",
|
|
96
|
-
icon: "sdkIcon"
|
|
97
|
-
};
|
|
98
|
-
function connectionConnectorId(connection2) {
|
|
99
|
-
const packaged = connection2.filters?.[SDK_FILTER_KEYS.connectorId];
|
|
100
|
-
return typeof packaged === "string" && packaged !== "" ? packaged : connection2.connectorId;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
107
|
// ../server/src/default-workflows.ts
|
|
104
108
|
var DEFAULT_TASK_WORKFLOW_ID = "system:default-task-workflow";
|
|
105
109
|
function buildDefaultTaskWorkflow() {
|
|
@@ -900,6 +904,39 @@ function migrateSchema(d) {
|
|
|
900
904
|
})();
|
|
901
905
|
logger_default.info("[database] migrated schema to version 16 (shell working directory)");
|
|
902
906
|
}
|
|
907
|
+
if (version < 17) {
|
|
908
|
+
d.transaction(() => {
|
|
909
|
+
const derived = `(
|
|
910
|
+
SELECT json_extract(sc.filters, '$.sdkConnectorId')
|
|
911
|
+
FROM task_source_links tsl
|
|
912
|
+
JOIN source_connections sc ON sc.id = tsl.connection_id
|
|
913
|
+
WHERE tsl.task_id = tasks.id
|
|
914
|
+
)`;
|
|
915
|
+
d.exec(`
|
|
916
|
+
UPDATE tasks
|
|
917
|
+
SET source_connector_id = ${derived}
|
|
918
|
+
WHERE source_connector_id = 'mcp' AND ${derived} IS NOT NULL
|
|
919
|
+
`);
|
|
920
|
+
d.exec(`
|
|
921
|
+
UPDATE task_source_links
|
|
922
|
+
SET connector_id = (
|
|
923
|
+
SELECT json_extract(sc.filters, '$.sdkConnectorId')
|
|
924
|
+
FROM source_connections sc
|
|
925
|
+
WHERE sc.id = task_source_links.connection_id
|
|
926
|
+
)
|
|
927
|
+
WHERE connector_id = 'mcp'
|
|
928
|
+
AND (
|
|
929
|
+
SELECT json_extract(sc.filters, '$.sdkConnectorId')
|
|
930
|
+
FROM source_connections sc
|
|
931
|
+
WHERE sc.id = task_source_links.connection_id
|
|
932
|
+
) IS NOT NULL
|
|
933
|
+
`);
|
|
934
|
+
d.prepare(
|
|
935
|
+
"INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '17')"
|
|
936
|
+
).run();
|
|
937
|
+
})();
|
|
938
|
+
logger_default.info("[database] migrated schema to version 17 (packaged connector task ids)");
|
|
939
|
+
}
|
|
903
940
|
}
|
|
904
941
|
var REVISIONED_TABLES = [
|
|
905
942
|
"projects",
|
|
@@ -1615,7 +1652,7 @@ var configManager = new ConfigManager();
|
|
|
1615
1652
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1616
1653
|
|
|
1617
1654
|
// src/tools/tasks.ts
|
|
1618
|
-
import
|
|
1655
|
+
import crypto2 from "crypto";
|
|
1619
1656
|
import path4 from "path";
|
|
1620
1657
|
import { z as z2 } from "zod";
|
|
1621
1658
|
|
|
@@ -2012,7 +2049,7 @@ function registerTaskTools(server) {
|
|
|
2012
2049
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2013
2050
|
const status = args.status ?? "todo";
|
|
2014
2051
|
const task = {
|
|
2015
|
-
id:
|
|
2052
|
+
id: crypto2.randomUUID(),
|
|
2016
2053
|
projectName: args.project_name,
|
|
2017
2054
|
title: args.title,
|
|
2018
2055
|
description: args.description ?? "",
|
|
@@ -2712,47 +2749,87 @@ function registerSessionTools(server) {
|
|
|
2712
2749
|
}
|
|
2713
2750
|
|
|
2714
2751
|
// src/tools/workflows.ts
|
|
2715
|
-
import
|
|
2752
|
+
import crypto3 from "crypto";
|
|
2716
2753
|
import { z as z5 } from "zod";
|
|
2717
2754
|
|
|
2718
|
-
// src/workflow-portability.ts
|
|
2755
|
+
// ../shared/src/workflow-portability.ts
|
|
2719
2756
|
var PROJECT_PATH_TOKEN = "{{project.path}}";
|
|
2720
2757
|
var PROJECT_NAME_TOKEN = "{{project.name}}";
|
|
2721
2758
|
var PORTABLE_FORMAT_VERSION = 1;
|
|
2759
|
+
var HTTP_PROFILE_CONNECTOR = "http";
|
|
2722
2760
|
function importedWorkflowId(bundle, slug) {
|
|
2723
2761
|
return `import:${bundle}:${slug}`;
|
|
2724
2762
|
}
|
|
2763
|
+
function importedWorkflowIdFor(bundle, slug, name, existing) {
|
|
2764
|
+
for (let attempt = 1; ; attempt++) {
|
|
2765
|
+
const id = importedWorkflowId(bundle, attempt === 1 ? slug : `${slug}-${attempt}`);
|
|
2766
|
+
const held = existing.find((workflow) => workflow.id === id);
|
|
2767
|
+
if (!held || held.name === name) return id;
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2725
2770
|
function slugify(name) {
|
|
2726
2771
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "workflow";
|
|
2727
2772
|
}
|
|
2728
|
-
function
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2773
|
+
function connectorOf(connection2) {
|
|
2774
|
+
return connectionConnectorId({
|
|
2775
|
+
connectorId: connection2.connectorId,
|
|
2776
|
+
filters: connection2.filters ?? {}
|
|
2777
|
+
});
|
|
2778
|
+
}
|
|
2779
|
+
function boundConnectionKey(node, config) {
|
|
2780
|
+
if (node.type === "trigger" && config.triggerType === "connectorPoll") return "connectionId";
|
|
2781
|
+
if (node.type === "callConnectorAction") return "connectionId";
|
|
2782
|
+
if (node.type === "httpRequest") return "profileConnectionId";
|
|
2783
|
+
return null;
|
|
2784
|
+
}
|
|
2785
|
+
function resolveRequirement(requirement, connections) {
|
|
2786
|
+
const candidates = connections.filter(
|
|
2787
|
+
(connection2) => requirement.kind === "httpProfile" ? connectorOf(connection2) === HTTP_PROFILE_CONNECTOR : requirement.connectorId !== "" && connectorOf(connection2) === requirement.connectorId
|
|
2788
|
+
);
|
|
2789
|
+
if (candidates.length === 0) return void 0;
|
|
2790
|
+
if (requirement.name !== "") {
|
|
2791
|
+
const named = candidates.filter((connection2) => connection2.name === requirement.name);
|
|
2792
|
+
if (named.length === 1) return named[0].id;
|
|
2738
2793
|
}
|
|
2739
|
-
return
|
|
2794
|
+
return candidates.length === 1 ? candidates[0].id : void 0;
|
|
2740
2795
|
}
|
|
2741
|
-
function toPortable(workflow, projectPath) {
|
|
2796
|
+
function toPortable(workflow, projectPath, connections = []) {
|
|
2742
2797
|
const slug = slugify(workflow.name);
|
|
2798
|
+
const requires = [];
|
|
2743
2799
|
const nodes = workflow.nodes.map((node) => {
|
|
2744
2800
|
const config = { ...node.config };
|
|
2801
|
+
if (node.type === "trigger" && config.triggerType === "webhook") config.token = "";
|
|
2745
2802
|
if (node.type === "launchAgent" || node.type === "script") {
|
|
2746
|
-
for (const
|
|
2747
|
-
const value = config[
|
|
2803
|
+
for (const key2 of ["projectPath", "cwd", "existingWorktreePath"]) {
|
|
2804
|
+
const value = config[key2];
|
|
2748
2805
|
if (typeof value === "string" && value) {
|
|
2749
|
-
config[
|
|
2806
|
+
config[key2] = replacePath(value, projectPath);
|
|
2750
2807
|
}
|
|
2751
2808
|
}
|
|
2752
2809
|
if (typeof config.projectName === "string" && config.projectName) {
|
|
2753
2810
|
config[`projectName`] = PROJECT_NAME_TOKEN;
|
|
2754
2811
|
}
|
|
2755
2812
|
delete config.remoteHostId;
|
|
2813
|
+
delete config.secretsFrom;
|
|
2814
|
+
}
|
|
2815
|
+
const key = boundConnectionKey(node, config);
|
|
2816
|
+
const bound = key === null ? "" : config[key];
|
|
2817
|
+
const unbound = key !== null && key !== "profileConnectionId" && bound === "";
|
|
2818
|
+
if (key !== null && (typeof bound === "string" && bound !== "" || unbound)) {
|
|
2819
|
+
const source2 = connections.find((connection2) => connection2.id === bound);
|
|
2820
|
+
const event = config.event;
|
|
2821
|
+
const declared = config.connectorId;
|
|
2822
|
+
requires.push(
|
|
2823
|
+
key === "profileConnectionId" ? { kind: "httpProfile", nodeId: node.id, name: source2?.name ?? "" } : {
|
|
2824
|
+
kind: "connection",
|
|
2825
|
+
nodeId: node.id,
|
|
2826
|
+
connectorId: source2 ? connectorOf(source2) : typeof declared === "string" ? declared : "",
|
|
2827
|
+
name: source2?.name ?? "",
|
|
2828
|
+
...typeof event === "string" && event !== "" && { event }
|
|
2829
|
+
}
|
|
2830
|
+
);
|
|
2831
|
+
if (key === "profileConnectionId") delete config[key];
|
|
2832
|
+
else config[key] = "";
|
|
2756
2833
|
}
|
|
2757
2834
|
return { ...node, config };
|
|
2758
2835
|
});
|
|
@@ -2763,6 +2840,7 @@ function toPortable(workflow, projectPath) {
|
|
|
2763
2840
|
...workflow.icon && { icon: workflow.icon },
|
|
2764
2841
|
...workflow.iconColor && { iconColor: workflow.iconColor },
|
|
2765
2842
|
...workflow.staggerDelayMs !== void 0 && { staggerDelayMs: workflow.staggerDelayMs },
|
|
2843
|
+
...requires.length > 0 && { requires },
|
|
2766
2844
|
nodes,
|
|
2767
2845
|
edges: workflow.edges
|
|
2768
2846
|
};
|
|
@@ -2773,17 +2851,38 @@ function normalizeForCompare(p) {
|
|
|
2773
2851
|
function replacePath(value, projectPath) {
|
|
2774
2852
|
const v = normalizeForCompare(value);
|
|
2775
2853
|
const root = normalizeForCompare(projectPath);
|
|
2854
|
+
if (root === "") return value;
|
|
2776
2855
|
if (v === root) return PROJECT_PATH_TOKEN;
|
|
2777
2856
|
if (v.startsWith(`${root}/`)) return `${PROJECT_PATH_TOKEN}/${v.slice(root.length + 1)}`;
|
|
2778
2857
|
return value;
|
|
2779
2858
|
}
|
|
2780
|
-
function
|
|
2859
|
+
function unresolvedRequirements(portable, connections) {
|
|
2860
|
+
const present = new Set(portable.nodes.map((node) => node.id));
|
|
2861
|
+
return (portable.requires ?? []).filter(
|
|
2862
|
+
(requirement) => present.has(requirement.nodeId) && resolveRequirement(requirement, connections) === void 0
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
2865
|
+
function fromPortable(portable, bundle, project, connections = [], mintToken = () => crypto.randomUUID()) {
|
|
2866
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
2867
|
+
for (const requirement of portable.requires ?? []) {
|
|
2868
|
+
bindings.set(requirement.nodeId, [...bindings.get(requirement.nodeId) ?? [], requirement]);
|
|
2869
|
+
}
|
|
2781
2870
|
const nodes = portable.nodes.map((node) => {
|
|
2782
2871
|
const config = { ...node.config };
|
|
2872
|
+
delete config.secretsFrom;
|
|
2783
2873
|
for (const [key, value] of Object.entries(config)) {
|
|
2784
2874
|
if (typeof value !== "string") continue;
|
|
2785
2875
|
config[key] = value.split(PROJECT_PATH_TOKEN).join(project.path.replace(/[/\\]+$/, "")).split(PROJECT_NAME_TOKEN).join(project.name);
|
|
2786
2876
|
}
|
|
2877
|
+
for (const requirement of bindings.get(node.id) ?? []) {
|
|
2878
|
+
const resolved = resolveRequirement(requirement, connections);
|
|
2879
|
+
if (resolved === void 0) continue;
|
|
2880
|
+
if (requirement.kind === "httpProfile") config.profileConnectionId = resolved;
|
|
2881
|
+
else config.connectionId = resolved;
|
|
2882
|
+
}
|
|
2883
|
+
if (node.type === "trigger" && config.triggerType === "webhook" && !config.token) {
|
|
2884
|
+
config.token = mintToken();
|
|
2885
|
+
}
|
|
2787
2886
|
return { ...node, config };
|
|
2788
2887
|
});
|
|
2789
2888
|
return {
|
|
@@ -2791,7 +2890,9 @@ function fromPortable(portable, bundle, project) {
|
|
|
2791
2890
|
name: portable.name,
|
|
2792
2891
|
icon: portable.icon ?? "Zap",
|
|
2793
2892
|
iconColor: portable.iconColor ?? "#6366f1",
|
|
2794
|
-
|
|
2893
|
+
// A file cannot ask to be running: a dropped cron workflow would start
|
|
2894
|
+
// firing before anyone had read it. Callers restore what they had.
|
|
2895
|
+
enabled: false,
|
|
2795
2896
|
...portable.staggerDelayMs !== void 0 && { staggerDelayMs: portable.staggerDelayMs },
|
|
2796
2897
|
nodes,
|
|
2797
2898
|
edges: portable.edges
|
|
@@ -3002,7 +3103,7 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
3002
3103
|
const nodes = [];
|
|
3003
3104
|
const edges = [];
|
|
3004
3105
|
const triggerNode = {
|
|
3005
|
-
id:
|
|
3106
|
+
id: crypto3.randomUUID(),
|
|
3006
3107
|
type: "trigger",
|
|
3007
3108
|
label: trigger.triggerType === "manual" ? "Manual Trigger" : trigger.triggerType === "once" ? "Schedule (Once)" : trigger.triggerType === "recurring" ? "Schedule (Recurring)" : trigger.triggerType === "taskCreated" ? "When Task Created" : trigger.triggerType === "taskStatusChanged" ? "When Task Status Changes" : "Trigger",
|
|
3008
3109
|
config: trigger,
|
|
@@ -3013,7 +3114,7 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
3013
3114
|
const NODE_GAP = 140;
|
|
3014
3115
|
for (let i = 0; i < actions.length; i++) {
|
|
3015
3116
|
const action = actions[i];
|
|
3016
|
-
const nodeId =
|
|
3117
|
+
const nodeId = crypto3.randomUUID();
|
|
3017
3118
|
nodes.push({
|
|
3018
3119
|
id: nodeId,
|
|
3019
3120
|
type: "launchAgent",
|
|
@@ -3022,7 +3123,7 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
3022
3123
|
position: { x: 0, y: (i + 1) * NODE_GAP }
|
|
3023
3124
|
});
|
|
3024
3125
|
edges.push({
|
|
3025
|
-
id:
|
|
3126
|
+
id: crypto3.randomUUID(),
|
|
3026
3127
|
source: prevId,
|
|
3027
3128
|
target: nodeId
|
|
3028
3129
|
});
|
|
@@ -3099,6 +3200,13 @@ function resolveWorkflowId(args) {
|
|
|
3099
3200
|
}
|
|
3100
3201
|
return { id };
|
|
3101
3202
|
}
|
|
3203
|
+
async function listPortableConnections() {
|
|
3204
|
+
try {
|
|
3205
|
+
return await rpcCall("connection:list", { connectorId: void 0 });
|
|
3206
|
+
} catch {
|
|
3207
|
+
return [];
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3102
3210
|
function registerWorkflowTools(server) {
|
|
3103
3211
|
server.tool(
|
|
3104
3212
|
"list_workflows",
|
|
@@ -3151,7 +3259,7 @@ function registerWorkflowTools(server) {
|
|
|
3151
3259
|
edges = graph.edges;
|
|
3152
3260
|
}
|
|
3153
3261
|
const workflow = {
|
|
3154
|
-
id:
|
|
3262
|
+
id: crypto3.randomUUID(),
|
|
3155
3263
|
name: args.name,
|
|
3156
3264
|
icon: args.icon ?? "Zap",
|
|
3157
3265
|
iconColor: args.icon_color ?? "#6366f1",
|
|
@@ -3421,7 +3529,7 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3421
3529
|
);
|
|
3422
3530
|
server.tool(
|
|
3423
3531
|
"export_workflow",
|
|
3424
|
-
"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.
|
|
3532
|
+
"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. Connections are dropped too and recorded as requirements the importing machine rebinds by connector and name.",
|
|
3425
3533
|
{
|
|
3426
3534
|
workflow_id: V.id.optional().describe("Workflow ID (from list_workflows)"),
|
|
3427
3535
|
id: V.id.optional().describe("Deprecated alias for workflow_id")
|
|
@@ -3438,18 +3546,6 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3438
3546
|
isError: true
|
|
3439
3547
|
};
|
|
3440
3548
|
}
|
|
3441
|
-
const blockers = portabilityBlockers(workflow);
|
|
3442
|
-
if (blockers.length > 0) {
|
|
3443
|
-
return {
|
|
3444
|
-
content: [
|
|
3445
|
-
{
|
|
3446
|
-
type: "text",
|
|
3447
|
-
text: `Error: "${workflow.name}" cannot be exported portably because ` + blockers.join("; ") + ". Rebuild those steps without the connection, or keep this workflow local."
|
|
3448
|
-
}
|
|
3449
|
-
],
|
|
3450
|
-
isError: true
|
|
3451
|
-
};
|
|
3452
|
-
}
|
|
3453
3549
|
const projects = await dbListProjects();
|
|
3454
3550
|
const projectName = workflow.nodes.map((n) => n.config.projectName).find((name) => typeof name === "string" && name.length > 0);
|
|
3455
3551
|
const project = projects.find((p) => p.name === projectName);
|
|
@@ -3464,15 +3560,20 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3464
3560
|
isError: true
|
|
3465
3561
|
};
|
|
3466
3562
|
}
|
|
3467
|
-
const portable = toPortable(workflow, project.path);
|
|
3563
|
+
const portable = toPortable(workflow, project.path, await listPortableConnections());
|
|
3468
3564
|
const residual = residualAbsolutePaths(portable);
|
|
3565
|
+
const unnamed = (portable.requires ?? []).filter(
|
|
3566
|
+
(requirement) => requirement.kind === "connection" && requirement.connectorId === ""
|
|
3567
|
+
);
|
|
3469
3568
|
return {
|
|
3470
3569
|
content: [
|
|
3471
3570
|
{
|
|
3472
3571
|
type: "text",
|
|
3473
3572
|
text: JSON.stringify(portable, null, 2) + (residual.length > 0 ? `
|
|
3474
3573
|
|
|
3475
|
-
Warning: these still hold a machine-specific path and will not travel: ${residual.join(", ")}` : "")
|
|
3574
|
+
Warning: these still hold a machine-specific path and will not travel: ${residual.join(", ")}` : "") + (unnamed.length > 0 ? `
|
|
3575
|
+
|
|
3576
|
+
Warning: ${unnamed.length} step(s) point at a connection this install could not name, so an import cannot rebind them automatically.` : "")
|
|
3476
3577
|
}
|
|
3477
3578
|
]
|
|
3478
3579
|
};
|
|
@@ -3480,7 +3581,7 @@ Warning: these still hold a machine-specific path and will not travel: ${residua
|
|
|
3480
3581
|
);
|
|
3481
3582
|
server.tool(
|
|
3482
3583
|
"import_workflow",
|
|
3483
|
-
"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.",
|
|
3584
|
+
"Import a workflow exported by export_workflow, resolving {{project.path}} and {{project.name}} against a registered project. Recorded connection requirements are rebound when this machine has one unambiguous match, and reported as still to connect otherwise. 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.",
|
|
3484
3585
|
{
|
|
3485
3586
|
workflow: z5.string().max(5e5).describe("The exported workflow JSON"),
|
|
3486
3587
|
project_name: V.name.describe("Registered project to resolve paths against"),
|
|
@@ -3540,38 +3641,38 @@ Warning: these still hold a machine-specific path and will not travel: ${residua
|
|
|
3540
3641
|
};
|
|
3541
3642
|
}
|
|
3542
3643
|
const bundle = args.bundle ?? slugify(project.name);
|
|
3543
|
-
const
|
|
3544
|
-
|
|
3644
|
+
const portable = { ...parsed, slug: parsed.slug ?? slugify(parsed.name) };
|
|
3645
|
+
const connections = await listPortableConnections();
|
|
3646
|
+
const resolved = fromPortable(
|
|
3647
|
+
portable,
|
|
3545
3648
|
bundle,
|
|
3546
3649
|
{
|
|
3547
3650
|
name: project.name,
|
|
3548
3651
|
path: project.path
|
|
3549
|
-
}
|
|
3652
|
+
},
|
|
3653
|
+
connections
|
|
3550
3654
|
);
|
|
3551
|
-
const
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
type: "text",
|
|
3557
|
-
text: `Error: this workflow cannot be imported because ${blockers.join("; ")}.`
|
|
3558
|
-
}
|
|
3559
|
-
],
|
|
3560
|
-
isError: true
|
|
3561
|
-
};
|
|
3562
|
-
}
|
|
3563
|
-
const existing = (await dbListWorkflows()).find((w) => w.id === definition.id);
|
|
3655
|
+
const unresolved = unresolvedRequirements(portable, connections);
|
|
3656
|
+
const known = await dbListWorkflows();
|
|
3657
|
+
const id = importedWorkflowIdFor(bundle, portable.slug, portable.name, known);
|
|
3658
|
+
const existing = known.find((w) => w.id === id);
|
|
3659
|
+
const definition = { ...resolved, id, enabled: existing ? existing.enabled : false };
|
|
3564
3660
|
if (existing) {
|
|
3565
3661
|
await dbUpdateWorkflow(definition.id, definition);
|
|
3566
3662
|
} else {
|
|
3567
3663
|
await dbInsertWorkflow(definition);
|
|
3568
3664
|
}
|
|
3569
3665
|
dbSignalChange();
|
|
3666
|
+
const pending = unresolved.map(
|
|
3667
|
+
(requirement) => requirement.kind === "httpProfile" ? `${requirement.nodeId} needs an HTTP profile${requirement.name ? ` like "${requirement.name}"` : ""}` : `${requirement.nodeId} needs a ${requirement.connectorId || "connector"} connection${requirement.name ? ` like "${requirement.name}"` : ""}`
|
|
3668
|
+
).join("; ");
|
|
3570
3669
|
return {
|
|
3571
3670
|
content: [
|
|
3572
3671
|
{
|
|
3573
3672
|
type: "text",
|
|
3574
|
-
text: `${existing ? "Updated" : "Imported"} "${definition.name}" as ${definition.id}, resolved against ${project.path}`
|
|
3673
|
+
text: `${existing ? "Updated" : "Imported"} "${definition.name}" as ${definition.id}, resolved against ${project.path}` + (existing || definition.enabled ? "" : ". It is disabled; enable it when ready") + (pending ? `
|
|
3674
|
+
|
|
3675
|
+
Still to connect: ${pending}` : "")
|
|
3575
3676
|
}
|
|
3576
3677
|
]
|
|
3577
3678
|
};
|
|
@@ -3592,7 +3693,7 @@ function registerConfigTools(server) {
|
|
|
3592
3693
|
}
|
|
3593
3694
|
|
|
3594
3695
|
// src/tools/workspaces.ts
|
|
3595
|
-
import
|
|
3696
|
+
import crypto4 from "crypto";
|
|
3596
3697
|
import { z as z6 } from "zod";
|
|
3597
3698
|
function registerWorkspaceTools(server) {
|
|
3598
3699
|
server.tool("list_workspaces", "List all workspaces", async () => {
|
|
@@ -3611,7 +3712,7 @@ function registerWorkspaceTools(server) {
|
|
|
3611
3712
|
const existing = await dbListWorkspaces();
|
|
3612
3713
|
const maxOrder = existing.reduce((max, w) => Math.max(max, w.order), 0);
|
|
3613
3714
|
const workspace = {
|
|
3614
|
-
id:
|
|
3715
|
+
id: crypto4.randomUUID(),
|
|
3615
3716
|
name: args.name,
|
|
3616
3717
|
order: maxOrder + 1,
|
|
3617
3718
|
...args.icon && { icon: args.icon },
|
|
@@ -3802,10 +3903,13 @@ function registerConnectorTools(server) {
|
|
|
3802
3903
|
);
|
|
3803
3904
|
server.tool(
|
|
3804
3905
|
"install_connector",
|
|
3805
|
-
"Install a connector from the catalog or from an npm package, creating a connection ready to poll. Call list_connectors for catalog ids and inspect_connector_package to see which environment variables are needed. Secrets cannot be set this way \u2014 see the error it returns if the connector requires one.",
|
|
3906
|
+
"Install a connector from a pack file, from the catalog, or from an npm package, creating a connection ready to poll. Call list_connectors for catalog ids and inspect_connector_package to see which environment variables are needed. Secrets cannot be set this way \u2014 see the error it returns if the connector requires one.",
|
|
3806
3907
|
{
|
|
3807
3908
|
connector_id: V.id.optional().describe("Catalog connector id (from list_connectors). Use this or package."),
|
|
3808
3909
|
package: V.shortText.optional().describe("npm package name or launch command"),
|
|
3910
|
+
pack_path: V.shortText.optional().describe(
|
|
3911
|
+
"Path to a .vorn.tgz pack to install first. It is verified and copied to disk, and the connection then launches those files rather than resolving a package."
|
|
3912
|
+
),
|
|
3809
3913
|
name: V.title.optional().describe("Connection name (defaults to the connector name)"),
|
|
3810
3914
|
project: V.name.optional().describe("Vorn project tasks should be created in"),
|
|
3811
3915
|
trigger: V.shortText.optional().describe("Trigger type to configure (defaults to the first the connector offers)"),
|
|
@@ -3820,8 +3924,17 @@ function registerConnectorTools(server) {
|
|
|
3820
3924
|
`No connector "${args.connector_id}" in the catalog. Known: ${catalog.map((c) => c.id).join(", ") || "(none)"}. To install something not in the catalog, pass \`package\` instead.`
|
|
3821
3925
|
);
|
|
3822
3926
|
}
|
|
3823
|
-
|
|
3824
|
-
if (
|
|
3927
|
+
let installed;
|
|
3928
|
+
if (args.pack_path) {
|
|
3929
|
+
const outcome = await rpcCall("connector:installPack", {
|
|
3930
|
+
kind: "file",
|
|
3931
|
+
path: args.pack_path
|
|
3932
|
+
});
|
|
3933
|
+
if (!outcome.ok) return failure(`The pack was refused: ${outcome.error}`);
|
|
3934
|
+
installed = outcome.pack;
|
|
3935
|
+
}
|
|
3936
|
+
const target = installed ? packLaunch(installed) : entry?.launch ?? args.package;
|
|
3937
|
+
if (!target) return failure("Provide either connector_id, package, or pack_path.");
|
|
3825
3938
|
const result = await probe(target);
|
|
3826
3939
|
if (!result.ok) return failure(result.error);
|
|
3827
3940
|
const manifest = result.manifest;
|
|
@@ -3873,6 +3986,7 @@ function registerConnectorTools(server) {
|
|
|
3873
3986
|
installed: manifest.name,
|
|
3874
3987
|
connectionId: connection2.id,
|
|
3875
3988
|
trigger: trigger?.type,
|
|
3989
|
+
...installed && { version: installed.version, path: installed.path },
|
|
3876
3990
|
note: "Poll it now with backfill_connection, or reference it from a workflow."
|
|
3877
3991
|
});
|
|
3878
3992
|
}
|
|
@@ -3910,6 +4024,9 @@ function registerConnectorTools(server) {
|
|
|
3910
4024
|
}
|
|
3911
4025
|
);
|
|
3912
4026
|
}
|
|
4027
|
+
function packLaunch(pack) {
|
|
4028
|
+
return { command: "node", args: [`${pack.path}/index.js`] };
|
|
4029
|
+
}
|
|
3913
4030
|
async function probe(target) {
|
|
3914
4031
|
const launch = typeof target === "string" ? parseLaunch(target) : target;
|
|
3915
4032
|
return rpcCall("connector:probeSdk", launch, PROBE_TIMEOUT_MS);
|
|
@@ -3931,7 +4048,7 @@ function plural(count, word) {
|
|
|
3931
4048
|
}
|
|
3932
4049
|
|
|
3933
4050
|
// src/tools/browser.ts
|
|
3934
|
-
import
|
|
4051
|
+
import crypto5 from "crypto";
|
|
3935
4052
|
import { z as z8 } from "zod";
|
|
3936
4053
|
function sessionId(env = process.env) {
|
|
3937
4054
|
const id = env.VORN_SESSION_ID;
|
|
@@ -3958,7 +4075,7 @@ function source(label) {
|
|
|
3958
4075
|
return label.includes("WEB PAGE") || label.includes("BROWSER") ? "page" : "device";
|
|
3959
4076
|
}
|
|
3960
4077
|
function pageResult(data, label = "WEB PAGE CONTENT") {
|
|
3961
|
-
const nonce =
|
|
4078
|
+
const nonce = crypto5.randomUUID();
|
|
3962
4079
|
return {
|
|
3963
4080
|
content: [
|
|
3964
4081
|
{
|
|
@@ -4406,7 +4523,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
4406
4523
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
4407
4524
|
async function main() {
|
|
4408
4525
|
configManager.init();
|
|
4409
|
-
const version = true ? "0.7.0-beta.
|
|
4526
|
+
const version = true ? "0.7.0-beta.9" : createRequire(import.meta.url)("../package.json").version;
|
|
4410
4527
|
const server = createMcpServer(version);
|
|
4411
4528
|
const transport = new StdioServerTransport();
|
|
4412
4529
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vornrun/mcp",
|
|
3
|
-
"version": "0.7.0-beta.
|
|
3
|
+
"version": "0.7.0-beta.9",
|
|
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.7.0-beta.
|
|
42
|
-
"@vornrun/shared": "0.7.0-beta.
|
|
41
|
+
"@vornrun/server": "0.7.0-beta.9",
|
|
42
|
+
"@vornrun/shared": "0.7.0-beta.9",
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
44
|
"tsx": "^4.23.1",
|
|
45
45
|
"typescript": "^6.0.3"
|