node-exec-mcp 0.1.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.
@@ -0,0 +1,8 @@
1
+ export interface Config {
2
+ nodeId: string;
3
+ openclawBin: string;
4
+ defaultCwd: string;
5
+ timeoutMs: number;
6
+ }
7
+ export declare function loadConfig(): Config;
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,IAAI,MAAM,CA2BnC"}
package/dist/config.js ADDED
@@ -0,0 +1,20 @@
1
+ import { existsSync } from "node:fs";
2
+ export function loadConfig() {
3
+ const nodeId = process.env.NODE_ID;
4
+ if (!nodeId) {
5
+ throw new Error("NODE_ID environment variable is required. Set it to the target node's ID " +
6
+ "(from `openclaw nodes status`).");
7
+ }
8
+ const openclawBin = process.env.OPENCLAW_BIN || "openclaw";
9
+ const defaultCwd = process.env.DEFAULT_CWD || "/tmp";
10
+ const timeoutMs = parseInt(process.env.TIMEOUT_MS || "300000", 10);
11
+ if (isNaN(timeoutMs) || timeoutMs <= 0) {
12
+ throw new Error(`TIMEOUT_MS must be a positive integer, got: ${process.env.TIMEOUT_MS}`);
13
+ }
14
+ // Validate openclaw binary exists if an absolute path was given
15
+ if (openclawBin.startsWith("/") && !existsSync(openclawBin)) {
16
+ throw new Error(`OPENCLAW_BIN path does not exist: ${openclawBin}`);
17
+ }
18
+ return { nodeId, openclawBin, defaultCwd, timeoutMs };
19
+ }
20
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AASrC,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,iCAAiC,CACpC,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,UAAU,CAAC;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC;IACrD,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEnE,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CACb,+CAA+C,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CACxE,CAAC;IACJ,CAAC;IAED,gEAAgE;IAChE,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,qCAAqC,WAAW,EAAE,CACnD,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ async function main() {
4
+ const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const { loadConfig } = await import("./config.js");
7
+ const { registerRunTool } = await import("./tools/run.js");
8
+ const require = createRequire(import.meta.url);
9
+ const { version } = require("../package.json");
10
+ const config = loadConfig();
11
+ const server = new McpServer({ name: "node-exec-mcp", version }, { capabilities: { logging: {} } });
12
+ registerRunTool(server, config);
13
+ const transport = new StdioServerTransport();
14
+ await server.connect(transport);
15
+ const shutdown = async () => {
16
+ await server.close();
17
+ process.exit(0);
18
+ };
19
+ process.on("SIGINT", shutdown);
20
+ process.on("SIGTERM", shutdown);
21
+ transport.onclose = shutdown;
22
+ }
23
+ main().catch((err) => {
24
+ console.error("Fatal:", err);
25
+ process.exit(1);
26
+ });
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAChC,yCAAyC,CAC1C,CAAC;IACF,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAC3C,2CAA2C,CAC5C,CAAC;IACF,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAE3D,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;IAEtE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAClC,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAClC,CAAC;IAEF,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,SAAS,CAAC,OAAO,GAAG,QAAQ,CAAC;AAC/B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { Config } from "../config.js";
3
+ export declare function registerRunTool(server: McpServer, config: Config): void;
4
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAkB3C,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA8HvE"}
@@ -0,0 +1,114 @@
1
+ import { spawn } from "node:child_process";
2
+ import { z } from "zod";
3
+ export function registerRunTool(server, config) {
4
+ server.registerTool("run", {
5
+ title: "Run command on Mac",
6
+ description: "Execute a shell command on the remote Mac via OpenClaw node exec. " +
7
+ "Returns stdout, stderr, and exit code. Commands run as the Mac user " +
8
+ "with the configured approval policy.",
9
+ inputSchema: {
10
+ command: z
11
+ .string()
12
+ .describe("Shell command to execute on the Mac"),
13
+ cwd: z
14
+ .string()
15
+ .optional()
16
+ .describe(`Working directory on the Mac (default: ${config.defaultCwd})`),
17
+ timeout_ms: z
18
+ .number()
19
+ .positive()
20
+ .optional()
21
+ .describe(`Per-call timeout in milliseconds (default: ${config.timeoutMs})`),
22
+ },
23
+ }, async (args) => {
24
+ const cwd = args.cwd || config.defaultCwd;
25
+ const timeout = args.timeout_ms || config.timeoutMs;
26
+ const spawnArgs = [
27
+ "nodes",
28
+ "run",
29
+ "--node",
30
+ config.nodeId,
31
+ "--json",
32
+ "--timeout",
33
+ String(timeout),
34
+ "--cwd",
35
+ cwd,
36
+ "--",
37
+ "bash",
38
+ "-c",
39
+ args.command,
40
+ ];
41
+ return new Promise((resolve) => {
42
+ const child = spawn(config.openclawBin, spawnArgs, {
43
+ stdio: ["ignore", "pipe", "pipe"],
44
+ env: { ...process.env },
45
+ });
46
+ let stdout = "";
47
+ let stderr = "";
48
+ child.stdout.on("data", (chunk) => {
49
+ stdout += chunk.toString();
50
+ });
51
+ child.stderr.on("data", (chunk) => {
52
+ stderr += chunk.toString();
53
+ });
54
+ // Send progress notifications every 15s to prevent MCP adapter timeout
55
+ const progressInterval = setInterval(() => {
56
+ try {
57
+ server.server.sendLoggingMessage({
58
+ level: "info",
59
+ data: "Command still running...",
60
+ });
61
+ }
62
+ catch {
63
+ // Transport closed — ignore
64
+ }
65
+ }, 15_000);
66
+ child.on("close", (exitCode) => {
67
+ clearInterval(progressInterval);
68
+ // Try to parse JSON response from stdout
69
+ try {
70
+ const response = JSON.parse(stdout.trim());
71
+ const p = response.payload;
72
+ let output = "";
73
+ if (p.stdout)
74
+ output += p.stdout;
75
+ if (p.stderr)
76
+ output += (output ? "\n" : "") + `[stderr] ${p.stderr}`;
77
+ if (p.error)
78
+ output += (output ? "\n" : "") + `[error] ${p.error}`;
79
+ if (p.timedOut)
80
+ output += (output ? "\n" : "") + "[timed out]";
81
+ output = output || "(no output)";
82
+ output += `\n[exit code: ${p.exitCode}]`;
83
+ resolve({
84
+ content: [{ type: "text", text: output }],
85
+ isError: !p.success,
86
+ });
87
+ }
88
+ catch {
89
+ // JSON parse failed — gateway-level error (plain text on stderr)
90
+ const errorMsg = stderr.trim() ||
91
+ stdout.trim() ||
92
+ `Command failed with exit code ${exitCode}`;
93
+ resolve({
94
+ content: [{ type: "text", text: errorMsg }],
95
+ isError: true,
96
+ });
97
+ }
98
+ });
99
+ child.on("error", (err) => {
100
+ clearInterval(progressInterval);
101
+ resolve({
102
+ content: [
103
+ {
104
+ type: "text",
105
+ text: `Failed to spawn openclaw: ${err.message}`,
106
+ },
107
+ ],
108
+ isError: true,
109
+ });
110
+ });
111
+ });
112
+ });
113
+ }
114
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "node-exec-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for executing commands on remote machines via OpenClaw node exec",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "node-exec-mcp": "./dist/index.js"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "clean": "rm -rf dist",
23
+ "dev": "tsc --watch",
24
+ "start": "node dist/index.js",
25
+ "prepublishOnly": "npm run clean && npm run build",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest"
28
+ },
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "license": "MIT",
33
+ "author": "Andreas Spannagel",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/pandysp/node-exec-mcp.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/pandysp/node-exec-mcp/issues"
40
+ },
41
+ "homepage": "https://github.com/pandysp/node-exec-mcp#readme",
42
+ "keywords": [
43
+ "mcp",
44
+ "model-context-protocol",
45
+ "node-exec",
46
+ "remote-execution",
47
+ "openclaw"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "dependencies": {
53
+ "@modelcontextprotocol/sdk": "^1.25.3"
54
+ },
55
+ "devDependencies": {
56
+ "typescript": "^5.7.3",
57
+ "@types/node": "^22.0.0",
58
+ "vitest": "^3.2.0"
59
+ }
60
+ }