wormkey-mcp 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +97 -0
- package/package.json +27 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile, spawn } from "node:child_process";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
const wormkeyBin = process.env.WORMKEY_BIN ?? "wormkey";
|
|
7
|
+
const tunnels = new Map();
|
|
8
|
+
const tools = [
|
|
9
|
+
{ name: "wormkey_start_tunnel", description: "Start a Wormkey HTTP tunnel", inputSchema: { type: "object", properties: { port: { type: "number" }, wait_for: { type: "string" }, timeout: { type: "string" }, inspect: { type: "boolean" } }, required: ["port"] } },
|
|
10
|
+
{ name: "wormkey_get_tunnel", description: "Get a tunnel status", inputSchema: { type: "object", properties: { session: { type: "string" } } } },
|
|
11
|
+
{ name: "wormkey_list_tunnels", description: "List Wormkey tunnels", inputSchema: { type: "object", properties: {} } },
|
|
12
|
+
{ name: "wormkey_close_tunnel", description: "Close a Wormkey tunnel", inputSchema: { type: "object", properties: { session: { type: "string" } } } },
|
|
13
|
+
{ name: "wormkey_get_requests", description: "Read captured request logs", inputSchema: { type: "object", properties: { session: { type: "string" } } } },
|
|
14
|
+
{ name: "wormkey_replay_request", description: "Replay a captured request", inputSchema: { type: "object", properties: { request_id: { type: "string" } }, required: ["request_id"] } },
|
|
15
|
+
{ name: "wormkey_wait_until_ready", description: "Wait for a local HTTP service", inputSchema: { type: "object", properties: { port: { type: "number" }, path: { type: "string" }, timeout: { type: "string" } }, required: ["port"] } },
|
|
16
|
+
];
|
|
17
|
+
async function runCommand(args) {
|
|
18
|
+
const { stdout } = await execFileAsync(wormkeyBin, args, { maxBuffer: 10 * 1024 * 1024 });
|
|
19
|
+
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
20
|
+
if (lines.length === 0)
|
|
21
|
+
return { ok: true };
|
|
22
|
+
return lines.map((line) => JSON.parse(line));
|
|
23
|
+
}
|
|
24
|
+
async function startTunnel(argumentsValue) {
|
|
25
|
+
const args = ["http", String(argumentsValue.port), "--json", "--no-interactive"];
|
|
26
|
+
if (argumentsValue.wait_for)
|
|
27
|
+
args.push("--wait-for", String(argumentsValue.wait_for));
|
|
28
|
+
if (argumentsValue.timeout)
|
|
29
|
+
args.push("--timeout", String(argumentsValue.timeout));
|
|
30
|
+
if (argumentsValue.inspect)
|
|
31
|
+
args.push("--inspect");
|
|
32
|
+
const child = spawn(wormkeyBin, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
33
|
+
const output = createInterface({ input: child.stdout });
|
|
34
|
+
const firstLine = await new Promise((resolve, reject) => {
|
|
35
|
+
output.once("line", resolve);
|
|
36
|
+
child.once("error", reject);
|
|
37
|
+
child.once("exit", (code) => reject(new Error(`wormkey exited before readiness with code ${code}`)));
|
|
38
|
+
});
|
|
39
|
+
output.close();
|
|
40
|
+
const session = JSON.parse(firstLine);
|
|
41
|
+
tunnels.set(session.session_id, child);
|
|
42
|
+
child.once("exit", () => tunnels.delete(session.session_id));
|
|
43
|
+
return session;
|
|
44
|
+
}
|
|
45
|
+
async function callTool(name, argumentsValue) {
|
|
46
|
+
if (name === "wormkey_start_tunnel")
|
|
47
|
+
return startTunnel(argumentsValue);
|
|
48
|
+
if (name === "wormkey_get_tunnel")
|
|
49
|
+
return runCommand(["status", ...argumentsValue.session ? [String(argumentsValue.session)] : [], "--json"]);
|
|
50
|
+
if (name === "wormkey_list_tunnels")
|
|
51
|
+
return runCommand(["list", "--json"]);
|
|
52
|
+
if (name === "wormkey_close_tunnel")
|
|
53
|
+
return runCommand(["close", ...argumentsValue.session ? [String(argumentsValue.session)] : [], "--json"]);
|
|
54
|
+
if (name === "wormkey_get_requests")
|
|
55
|
+
return runCommand(["logs", ...argumentsValue.session ? [String(argumentsValue.session)] : [], "--json"]);
|
|
56
|
+
if (name === "wormkey_replay_request")
|
|
57
|
+
return runCommand(["replay", String(argumentsValue.request_id)]);
|
|
58
|
+
if (name === "wormkey_wait_until_ready")
|
|
59
|
+
return runCommand(["wait-until-ready", String(argumentsValue.port), "--path", String(argumentsValue.path ?? "/"), "--timeout", String(argumentsValue.timeout ?? "60s")]);
|
|
60
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
61
|
+
}
|
|
62
|
+
function send(message) {
|
|
63
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
64
|
+
}
|
|
65
|
+
const input = createInterface({ input: process.stdin });
|
|
66
|
+
input.on("line", async (line) => {
|
|
67
|
+
let request = { method: "" };
|
|
68
|
+
try {
|
|
69
|
+
request = JSON.parse(line);
|
|
70
|
+
if (request.method === "initialize") {
|
|
71
|
+
send({ jsonrpc: "2.0", id: request.id, result: { protocolVersion: "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "wormkey", version: "0.4.0" } } });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (request.method === "notifications/initialized")
|
|
75
|
+
return;
|
|
76
|
+
if (request.method === "tools/list") {
|
|
77
|
+
send({ jsonrpc: "2.0", id: request.id, result: { tools } });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (request.method === "tools/call") {
|
|
81
|
+
const result = await callTool(request.params?.name, request.params?.arguments ?? {});
|
|
82
|
+
send({ jsonrpc: "2.0", id: request.id, result: { content: [{ type: "text", text: JSON.stringify(result) }] } });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
send({ jsonrpc: "2.0", id: request.id, error: { code: -32601, message: "Method not found" } });
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
send({ jsonrpc: "2.0", id: request?.id, error: { code: -32000, message: error instanceof Error ? error.message : String(error) } });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
function shutdown() {
|
|
92
|
+
for (const child of tunnels.values())
|
|
93
|
+
child.kill("SIGTERM");
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
process.on("SIGINT", shutdown);
|
|
97
|
+
process.on("SIGTERM", shutdown);
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wormkey-mcp",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "MCP server for Wormkey tunnels",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wormkey-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"files": ["dist"],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "node dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"wormkey": "^0.4.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^20.10.0",
|
|
21
|
+
"typescript": "^5.3.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|