@vornrun/mcp 0.7.0-beta.7 → 0.7.0-beta.8
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 +142 -66
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1615,7 +1615,7 @@ var configManager = new ConfigManager();
|
|
|
1615
1615
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1616
1616
|
|
|
1617
1617
|
// src/tools/tasks.ts
|
|
1618
|
-
import
|
|
1618
|
+
import crypto2 from "crypto";
|
|
1619
1619
|
import path4 from "path";
|
|
1620
1620
|
import { z as z2 } from "zod";
|
|
1621
1621
|
|
|
@@ -2012,7 +2012,7 @@ function registerTaskTools(server) {
|
|
|
2012
2012
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2013
2013
|
const status = args.status ?? "todo";
|
|
2014
2014
|
const task = {
|
|
2015
|
-
id:
|
|
2015
|
+
id: crypto2.randomUUID(),
|
|
2016
2016
|
projectName: args.project_name,
|
|
2017
2017
|
title: args.title,
|
|
2018
2018
|
description: args.description ?? "",
|
|
@@ -2712,41 +2712,61 @@ function registerSessionTools(server) {
|
|
|
2712
2712
|
}
|
|
2713
2713
|
|
|
2714
2714
|
// src/tools/workflows.ts
|
|
2715
|
-
import
|
|
2715
|
+
import crypto3 from "crypto";
|
|
2716
2716
|
import { z as z5 } from "zod";
|
|
2717
2717
|
|
|
2718
|
-
// src/workflow-portability.ts
|
|
2718
|
+
// ../shared/src/workflow-portability.ts
|
|
2719
2719
|
var PROJECT_PATH_TOKEN = "{{project.path}}";
|
|
2720
2720
|
var PROJECT_NAME_TOKEN = "{{project.name}}";
|
|
2721
2721
|
var PORTABLE_FORMAT_VERSION = 1;
|
|
2722
|
+
var HTTP_PROFILE_CONNECTOR = "http";
|
|
2722
2723
|
function importedWorkflowId(bundle, slug) {
|
|
2723
2724
|
return `import:${bundle}:${slug}`;
|
|
2724
2725
|
}
|
|
2726
|
+
function importedWorkflowIdFor(bundle, slug, name, existing) {
|
|
2727
|
+
for (let attempt = 1; ; attempt++) {
|
|
2728
|
+
const id = importedWorkflowId(bundle, attempt === 1 ? slug : `${slug}-${attempt}`);
|
|
2729
|
+
const held = existing.find((workflow) => workflow.id === id);
|
|
2730
|
+
if (!held || held.name === name) return id;
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2725
2733
|
function slugify(name) {
|
|
2726
2734
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "workflow";
|
|
2727
2735
|
}
|
|
2728
|
-
function
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2736
|
+
function connectorOf(connection2) {
|
|
2737
|
+
return connectionConnectorId({
|
|
2738
|
+
connectorId: connection2.connectorId,
|
|
2739
|
+
filters: connection2.filters ?? {}
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
function boundConnectionKey(node, config) {
|
|
2743
|
+
if (node.type === "trigger" && config.triggerType === "connectorPoll") return "connectionId";
|
|
2744
|
+
if (node.type === "callConnectorAction") return "connectionId";
|
|
2745
|
+
if (node.type === "httpRequest") return "profileConnectionId";
|
|
2746
|
+
return null;
|
|
2747
|
+
}
|
|
2748
|
+
function resolveRequirement(requirement, connections) {
|
|
2749
|
+
const candidates = connections.filter(
|
|
2750
|
+
(connection2) => requirement.kind === "httpProfile" ? connectorOf(connection2) === HTTP_PROFILE_CONNECTOR : requirement.connectorId !== "" && connectorOf(connection2) === requirement.connectorId
|
|
2751
|
+
);
|
|
2752
|
+
if (candidates.length === 0) return void 0;
|
|
2753
|
+
if (requirement.name !== "") {
|
|
2754
|
+
const named = candidates.filter((connection2) => connection2.name === requirement.name);
|
|
2755
|
+
if (named.length === 1) return named[0].id;
|
|
2738
2756
|
}
|
|
2739
|
-
return
|
|
2757
|
+
return candidates.length === 1 ? candidates[0].id : void 0;
|
|
2740
2758
|
}
|
|
2741
|
-
function toPortable(workflow, projectPath) {
|
|
2759
|
+
function toPortable(workflow, projectPath, connections = []) {
|
|
2742
2760
|
const slug = slugify(workflow.name);
|
|
2761
|
+
const requires = [];
|
|
2743
2762
|
const nodes = workflow.nodes.map((node) => {
|
|
2744
2763
|
const config = { ...node.config };
|
|
2764
|
+
if (node.type === "trigger" && config.triggerType === "webhook") config.token = "";
|
|
2745
2765
|
if (node.type === "launchAgent" || node.type === "script") {
|
|
2746
|
-
for (const
|
|
2747
|
-
const value = config[
|
|
2766
|
+
for (const key2 of ["projectPath", "cwd", "existingWorktreePath"]) {
|
|
2767
|
+
const value = config[key2];
|
|
2748
2768
|
if (typeof value === "string" && value) {
|
|
2749
|
-
config[
|
|
2769
|
+
config[key2] = replacePath(value, projectPath);
|
|
2750
2770
|
}
|
|
2751
2771
|
}
|
|
2752
2772
|
if (typeof config.projectName === "string" && config.projectName) {
|
|
@@ -2754,6 +2774,23 @@ function toPortable(workflow, projectPath) {
|
|
|
2754
2774
|
}
|
|
2755
2775
|
delete config.remoteHostId;
|
|
2756
2776
|
}
|
|
2777
|
+
const key = boundConnectionKey(node, config);
|
|
2778
|
+
const bound = key === null ? "" : config[key];
|
|
2779
|
+
if (key !== null && typeof bound === "string" && bound !== "") {
|
|
2780
|
+
const source2 = connections.find((connection2) => connection2.id === bound);
|
|
2781
|
+
const event = config.event;
|
|
2782
|
+
requires.push(
|
|
2783
|
+
key === "profileConnectionId" ? { kind: "httpProfile", nodeId: node.id, name: source2?.name ?? "" } : {
|
|
2784
|
+
kind: "connection",
|
|
2785
|
+
nodeId: node.id,
|
|
2786
|
+
connectorId: source2 ? connectorOf(source2) : "",
|
|
2787
|
+
name: source2?.name ?? "",
|
|
2788
|
+
...typeof event === "string" && event !== "" && { event }
|
|
2789
|
+
}
|
|
2790
|
+
);
|
|
2791
|
+
if (key === "profileConnectionId") delete config[key];
|
|
2792
|
+
else config[key] = "";
|
|
2793
|
+
}
|
|
2757
2794
|
return { ...node, config };
|
|
2758
2795
|
});
|
|
2759
2796
|
return {
|
|
@@ -2763,6 +2800,7 @@ function toPortable(workflow, projectPath) {
|
|
|
2763
2800
|
...workflow.icon && { icon: workflow.icon },
|
|
2764
2801
|
...workflow.iconColor && { iconColor: workflow.iconColor },
|
|
2765
2802
|
...workflow.staggerDelayMs !== void 0 && { staggerDelayMs: workflow.staggerDelayMs },
|
|
2803
|
+
...requires.length > 0 && { requires },
|
|
2766
2804
|
nodes,
|
|
2767
2805
|
edges: workflow.edges
|
|
2768
2806
|
};
|
|
@@ -2773,17 +2811,37 @@ function normalizeForCompare(p) {
|
|
|
2773
2811
|
function replacePath(value, projectPath) {
|
|
2774
2812
|
const v = normalizeForCompare(value);
|
|
2775
2813
|
const root = normalizeForCompare(projectPath);
|
|
2814
|
+
if (root === "") return value;
|
|
2776
2815
|
if (v === root) return PROJECT_PATH_TOKEN;
|
|
2777
2816
|
if (v.startsWith(`${root}/`)) return `${PROJECT_PATH_TOKEN}/${v.slice(root.length + 1)}`;
|
|
2778
2817
|
return value;
|
|
2779
2818
|
}
|
|
2780
|
-
function
|
|
2819
|
+
function unresolvedRequirements(portable, connections) {
|
|
2820
|
+
const present = new Set(portable.nodes.map((node) => node.id));
|
|
2821
|
+
return (portable.requires ?? []).filter(
|
|
2822
|
+
(requirement) => present.has(requirement.nodeId) && resolveRequirement(requirement, connections) === void 0
|
|
2823
|
+
);
|
|
2824
|
+
}
|
|
2825
|
+
function fromPortable(portable, bundle, project, connections = [], mintToken = () => crypto.randomUUID()) {
|
|
2826
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
2827
|
+
for (const requirement of portable.requires ?? []) {
|
|
2828
|
+
bindings.set(requirement.nodeId, [...bindings.get(requirement.nodeId) ?? [], requirement]);
|
|
2829
|
+
}
|
|
2781
2830
|
const nodes = portable.nodes.map((node) => {
|
|
2782
2831
|
const config = { ...node.config };
|
|
2783
2832
|
for (const [key, value] of Object.entries(config)) {
|
|
2784
2833
|
if (typeof value !== "string") continue;
|
|
2785
2834
|
config[key] = value.split(PROJECT_PATH_TOKEN).join(project.path.replace(/[/\\]+$/, "")).split(PROJECT_NAME_TOKEN).join(project.name);
|
|
2786
2835
|
}
|
|
2836
|
+
for (const requirement of bindings.get(node.id) ?? []) {
|
|
2837
|
+
const resolved = resolveRequirement(requirement, connections);
|
|
2838
|
+
if (resolved === void 0) continue;
|
|
2839
|
+
if (requirement.kind === "httpProfile") config.profileConnectionId = resolved;
|
|
2840
|
+
else config.connectionId = resolved;
|
|
2841
|
+
}
|
|
2842
|
+
if (node.type === "trigger" && config.triggerType === "webhook" && !config.token) {
|
|
2843
|
+
config.token = mintToken();
|
|
2844
|
+
}
|
|
2787
2845
|
return { ...node, config };
|
|
2788
2846
|
});
|
|
2789
2847
|
return {
|
|
@@ -2791,7 +2849,9 @@ function fromPortable(portable, bundle, project) {
|
|
|
2791
2849
|
name: portable.name,
|
|
2792
2850
|
icon: portable.icon ?? "Zap",
|
|
2793
2851
|
iconColor: portable.iconColor ?? "#6366f1",
|
|
2794
|
-
|
|
2852
|
+
// A file cannot ask to be running: a dropped cron workflow would start
|
|
2853
|
+
// firing before anyone had read it. Callers restore what they had.
|
|
2854
|
+
enabled: false,
|
|
2795
2855
|
...portable.staggerDelayMs !== void 0 && { staggerDelayMs: portable.staggerDelayMs },
|
|
2796
2856
|
nodes,
|
|
2797
2857
|
edges: portable.edges
|
|
@@ -3002,7 +3062,7 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
3002
3062
|
const nodes = [];
|
|
3003
3063
|
const edges = [];
|
|
3004
3064
|
const triggerNode = {
|
|
3005
|
-
id:
|
|
3065
|
+
id: crypto3.randomUUID(),
|
|
3006
3066
|
type: "trigger",
|
|
3007
3067
|
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
3068
|
config: trigger,
|
|
@@ -3013,7 +3073,7 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
3013
3073
|
const NODE_GAP = 140;
|
|
3014
3074
|
for (let i = 0; i < actions.length; i++) {
|
|
3015
3075
|
const action = actions[i];
|
|
3016
|
-
const nodeId =
|
|
3076
|
+
const nodeId = crypto3.randomUUID();
|
|
3017
3077
|
nodes.push({
|
|
3018
3078
|
id: nodeId,
|
|
3019
3079
|
type: "launchAgent",
|
|
@@ -3022,7 +3082,7 @@ function buildGraphFromFlat(trigger, actions) {
|
|
|
3022
3082
|
position: { x: 0, y: (i + 1) * NODE_GAP }
|
|
3023
3083
|
});
|
|
3024
3084
|
edges.push({
|
|
3025
|
-
id:
|
|
3085
|
+
id: crypto3.randomUUID(),
|
|
3026
3086
|
source: prevId,
|
|
3027
3087
|
target: nodeId
|
|
3028
3088
|
});
|
|
@@ -3099,6 +3159,13 @@ function resolveWorkflowId(args) {
|
|
|
3099
3159
|
}
|
|
3100
3160
|
return { id };
|
|
3101
3161
|
}
|
|
3162
|
+
async function listPortableConnections() {
|
|
3163
|
+
try {
|
|
3164
|
+
return await rpcCall("connection:list", { connectorId: void 0 });
|
|
3165
|
+
} catch {
|
|
3166
|
+
return [];
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3102
3169
|
function registerWorkflowTools(server) {
|
|
3103
3170
|
server.tool(
|
|
3104
3171
|
"list_workflows",
|
|
@@ -3151,7 +3218,7 @@ function registerWorkflowTools(server) {
|
|
|
3151
3218
|
edges = graph.edges;
|
|
3152
3219
|
}
|
|
3153
3220
|
const workflow = {
|
|
3154
|
-
id:
|
|
3221
|
+
id: crypto3.randomUUID(),
|
|
3155
3222
|
name: args.name,
|
|
3156
3223
|
icon: args.icon ?? "Zap",
|
|
3157
3224
|
iconColor: args.icon_color ?? "#6366f1",
|
|
@@ -3421,7 +3488,7 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3421
3488
|
);
|
|
3422
3489
|
server.tool(
|
|
3423
3490
|
"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.
|
|
3491
|
+
"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
3492
|
{
|
|
3426
3493
|
workflow_id: V.id.optional().describe("Workflow ID (from list_workflows)"),
|
|
3427
3494
|
id: V.id.optional().describe("Deprecated alias for workflow_id")
|
|
@@ -3438,18 +3505,6 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3438
3505
|
isError: true
|
|
3439
3506
|
};
|
|
3440
3507
|
}
|
|
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
3508
|
const projects = await dbListProjects();
|
|
3454
3509
|
const projectName = workflow.nodes.map((n) => n.config.projectName).find((name) => typeof name === "string" && name.length > 0);
|
|
3455
3510
|
const project = projects.find((p) => p.name === projectName);
|
|
@@ -3464,15 +3519,20 @@ Run history: list_workflow_runs with workflow_id ${args.workflow_id}`
|
|
|
3464
3519
|
isError: true
|
|
3465
3520
|
};
|
|
3466
3521
|
}
|
|
3467
|
-
const portable = toPortable(workflow, project.path);
|
|
3522
|
+
const portable = toPortable(workflow, project.path, await listPortableConnections());
|
|
3468
3523
|
const residual = residualAbsolutePaths(portable);
|
|
3524
|
+
const unnamed = (portable.requires ?? []).filter(
|
|
3525
|
+
(requirement) => requirement.kind === "connection" && requirement.connectorId === ""
|
|
3526
|
+
);
|
|
3469
3527
|
return {
|
|
3470
3528
|
content: [
|
|
3471
3529
|
{
|
|
3472
3530
|
type: "text",
|
|
3473
3531
|
text: JSON.stringify(portable, null, 2) + (residual.length > 0 ? `
|
|
3474
3532
|
|
|
3475
|
-
Warning: these still hold a machine-specific path and will not travel: ${residual.join(", ")}` : "")
|
|
3533
|
+
Warning: these still hold a machine-specific path and will not travel: ${residual.join(", ")}` : "") + (unnamed.length > 0 ? `
|
|
3534
|
+
|
|
3535
|
+
Warning: ${unnamed.length} step(s) point at a connection this install could not name, so an import cannot rebind them automatically.` : "")
|
|
3476
3536
|
}
|
|
3477
3537
|
]
|
|
3478
3538
|
};
|
|
@@ -3480,7 +3540,7 @@ Warning: these still hold a machine-specific path and will not travel: ${residua
|
|
|
3480
3540
|
);
|
|
3481
3541
|
server.tool(
|
|
3482
3542
|
"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.",
|
|
3543
|
+
"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
3544
|
{
|
|
3485
3545
|
workflow: z5.string().max(5e5).describe("The exported workflow JSON"),
|
|
3486
3546
|
project_name: V.name.describe("Registered project to resolve paths against"),
|
|
@@ -3540,38 +3600,38 @@ Warning: these still hold a machine-specific path and will not travel: ${residua
|
|
|
3540
3600
|
};
|
|
3541
3601
|
}
|
|
3542
3602
|
const bundle = args.bundle ?? slugify(project.name);
|
|
3543
|
-
const
|
|
3544
|
-
|
|
3603
|
+
const portable = { ...parsed, slug: parsed.slug ?? slugify(parsed.name) };
|
|
3604
|
+
const connections = await listPortableConnections();
|
|
3605
|
+
const resolved = fromPortable(
|
|
3606
|
+
portable,
|
|
3545
3607
|
bundle,
|
|
3546
3608
|
{
|
|
3547
3609
|
name: project.name,
|
|
3548
3610
|
path: project.path
|
|
3549
|
-
}
|
|
3611
|
+
},
|
|
3612
|
+
connections
|
|
3550
3613
|
);
|
|
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);
|
|
3614
|
+
const unresolved = unresolvedRequirements(portable, connections);
|
|
3615
|
+
const known = await dbListWorkflows();
|
|
3616
|
+
const id = importedWorkflowIdFor(bundle, portable.slug, portable.name, known);
|
|
3617
|
+
const existing = known.find((w) => w.id === id);
|
|
3618
|
+
const definition = { ...resolved, id, enabled: existing ? existing.enabled : false };
|
|
3564
3619
|
if (existing) {
|
|
3565
3620
|
await dbUpdateWorkflow(definition.id, definition);
|
|
3566
3621
|
} else {
|
|
3567
3622
|
await dbInsertWorkflow(definition);
|
|
3568
3623
|
}
|
|
3569
3624
|
dbSignalChange();
|
|
3625
|
+
const pending = unresolved.map(
|
|
3626
|
+
(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}"` : ""}`
|
|
3627
|
+
).join("; ");
|
|
3570
3628
|
return {
|
|
3571
3629
|
content: [
|
|
3572
3630
|
{
|
|
3573
3631
|
type: "text",
|
|
3574
|
-
text: `${existing ? "Updated" : "Imported"} "${definition.name}" as ${definition.id}, resolved against ${project.path}`
|
|
3632
|
+
text: `${existing ? "Updated" : "Imported"} "${definition.name}" as ${definition.id}, resolved against ${project.path}` + (existing || definition.enabled ? "" : ". It is disabled; enable it when ready") + (pending ? `
|
|
3633
|
+
|
|
3634
|
+
Still to connect: ${pending}` : "")
|
|
3575
3635
|
}
|
|
3576
3636
|
]
|
|
3577
3637
|
};
|
|
@@ -3592,7 +3652,7 @@ function registerConfigTools(server) {
|
|
|
3592
3652
|
}
|
|
3593
3653
|
|
|
3594
3654
|
// src/tools/workspaces.ts
|
|
3595
|
-
import
|
|
3655
|
+
import crypto4 from "crypto";
|
|
3596
3656
|
import { z as z6 } from "zod";
|
|
3597
3657
|
function registerWorkspaceTools(server) {
|
|
3598
3658
|
server.tool("list_workspaces", "List all workspaces", async () => {
|
|
@@ -3611,7 +3671,7 @@ function registerWorkspaceTools(server) {
|
|
|
3611
3671
|
const existing = await dbListWorkspaces();
|
|
3612
3672
|
const maxOrder = existing.reduce((max, w) => Math.max(max, w.order), 0);
|
|
3613
3673
|
const workspace = {
|
|
3614
|
-
id:
|
|
3674
|
+
id: crypto4.randomUUID(),
|
|
3615
3675
|
name: args.name,
|
|
3616
3676
|
order: maxOrder + 1,
|
|
3617
3677
|
...args.icon && { icon: args.icon },
|
|
@@ -3802,10 +3862,13 @@ function registerConnectorTools(server) {
|
|
|
3802
3862
|
);
|
|
3803
3863
|
server.tool(
|
|
3804
3864
|
"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.",
|
|
3865
|
+
"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
3866
|
{
|
|
3807
3867
|
connector_id: V.id.optional().describe("Catalog connector id (from list_connectors). Use this or package."),
|
|
3808
3868
|
package: V.shortText.optional().describe("npm package name or launch command"),
|
|
3869
|
+
pack_path: V.shortText.optional().describe(
|
|
3870
|
+
"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."
|
|
3871
|
+
),
|
|
3809
3872
|
name: V.title.optional().describe("Connection name (defaults to the connector name)"),
|
|
3810
3873
|
project: V.name.optional().describe("Vorn project tasks should be created in"),
|
|
3811
3874
|
trigger: V.shortText.optional().describe("Trigger type to configure (defaults to the first the connector offers)"),
|
|
@@ -3820,8 +3883,17 @@ function registerConnectorTools(server) {
|
|
|
3820
3883
|
`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
3884
|
);
|
|
3822
3885
|
}
|
|
3823
|
-
|
|
3824
|
-
if (
|
|
3886
|
+
let installed;
|
|
3887
|
+
if (args.pack_path) {
|
|
3888
|
+
const outcome = await rpcCall("connector:installPack", {
|
|
3889
|
+
kind: "file",
|
|
3890
|
+
path: args.pack_path
|
|
3891
|
+
});
|
|
3892
|
+
if (!outcome.ok) return failure(`The pack was refused: ${outcome.error}`);
|
|
3893
|
+
installed = outcome.pack;
|
|
3894
|
+
}
|
|
3895
|
+
const target = installed ? packLaunch(installed) : entry?.launch ?? args.package;
|
|
3896
|
+
if (!target) return failure("Provide either connector_id, package, or pack_path.");
|
|
3825
3897
|
const result = await probe(target);
|
|
3826
3898
|
if (!result.ok) return failure(result.error);
|
|
3827
3899
|
const manifest = result.manifest;
|
|
@@ -3873,6 +3945,7 @@ function registerConnectorTools(server) {
|
|
|
3873
3945
|
installed: manifest.name,
|
|
3874
3946
|
connectionId: connection2.id,
|
|
3875
3947
|
trigger: trigger?.type,
|
|
3948
|
+
...installed && { version: installed.version, path: installed.path },
|
|
3876
3949
|
note: "Poll it now with backfill_connection, or reference it from a workflow."
|
|
3877
3950
|
});
|
|
3878
3951
|
}
|
|
@@ -3910,6 +3983,9 @@ function registerConnectorTools(server) {
|
|
|
3910
3983
|
}
|
|
3911
3984
|
);
|
|
3912
3985
|
}
|
|
3986
|
+
function packLaunch(pack) {
|
|
3987
|
+
return { command: "node", args: [`${pack.path}/index.js`] };
|
|
3988
|
+
}
|
|
3913
3989
|
async function probe(target) {
|
|
3914
3990
|
const launch = typeof target === "string" ? parseLaunch(target) : target;
|
|
3915
3991
|
return rpcCall("connector:probeSdk", launch, PROBE_TIMEOUT_MS);
|
|
@@ -3931,7 +4007,7 @@ function plural(count, word) {
|
|
|
3931
4007
|
}
|
|
3932
4008
|
|
|
3933
4009
|
// src/tools/browser.ts
|
|
3934
|
-
import
|
|
4010
|
+
import crypto5 from "crypto";
|
|
3935
4011
|
import { z as z8 } from "zod";
|
|
3936
4012
|
function sessionId(env = process.env) {
|
|
3937
4013
|
const id = env.VORN_SESSION_ID;
|
|
@@ -3958,7 +4034,7 @@ function source(label) {
|
|
|
3958
4034
|
return label.includes("WEB PAGE") || label.includes("BROWSER") ? "page" : "device";
|
|
3959
4035
|
}
|
|
3960
4036
|
function pageResult(data, label = "WEB PAGE CONTENT") {
|
|
3961
|
-
const nonce =
|
|
4037
|
+
const nonce = crypto5.randomUUID();
|
|
3962
4038
|
return {
|
|
3963
4039
|
content: [
|
|
3964
4040
|
{
|
|
@@ -4406,7 +4482,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
4406
4482
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
4407
4483
|
async function main() {
|
|
4408
4484
|
configManager.init();
|
|
4409
|
-
const version = true ? "0.7.0-beta.
|
|
4485
|
+
const version = true ? "0.7.0-beta.8" : createRequire(import.meta.url)("../package.json").version;
|
|
4410
4486
|
const server = createMcpServer(version);
|
|
4411
4487
|
const transport = new StdioServerTransport();
|
|
4412
4488
|
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.8",
|
|
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.8",
|
|
42
|
+
"@vornrun/shared": "0.7.0-beta.8",
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
44
|
"tsx": "^4.23.1",
|
|
45
45
|
"typescript": "^6.0.3"
|