@justanothermldude/mcp-exec 1.1.0 → 1.2.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.js CHANGED
@@ -1,67 +1,1475 @@
1
- #!/usr/bin/env node
2
- /**
3
- * @meta-mcp/exec - MCP execution utilities for sandboxed code execution
4
- *
5
- * Entry point for both the MCP server and programmatic API usage.
6
- */
7
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
- import { createMcpExecServer } from './server.js';
9
- import { ServerPool, createConnection, getServerConfig, loadServerManifest } from '@justanothermldude/meta-mcp-core';
10
- // Export types
11
- export * from './types/index.js';
12
- // Export sandbox module
13
- export * from './sandbox/index.js';
14
- // Export codegen module
15
- export * from './codegen/index.js';
16
- // Export bridge module
17
- export * from './bridge/index.js';
18
- // Export tools module
19
- export * from './tools/index.js';
20
- // Export server module
21
- export { createMcpExecServer } from './server.js';
22
- import { SandboxExecutor } from './sandbox/index.js';
23
- import { DEFAULT_TIMEOUT_MS } from './types/execution.js';
24
- export const APP_NAME = 'mcp-exec';
25
- export const VERSION = '0.1.0';
26
- // Default executor instance (lazily initialized)
27
- let defaultExecutor = null;
28
- /**
29
- * Get or create the default SandboxExecutor instance
30
- */
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // dist/index.js
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+
7
+ // dist/server.js
8
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
9
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
10
+
11
+ // dist/tools/list-servers.js
12
+ import { listServers } from "@justanothermldude/meta-mcp-core";
13
+ var listAvailableMcpServersTool = {
14
+ name: "list_available_mcp_servers",
15
+ description: "List available MCP servers with their names, descriptions, and tags. Optionally filter by name or tag.",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ filter: {
20
+ type: "string",
21
+ description: "Optional filter string to match server names, descriptions, or tags"
22
+ }
23
+ },
24
+ required: []
25
+ }
26
+ };
27
+ function createListServersHandler() {
28
+ return /* @__PURE__ */ __name(async function listServersHandler(args2) {
29
+ const { filter } = args2;
30
+ try {
31
+ let servers = listServers();
32
+ if (filter && typeof filter === "string") {
33
+ const filterLower = filter.toLowerCase();
34
+ servers = servers.filter((server) => {
35
+ if (server.name.toLowerCase().includes(filterLower)) {
36
+ return true;
37
+ }
38
+ if (server.description?.toLowerCase().includes(filterLower)) {
39
+ return true;
40
+ }
41
+ if (server.tags?.some((tag) => tag.toLowerCase().includes(filterLower))) {
42
+ return true;
43
+ }
44
+ return false;
45
+ });
46
+ }
47
+ return {
48
+ content: [{ type: "text", text: JSON.stringify(servers, null, 2) }],
49
+ isError: false
50
+ };
51
+ } catch (error) {
52
+ const errorMessage = error instanceof Error ? error.message : String(error);
53
+ return {
54
+ content: [{ type: "text", text: `Error listing servers: ${errorMessage}` }],
55
+ isError: true
56
+ };
57
+ }
58
+ }, "listServersHandler");
59
+ }
60
+ __name(createListServersHandler, "createListServersHandler");
61
+ function isListServersInput(args2) {
62
+ if (typeof args2 !== "object" || args2 === null) {
63
+ return false;
64
+ }
65
+ const input = args2;
66
+ if ("filter" in input && typeof input.filter !== "string") {
67
+ return false;
68
+ }
69
+ return true;
70
+ }
71
+ __name(isListServersInput, "isListServersInput");
72
+
73
+ // dist/tools/get-tool-schema.js
74
+ var getMcpToolSchemaTool = {
75
+ name: "get_mcp_tool_schema",
76
+ description: "Get the full schema for a specific MCP tool, or list all tools on a server. Omit tool param to discover available tools.",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {
80
+ server: {
81
+ type: "string",
82
+ description: "The name of the MCP server that has the tool"
83
+ },
84
+ tool: {
85
+ type: "string",
86
+ description: "The name of the tool to get the schema for. Omit to list all tools."
87
+ }
88
+ },
89
+ required: ["server"]
90
+ }
91
+ };
92
+ function createGetToolSchemaHandler(pool) {
93
+ return /* @__PURE__ */ __name(async function getToolSchemaHandler(args2) {
94
+ const { server, tool } = args2;
95
+ try {
96
+ const connection = await pool.getConnection(server);
97
+ const tools = await connection.getTools();
98
+ if (!tool) {
99
+ const summary = tools.map((t) => ({
100
+ name: t.name,
101
+ description: t.description || ""
102
+ }));
103
+ return {
104
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }],
105
+ isError: false
106
+ };
107
+ }
108
+ const matchedTool = tools.find((t) => t.name === tool);
109
+ if (!matchedTool) {
110
+ const availableTools = tools.map((t) => t.name).sort();
111
+ return {
112
+ content: [
113
+ {
114
+ type: "text",
115
+ text: `Tool '${tool}' not found on server '${server}'. Available tools: ${availableTools.join(", ")}`
116
+ }
117
+ ],
118
+ isError: true
119
+ };
120
+ }
121
+ return {
122
+ content: [{ type: "text", text: JSON.stringify(matchedTool, null, 2) }],
123
+ isError: false
124
+ };
125
+ } catch (error) {
126
+ const errorMessage = error instanceof Error ? error.message : String(error);
127
+ if (errorMessage.includes("not found") || errorMessage.includes("unknown server")) {
128
+ return {
129
+ content: [
130
+ {
131
+ type: "text",
132
+ text: `Error connecting to server '${server}': ${errorMessage}. Use list_available_mcp_servers to see available servers.`
133
+ }
134
+ ],
135
+ isError: true
136
+ };
137
+ }
138
+ return {
139
+ content: [
140
+ {
141
+ type: "text",
142
+ text: `Error getting tool schema: ${errorMessage}`
143
+ }
144
+ ],
145
+ isError: true
146
+ };
147
+ }
148
+ }, "getToolSchemaHandler");
149
+ }
150
+ __name(createGetToolSchemaHandler, "createGetToolSchemaHandler");
151
+ function isGetToolSchemaInput(args2) {
152
+ if (typeof args2 !== "object" || args2 === null) {
153
+ return false;
154
+ }
155
+ const input = args2;
156
+ if (typeof input.server !== "string") {
157
+ return false;
158
+ }
159
+ if (input.tool !== void 0 && typeof input.tool !== "string") {
160
+ return false;
161
+ }
162
+ return true;
163
+ }
164
+ __name(isGetToolSchemaInput, "isGetToolSchemaInput");
165
+
166
+ // dist/codegen/wrapper-generator.js
167
+ var BRIDGE_ENDPOINT = "http://127.0.0.1:3000/call";
168
+ function jsonSchemaToTs(prop, _required = true) {
169
+ if (!prop) {
170
+ return "unknown";
171
+ }
172
+ if (Array.isArray(prop.type)) {
173
+ const types = prop.type.map((t) => primitiveToTs(t));
174
+ return types.join(" | ");
175
+ }
176
+ if (prop.enum) {
177
+ return prop.enum.map((v) => JSON.stringify(v)).join(" | ");
178
+ }
179
+ if (prop.type === "object" && prop.properties) {
180
+ const propLines = Object.entries(prop.properties).map(([key, value]) => {
181
+ const isRequired = prop.required?.includes(key) ?? false;
182
+ const tsType = jsonSchemaToTs(value, isRequired);
183
+ const optionalMark = isRequired ? "" : "?";
184
+ return `${key}${optionalMark}: ${tsType}`;
185
+ });
186
+ return `{ ${propLines.join("; ")} }`;
187
+ }
188
+ if (prop.type === "array") {
189
+ const itemType = prop.items ? jsonSchemaToTs(prop.items, true) : "unknown";
190
+ return `${itemType}[]`;
191
+ }
192
+ return primitiveToTs(prop.type ?? "unknown");
193
+ }
194
+ __name(jsonSchemaToTs, "jsonSchemaToTs");
195
+ function primitiveToTs(type) {
196
+ switch (type) {
197
+ case "string":
198
+ return "string";
199
+ case "number":
200
+ case "integer":
201
+ return "number";
202
+ case "boolean":
203
+ return "boolean";
204
+ case "null":
205
+ return "null";
206
+ case "object":
207
+ return "Record<string, unknown>";
208
+ case "array":
209
+ return "unknown[]";
210
+ default:
211
+ return "unknown";
212
+ }
213
+ }
214
+ __name(primitiveToTs, "primitiveToTs");
215
+ function generateInterface(name, schema) {
216
+ if (!schema.properties || Object.keys(schema.properties).length === 0) {
217
+ return `interface ${name} {}`;
218
+ }
219
+ const lines = [];
220
+ lines.push(`interface ${name} {`);
221
+ for (const [propName, propValue] of Object.entries(schema.properties)) {
222
+ const prop = propValue;
223
+ const isRequired = schema.required?.includes(propName) ?? false;
224
+ const optionalMark = isRequired ? "" : "?";
225
+ const tsType = jsonSchemaToTs(prop, isRequired);
226
+ if (prop.description) {
227
+ lines.push(` /** ${prop.description} */`);
228
+ }
229
+ lines.push(` ${propName}${optionalMark}: ${tsType};`);
230
+ }
231
+ lines.push("}");
232
+ return lines.join("\n");
233
+ }
234
+ __name(generateInterface, "generateInterface");
235
+ function sanitizeIdentifier(name) {
236
+ let sanitized = name.replace(/[^a-zA-Z0-9_]/g, "_");
237
+ if (/^[0-9]/.test(sanitized)) {
238
+ sanitized = "_" + sanitized;
239
+ }
240
+ return sanitized;
241
+ }
242
+ __name(sanitizeIdentifier, "sanitizeIdentifier");
243
+ function normalizeName(name) {
244
+ return name.toLowerCase().replace(/[_-]/g, "");
245
+ }
246
+ __name(normalizeName, "normalizeName");
247
+ function generateFuzzyProxy(targetVarName, contextName) {
248
+ const safeContextName = JSON.stringify(contextName);
249
+ return `new Proxy(${targetVarName}, {
250
+ get(target, prop) {
251
+ if (typeof prop !== 'string') return undefined;
252
+
253
+ // Fast-path: exact match
254
+ if (prop in target) return target[prop];
255
+
256
+ // Fuzzy match: normalize and search
257
+ const normalizedProp = prop.toLowerCase().replace(/[_-]/g, '');
258
+ for (const key of Object.keys(target)) {
259
+ const normalizedKey = key.toLowerCase().replace(/[_-]/g, '');
260
+ if (normalizedKey === normalizedProp) {
261
+ return target[key];
262
+ }
263
+ }
264
+
265
+ // No match found - throw helpful error
266
+ const available = Object.keys(target).join(', ');
267
+ throw new TypeError(\`Property "\${prop}" not found on ${safeContextName}. Available: \${available}\`);
268
+ }
269
+ })`;
270
+ }
271
+ __name(generateFuzzyProxy, "generateFuzzyProxy");
272
+ function toPascalCase(name) {
273
+ return name.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
274
+ }
275
+ __name(toPascalCase, "toPascalCase");
276
+ function generateToolInterface(tool) {
277
+ const interfaceName = `${toPascalCase(tool.name)}Input`;
278
+ if (tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0) {
279
+ return generateInterface(interfaceName, tool.inputSchema);
280
+ }
281
+ return "";
282
+ }
283
+ __name(generateToolInterface, "generateToolInterface");
284
+ function generateMethodDefinition(tool, serverName, bridgePort) {
285
+ const methodName = sanitizeIdentifier(tool.name);
286
+ const interfaceName = `${toPascalCase(tool.name)}Input`;
287
+ const hasInput = tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0;
288
+ const inputParam = hasInput ? `input: ${interfaceName}` : "";
289
+ const inputArg = hasInput ? "input" : "{}";
290
+ const lines = [];
291
+ if (tool.description) {
292
+ lines.push(" /**");
293
+ lines.push(` * ${tool.description}`);
294
+ if (tool.inputSchema?.properties) {
295
+ for (const [propName, propValue] of Object.entries(tool.inputSchema.properties)) {
296
+ const prop = propValue;
297
+ if (prop.description) {
298
+ lines.push(` * @param input.${propName} - ${prop.description}`);
299
+ }
300
+ }
301
+ }
302
+ lines.push(" */");
303
+ }
304
+ const safeServerName = JSON.stringify(serverName);
305
+ const safeToolName = JSON.stringify(tool.name);
306
+ lines.push(` ${methodName}: async (${inputParam}): Promise<unknown> => {`);
307
+ lines.push(` const response = await fetch('http://127.0.0.1:${bridgePort}/call', {`);
308
+ lines.push(` method: 'POST',`);
309
+ lines.push(` headers: { 'Content-Type': 'application/json' },`);
310
+ lines.push(` body: JSON.stringify({`);
311
+ lines.push(` server: ${safeServerName},`);
312
+ lines.push(` tool: ${safeToolName},`);
313
+ lines.push(` args: ${inputArg},`);
314
+ lines.push(` }),`);
315
+ lines.push(` });`);
316
+ lines.push(` if (!response.ok) {`);
317
+ lines.push(" throw new Error(`Tool call failed: ${response.statusText}`);");
318
+ lines.push(` }`);
319
+ lines.push(` const data = await response.json() as { success: boolean; content?: unknown; error?: string };`);
320
+ lines.push(` if (!data.success) {`);
321
+ lines.push(` throw new Error(data.error || 'Tool call failed');`);
322
+ lines.push(` }`);
323
+ lines.push(` // Auto-parse JSON from MCP text content blocks for convenience`);
324
+ lines.push(` const content = data.content;`);
325
+ lines.push(` if (Array.isArray(content) && content.length === 1 && content[0]?.type === 'text') {`);
326
+ lines.push(` try {`);
327
+ lines.push(` return JSON.parse(content[0].text);`);
328
+ lines.push(` } catch {`);
329
+ lines.push(` return content[0].text;`);
330
+ lines.push(` }`);
331
+ lines.push(` }`);
332
+ lines.push(` return content;`);
333
+ lines.push(` },`);
334
+ return lines.join("\n");
335
+ }
336
+ __name(generateMethodDefinition, "generateMethodDefinition");
337
+ function generateToolWrapper(tool, serverName) {
338
+ const funcName = sanitizeIdentifier(tool.name);
339
+ const interfaceName = `${toPascalCase(tool.name)}Input`;
340
+ const lines = [];
341
+ if (tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0) {
342
+ lines.push(generateInterface(interfaceName, tool.inputSchema));
343
+ lines.push("");
344
+ }
345
+ if (tool.description) {
346
+ lines.push("/**");
347
+ lines.push(` * ${tool.description}`);
348
+ if (tool.inputSchema?.properties) {
349
+ lines.push(" *");
350
+ for (const [propName, propValue] of Object.entries(tool.inputSchema.properties)) {
351
+ const prop = propValue;
352
+ if (prop.description) {
353
+ lines.push(` * @param input.${propName} - ${prop.description}`);
354
+ }
355
+ }
356
+ }
357
+ lines.push(" * @returns Promise resolving to tool result");
358
+ lines.push(" */");
359
+ }
360
+ const hasInput = tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0;
361
+ const inputParam = hasInput ? `input: ${interfaceName}` : "";
362
+ const inputArg = hasInput ? "input" : "{}";
363
+ lines.push(`async function ${funcName}(${inputParam}): Promise<unknown> {`);
364
+ lines.push(` const response = await fetch('${BRIDGE_ENDPOINT}', {`);
365
+ lines.push(` method: 'POST',`);
366
+ lines.push(` headers: { 'Content-Type': 'application/json' },`);
367
+ lines.push(` body: JSON.stringify({`);
368
+ lines.push(` server: '${serverName}',`);
369
+ lines.push(` tool: '${tool.name}',`);
370
+ lines.push(` args: ${inputArg},`);
371
+ lines.push(` }),`);
372
+ lines.push(` });`);
373
+ lines.push("");
374
+ lines.push(" if (!response.ok) {");
375
+ lines.push(" throw new Error(`Tool call failed: ${response.statusText}`);");
376
+ lines.push(" }");
377
+ lines.push("");
378
+ lines.push(" return response.json();");
379
+ lines.push("}");
380
+ return lines.join("\n");
381
+ }
382
+ __name(generateToolWrapper, "generateToolWrapper");
383
+ function generateServerModule(tools, serverName, bridgePort = 3e3) {
384
+ const lines = [];
385
+ const namespaceName = sanitizeIdentifier(serverName);
386
+ lines.push("/**");
387
+ lines.push(` * Auto-generated TypeScript wrappers for ${serverName} MCP server tools.`);
388
+ lines.push(` * Case-insensitive: methodName, method_name, and method-name all work.`);
389
+ lines.push(` * Access tools via: ${namespaceName}.methodName()`);
390
+ lines.push(" */");
391
+ lines.push("");
392
+ for (const tool of tools) {
393
+ const interfaceCode = generateToolInterface(tool);
394
+ if (interfaceCode) {
395
+ lines.push(interfaceCode);
396
+ lines.push("");
397
+ }
398
+ }
399
+ lines.push(`const ${namespaceName}_raw = {`);
400
+ for (let i = 0; i < tools.length; i++) {
401
+ const tool = tools[i];
402
+ lines.push(generateMethodDefinition(tool, serverName, bridgePort));
403
+ }
404
+ lines.push("};");
405
+ lines.push("");
406
+ lines.push(`const ${namespaceName} = ${generateFuzzyProxy(`${namespaceName}_raw`, serverName)};`);
407
+ lines.push("");
408
+ return lines.join("\n");
409
+ }
410
+ __name(generateServerModule, "generateServerModule");
411
+ function generateMcpDictionary(serverNames) {
412
+ const lines = [];
413
+ const sanitizedNames = serverNames.map(sanitizeIdentifier);
414
+ lines.push("/**");
415
+ lines.push(" * MCP Server Dictionary - Access all MCP servers via case-agnostic lookup.");
416
+ lines.push(` * Available: ${serverNames.map((n) => `mcp['${n}']`).join(", ")}`);
417
+ lines.push(` * Aliases: ${sanitizedNames.join(", ")}`);
418
+ lines.push(" */");
419
+ lines.push("");
420
+ lines.push("const mcp_servers_raw: Record<string, unknown> = {");
421
+ for (const serverName of serverNames) {
422
+ const sanitized = sanitizeIdentifier(serverName);
423
+ lines.push(` ${JSON.stringify(serverName)}: ${sanitized},`);
424
+ }
425
+ lines.push("};");
426
+ lines.push("");
427
+ lines.push(`const mcp = ${generateFuzzyProxy("mcp_servers_raw", "mcp")};`);
428
+ lines.push("");
429
+ return lines.join("\n");
430
+ }
431
+ __name(generateMcpDictionary, "generateMcpDictionary");
432
+
433
+ // dist/codegen/module-resolver.js
434
+ import { loadServerManifest } from "@justanothermldude/meta-mcp-core";
435
+ function sanitizeFileName(name) {
436
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
437
+ }
438
+ __name(sanitizeFileName, "sanitizeFileName");
439
+ var VirtualModuleResolver = class {
440
+ static {
441
+ __name(this, "VirtualModuleResolver");
442
+ }
443
+ constructor() {
444
+ this.modules = /* @__PURE__ */ new Map();
445
+ }
446
+ /**
447
+ * Register a virtual module at the given path
448
+ * @param path - Virtual path (e.g., ./servers/github/create_issue.ts)
449
+ * @param code - Generated TypeScript code
450
+ */
451
+ registerModule(path, code) {
452
+ this.modules.set(this.normalizePath(path), code);
453
+ }
454
+ /**
455
+ * Resolve a virtual module path to its generated code
456
+ * @param path - Virtual path to resolve
457
+ * @returns Generated TypeScript code or undefined if not found
458
+ */
459
+ resolve(path) {
460
+ return this.modules.get(this.normalizePath(path));
461
+ }
462
+ /**
463
+ * Check if a virtual module exists at the given path
464
+ * @param path - Virtual path to check
465
+ * @returns True if module exists
466
+ */
467
+ has(path) {
468
+ return this.modules.has(this.normalizePath(path));
469
+ }
470
+ /**
471
+ * Clear all registered modules
472
+ */
473
+ clear() {
474
+ this.modules.clear();
475
+ }
476
+ /**
477
+ * Get the number of registered modules
478
+ */
479
+ size() {
480
+ return this.modules.size;
481
+ }
482
+ /**
483
+ * Get all registered module paths
484
+ */
485
+ listModules() {
486
+ return Array.from(this.modules.keys());
487
+ }
488
+ /**
489
+ * Normalize a path for consistent lookup
490
+ */
491
+ normalizePath(path) {
492
+ return path.replace(/^\.\//, "").replace(/\\/g, "/");
493
+ }
494
+ /**
495
+ * Generate the virtual path for a tool
496
+ * @param serverName - Name of the MCP server
497
+ * @param toolName - Name of the tool
498
+ * @returns Virtual path in format servers/<name>/<tool>.ts
499
+ */
500
+ static getToolPath(serverName, toolName) {
501
+ return `servers/${serverName}/${sanitizeFileName(toolName)}.ts`;
502
+ }
503
+ /**
504
+ * Generate the virtual path for a server's index module
505
+ * @param serverName - Name of the MCP server
506
+ * @returns Virtual path in format servers/<name>/index.ts
507
+ */
508
+ static getServerIndexPath(serverName) {
509
+ return `servers/${serverName}/index.ts`;
510
+ }
511
+ };
512
+ async function generateFromManifest(resolver, options) {
513
+ const { toolCache, manifest = loadServerManifest() } = options;
514
+ let moduleCount = 0;
515
+ for (const serverName of Object.keys(manifest.servers)) {
516
+ const tools = toolCache.get(serverName);
517
+ if (!tools || tools.length === 0) {
518
+ continue;
519
+ }
520
+ for (const tool of tools) {
521
+ const path = VirtualModuleResolver.getToolPath(serverName, tool.name);
522
+ const code = generateToolWrapper(tool, serverName);
523
+ resolver.registerModule(path, code);
524
+ moduleCount++;
525
+ }
526
+ const indexPath = VirtualModuleResolver.getServerIndexPath(serverName);
527
+ const indexCode = generateServerIndexModule(serverName, tools);
528
+ resolver.registerModule(indexPath, indexCode);
529
+ moduleCount++;
530
+ }
531
+ return moduleCount;
532
+ }
533
+ __name(generateFromManifest, "generateFromManifest");
534
+ function generateServerIndexModule(serverName, tools) {
535
+ const lines = [];
536
+ lines.push("/**");
537
+ lines.push(` * Auto-generated index for ${serverName} MCP server tools.`);
538
+ lines.push(" * Re-exports all tool wrappers for convenient imports.");
539
+ lines.push(" */");
540
+ lines.push("");
541
+ for (const tool of tools) {
542
+ const fileName = sanitizeFileName(tool.name);
543
+ lines.push(`export * from './${fileName}.js';`);
544
+ }
545
+ return lines.join("\n");
546
+ }
547
+ __name(generateServerIndexModule, "generateServerIndexModule");
548
+ async function createModuleResolver(options) {
549
+ const resolver = new VirtualModuleResolver();
550
+ await generateFromManifest(resolver, options);
551
+ return resolver;
552
+ }
553
+ __name(createModuleResolver, "createModuleResolver");
554
+
555
+ // dist/sandbox/executor.js
556
+ import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
557
+ import { spawn } from "node:child_process";
558
+ import { writeFile, unlink, mkdir } from "node:fs/promises";
559
+ import { join } from "node:path";
560
+ import { tmpdir as tmpdir2 } from "node:os";
561
+ import { randomUUID } from "node:crypto";
562
+ import { transformSync } from "esbuild";
563
+
564
+ // dist/sandbox/config.js
565
+ import { tmpdir } from "node:os";
566
+ var DEFAULT_MCP_BRIDGE_PORT = 3e3;
567
+ function createDefaultNetworkConfig(mcpBridgePort = DEFAULT_MCP_BRIDGE_PORT) {
568
+ return {
569
+ allowedDomains: [`localhost:${mcpBridgePort}`],
570
+ deniedDomains: [],
571
+ allowLocalBinding: true
572
+ };
573
+ }
574
+ __name(createDefaultNetworkConfig, "createDefaultNetworkConfig");
575
+ function createDefaultFilesystemConfig(additionalWritePaths = []) {
576
+ return {
577
+ denyRead: [],
578
+ allowWrite: [tmpdir(), ...additionalWritePaths],
579
+ denyWrite: []
580
+ };
581
+ }
582
+ __name(createDefaultFilesystemConfig, "createDefaultFilesystemConfig");
583
+ function createSandboxRuntimeConfig(options = {}) {
584
+ const { mcpBridgePort = DEFAULT_MCP_BRIDGE_PORT, additionalWritePaths = [], networkConfig, filesystemConfig } = options;
585
+ return {
586
+ network: networkConfig ?? createDefaultNetworkConfig(mcpBridgePort),
587
+ filesystem: filesystemConfig ?? createDefaultFilesystemConfig(additionalWritePaths)
588
+ };
589
+ }
590
+ __name(createSandboxRuntimeConfig, "createSandboxRuntimeConfig");
591
+
592
+ // dist/sandbox/executor.js
593
+ var SandboxExecutor = class {
594
+ static {
595
+ __name(this, "SandboxExecutor");
596
+ }
597
+ constructor(options = {}) {
598
+ this.initialized = false;
599
+ this.executorConfig = options;
600
+ this.config = createSandboxRuntimeConfig(options);
601
+ this.tempDir = join(tmpdir2(), "mcp-exec");
602
+ }
603
+ /**
604
+ * Initialize the sandbox manager with configured restrictions
605
+ */
606
+ async initialize() {
607
+ if (this.initialized) {
608
+ return;
609
+ }
610
+ await mkdir(this.tempDir, { recursive: true });
611
+ await SandboxManager.initialize(
612
+ this.config,
613
+ void 0,
614
+ // No ask callback - deny by default
615
+ this.executorConfig.enableLogMonitor ?? false
616
+ );
617
+ this.initialized = true;
618
+ }
619
+ /**
620
+ * Generate the MCP helper preamble that provides the global `mcp` object
621
+ * for calling MCP tools via the HTTP bridge
622
+ */
623
+ getMcpPreamble() {
624
+ const bridgePort = this.executorConfig.mcpBridgePort ?? 3e3;
625
+ return `
626
+ // ============================================================================
627
+ // MCP-EXEC SANDBOX API
628
+ // ============================================================================
629
+ // Available: mcp.callTool(serverName, toolName, args)
630
+ //
631
+ // Usage:
632
+ // const result = await mcp.callTool('github', 'list_repos', { org: 'foo' });
633
+ // const issues = await mcp.callTool('jira', 'search_issues', { jql: 'project=X' });
634
+ //
635
+ // For long-running queries, prefer async patterns:
636
+ // const job = await mcp.callTool('splunk-async', 'create_search_job', { query: '...' });
637
+ // const results = await mcp.callTool('splunk-async', 'get_search_results', { job_id: job.sid });
638
+ // ============================================================================
639
+
640
+ // MCP helper for calling tools via HTTP bridge
641
+ declare global {
642
+ var mcp: {
643
+ callTool: (server: string, tool: string, args?: Record<string, unknown>) => Promise<unknown[]>;
644
+ };
645
+ }
646
+
647
+ globalThis.mcp = {
648
+ callTool: async (server: string, tool: string, args: Record<string, unknown> = {}) => {
649
+ const response = await fetch('http://127.0.0.1:${bridgePort}/call', {
650
+ method: 'POST',
651
+ headers: { 'Content-Type': 'application/json' },
652
+ body: JSON.stringify({ server, tool, args }),
653
+ });
654
+ const data = await response.json() as { success: boolean; content?: unknown[]; error?: string };
655
+ if (!data.success) {
656
+ throw new Error(data.error || 'MCP tool call failed');
657
+ }
658
+ return data.content || [];
659
+ },
660
+ };
661
+
662
+ `;
663
+ }
664
+ /**
665
+ * Execute TypeScript/JavaScript code in the sandbox
666
+ *
667
+ * @param code - The code to execute
668
+ * @param timeoutMs - Maximum execution time in milliseconds
669
+ * @returns ExecutionResult with output, error, and duration
670
+ */
671
+ async execute(code, timeoutMs) {
672
+ const startTime = Date.now();
673
+ if (!this.initialized) {
674
+ await this.initialize();
675
+ }
676
+ const fileId = randomUUID();
677
+ const tsFilePath = join(this.tempDir, `${fileId}.ts`);
678
+ const jsFilePath = join(this.tempDir, `${fileId}.js`);
679
+ const fullCode = this.getMcpPreamble() + code;
680
+ try {
681
+ await writeFile(tsFilePath, fullCode, "utf-8");
682
+ const transpiled = transformSync(fullCode, {
683
+ loader: "ts",
684
+ format: "esm",
685
+ target: "node20"
686
+ });
687
+ await writeFile(jsFilePath, transpiled.code, "utf-8");
688
+ const abortController = new AbortController();
689
+ const timeoutId = setTimeout(() => {
690
+ abortController.abort();
691
+ }, timeoutMs);
692
+ try {
693
+ const baseCommand = `node "${jsFilePath}"`;
694
+ const sandboxedCommand = await SandboxManager.wrapWithSandbox(
695
+ baseCommand,
696
+ void 0,
697
+ // Use default shell
698
+ void 0,
699
+ // Use default config
700
+ abortController.signal
701
+ );
702
+ const result = await this.executeCommand(sandboxedCommand, abortController.signal);
703
+ clearTimeout(timeoutId);
704
+ return {
705
+ output: result.stdout,
706
+ error: result.stderr || void 0,
707
+ durationMs: Date.now() - startTime
708
+ };
709
+ } catch (execError) {
710
+ clearTimeout(timeoutId);
711
+ if (abortController.signal.aborted) {
712
+ return {
713
+ output: [],
714
+ error: `Execution timed out after ${timeoutMs}ms`,
715
+ durationMs: Date.now() - startTime
716
+ };
717
+ }
718
+ const errorMessage = execError instanceof Error ? execError.message : String(execError);
719
+ const annotatedError = SandboxManager.annotateStderrWithSandboxFailures(`node "${jsFilePath}"`, errorMessage);
720
+ return {
721
+ output: [],
722
+ error: annotatedError,
723
+ durationMs: Date.now() - startTime
724
+ };
725
+ }
726
+ } finally {
727
+ await this.cleanup(tsFilePath, jsFilePath);
728
+ }
729
+ }
730
+ /**
731
+ * Execute a command and capture its output
732
+ */
733
+ executeCommand(command, abortSignal) {
734
+ return new Promise((resolve, reject) => {
735
+ const child = spawn("sh", ["-c", command], {
736
+ signal: abortSignal
737
+ });
738
+ const stdout = [];
739
+ let stderr = "";
740
+ child.stdout.on("data", (data) => {
741
+ const lines = data.toString().split("\n").filter((line) => line.length > 0);
742
+ stdout.push(...lines);
743
+ });
744
+ child.stderr.on("data", (data) => {
745
+ stderr += data.toString();
746
+ });
747
+ child.on("close", (code) => {
748
+ if (code === 0) {
749
+ resolve({ stdout, stderr: stderr || null });
750
+ } else {
751
+ resolve({ stdout, stderr: stderr || `Process exited with code ${code}` });
752
+ }
753
+ });
754
+ child.on("error", (error) => {
755
+ if (error.name === "AbortError") {
756
+ child.kill("SIGKILL");
757
+ reject(new Error("Execution aborted"));
758
+ } else {
759
+ reject(error);
760
+ }
761
+ });
762
+ });
763
+ }
764
+ /**
765
+ * Cleanup temporary files
766
+ */
767
+ async cleanup(...filePaths) {
768
+ await Promise.all(filePaths.map(async (filePath) => {
769
+ try {
770
+ await unlink(filePath);
771
+ } catch {
772
+ }
773
+ }));
774
+ }
775
+ /**
776
+ * Reset the sandbox manager (useful for testing)
777
+ */
778
+ async reset() {
779
+ await SandboxManager.reset();
780
+ this.initialized = false;
781
+ }
782
+ /**
783
+ * Check if sandbox dependencies are available
784
+ */
785
+ checkDependencies() {
786
+ return SandboxManager.checkDependencies();
787
+ }
788
+ /**
789
+ * Check if sandboxing is enabled on this platform
790
+ */
791
+ isSandboxingEnabled() {
792
+ return SandboxManager.isSandboxingEnabled();
793
+ }
794
+ /**
795
+ * Get the current sandbox configuration
796
+ */
797
+ getConfig() {
798
+ return this.config;
799
+ }
800
+ /**
801
+ * Update sandbox configuration (requires re-initialization)
802
+ */
803
+ updateConfig(newConfig) {
804
+ this.executorConfig = { ...this.executorConfig, ...newConfig };
805
+ this.config = createSandboxRuntimeConfig(this.executorConfig);
806
+ SandboxManager.updateConfig(this.config);
807
+ }
808
+ };
809
+
810
+ // dist/bridge/server.js
811
+ import { createServer } from "http";
812
+ import getPort from "get-port";
813
+ import { getServerConfig, listServers as listServers2 } from "@justanothermldude/meta-mcp-core";
814
+
815
+ // dist/bridge/port-cleanup.js
816
+ import { execSync } from "child_process";
817
+ function isMcpExecProcess(pid) {
818
+ try {
819
+ const cmdline = execSync(`ps -p ${pid} -o command=`, { encoding: "utf8" }).trim();
820
+ return cmdline.includes("mcp-exec") || cmdline.includes("meta-mcp");
821
+ } catch {
822
+ return false;
823
+ }
824
+ }
825
+ __name(isMcpExecProcess, "isMcpExecProcess");
826
+ function getProcessesOnPort(port) {
827
+ try {
828
+ const result = execSync(`lsof -ti :${port}`, { encoding: "utf8" }).trim();
829
+ if (!result)
830
+ return [];
831
+ return result.split("\n").filter(Boolean).map((pid) => parseInt(pid, 10)).filter((pid) => !isNaN(pid));
832
+ } catch {
833
+ return [];
834
+ }
835
+ }
836
+ __name(getProcessesOnPort, "getProcessesOnPort");
837
+ async function cleanupStaleProcess(port) {
838
+ const pids = getProcessesOnPort(port);
839
+ if (pids.length === 0)
840
+ return false;
841
+ let killedAny = false;
842
+ for (const pid of pids) {
843
+ if (isMcpExecProcess(pid)) {
844
+ try {
845
+ process.kill(pid, "SIGTERM");
846
+ killedAny = true;
847
+ } catch {
848
+ }
849
+ }
850
+ }
851
+ if (killedAny) {
852
+ await new Promise((resolve) => setTimeout(resolve, 100));
853
+ }
854
+ return killedAny;
855
+ }
856
+ __name(cleanupStaleProcess, "cleanupStaleProcess");
857
+ function isPortInUse(port) {
858
+ return getProcessesOnPort(port).length > 0;
859
+ }
860
+ __name(isPortInUse, "isPortInUse");
861
+
862
+ // dist/bridge/server.js
863
+ var DEFAULT_PORT = 3e3;
864
+ var DEFAULT_HOST = "127.0.0.1";
865
+ var MAX_REQUEST_BODY_SIZE = 10 * 1024 * 1024;
866
+ function stringSimilarity(a, b) {
867
+ const aLower = a.toLowerCase();
868
+ const bLower = b.toLowerCase();
869
+ if (aLower === bLower)
870
+ return 1;
871
+ if (aLower.includes(bLower) || bLower.includes(aLower)) {
872
+ return 0.8;
873
+ }
874
+ let prefixLen = 0;
875
+ const minLen = Math.min(aLower.length, bLower.length);
876
+ for (let i = 0; i < minLen; i++) {
877
+ if (aLower[i] === bLower[i])
878
+ prefixLen++;
879
+ else
880
+ break;
881
+ }
882
+ let suffixLen = 0;
883
+ for (let i = 0; i < minLen; i++) {
884
+ if (aLower[aLower.length - 1 - i] === bLower[bLower.length - 1 - i])
885
+ suffixLen++;
886
+ else
887
+ break;
888
+ }
889
+ const maxLen = Math.max(aLower.length, bLower.length);
890
+ return (prefixLen + suffixLen) / (2 * maxLen);
891
+ }
892
+ __name(stringSimilarity, "stringSimilarity");
893
+ function findClosestMatch(target, candidates) {
894
+ if (candidates.length === 0)
895
+ return null;
896
+ let bestMatch = null;
897
+ let bestScore = 0;
898
+ const threshold = 0.3;
899
+ for (const candidate of candidates) {
900
+ const score = stringSimilarity(target, candidate);
901
+ if (score > bestScore && score >= threshold) {
902
+ bestScore = score;
903
+ bestMatch = candidate;
904
+ }
905
+ }
906
+ return bestMatch;
907
+ }
908
+ __name(findClosestMatch, "findClosestMatch");
909
+ function buildServerNotFoundError(requestedServer) {
910
+ const servers = listServers2();
911
+ const serverNames = servers.map((s) => s.name);
912
+ let errorMsg = `Server '${requestedServer}' not found.`;
913
+ if (serverNames.length === 0) {
914
+ errorMsg += " No servers are configured. Check that servers.json is properly set up.";
915
+ return errorMsg;
916
+ }
917
+ const displayServers = serverNames.slice(0, 5);
918
+ const hasMore = serverNames.length > 5;
919
+ errorMsg += ` Available: ${displayServers.join(", ")}${hasMore ? ` (+${serverNames.length - 5} more)` : ""}.`;
920
+ const closest = findClosestMatch(requestedServer, serverNames);
921
+ if (closest) {
922
+ errorMsg += ` Did you mean '${closest}'?`;
923
+ }
924
+ return errorMsg;
925
+ }
926
+ __name(buildServerNotFoundError, "buildServerNotFoundError");
927
+ function buildToolNotFoundError(serverName, requestedTool, availableTools) {
928
+ let errorMsg = `Tool '${requestedTool}' not found on server '${serverName}'.`;
929
+ if (availableTools.length === 0) {
930
+ errorMsg += " No tools available on this server.";
931
+ return errorMsg;
932
+ }
933
+ const displayTools = availableTools.slice(0, 5);
934
+ const hasMore = availableTools.length > 5;
935
+ errorMsg += ` Available tools: ${displayTools.join(", ")}${hasMore ? ` (+${availableTools.length - 5} more)` : ""}.`;
936
+ const closest = findClosestMatch(requestedTool, availableTools);
937
+ if (closest) {
938
+ errorMsg += ` Did you mean '${closest}'?`;
939
+ }
940
+ return errorMsg;
941
+ }
942
+ __name(buildToolNotFoundError, "buildToolNotFoundError");
943
+ function buildConnectionError(serverName, originalError) {
944
+ let errorMsg = `Failed to connect to server '${serverName}': ${originalError}`;
945
+ const hints = [];
946
+ if (originalError.includes("ENOENT") || originalError.includes("not found")) {
947
+ hints.push("Check that the server command/binary exists and is in PATH");
948
+ }
949
+ if (originalError.includes("ECONNREFUSED")) {
950
+ hints.push("Check that the server process is running");
951
+ }
952
+ if (originalError.includes("timeout") || originalError.includes("Timeout")) {
953
+ hints.push("Server may be slow to start - try increasing timeout in servers.json");
954
+ }
955
+ if (originalError.includes("spawn")) {
956
+ hints.push("Verify the server configuration in servers.json");
957
+ }
958
+ hints.push("Is the server configured in servers.json?");
959
+ if (hints.length > 0) {
960
+ errorMsg += ` Troubleshooting: ${hints.join("; ")}.`;
961
+ }
962
+ return errorMsg;
963
+ }
964
+ __name(buildConnectionError, "buildConnectionError");
965
+ var MCPBridge = class {
966
+ static {
967
+ __name(this, "MCPBridge");
968
+ }
969
+ constructor(pool, config = {}) {
970
+ this.server = null;
971
+ this.pool = pool;
972
+ this.preferredPort = config.port ?? DEFAULT_PORT;
973
+ this.port = this.preferredPort;
974
+ this.host = config.host ?? DEFAULT_HOST;
975
+ }
976
+ /**
977
+ * Start the HTTP bridge server with dynamic port allocation
978
+ * Prefers the configured port but falls back to any available port
979
+ * @returns Promise that resolves when server is listening
980
+ */
981
+ async start() {
982
+ this.port = await getPort({ port: this.preferredPort });
983
+ return new Promise((resolve, reject) => {
984
+ this.server = createServer((req, res) => {
985
+ this.handleRequest(req, res);
986
+ });
987
+ this.server.on("error", async (err) => {
988
+ if (err.code === "EADDRINUSE") {
989
+ const cleaned = await cleanupStaleProcess(this.port);
990
+ if (cleaned) {
991
+ await new Promise((r) => setTimeout(r, 200));
992
+ this.port = await getPort({ port: this.preferredPort });
993
+ try {
994
+ this.server?.listen(this.port, this.host, () => {
995
+ resolve();
996
+ });
997
+ return;
998
+ } catch {
999
+ }
1000
+ }
1001
+ }
1002
+ reject(err);
1003
+ });
1004
+ this.server.listen(this.port, this.host, () => {
1005
+ resolve();
1006
+ });
1007
+ });
1008
+ }
1009
+ /**
1010
+ * Stop the HTTP bridge server
1011
+ * @returns Promise that resolves when server is closed
1012
+ */
1013
+ async stop() {
1014
+ return new Promise((resolve, reject) => {
1015
+ if (!this.server) {
1016
+ resolve();
1017
+ return;
1018
+ }
1019
+ this.server.close((err) => {
1020
+ if (err) {
1021
+ reject(err);
1022
+ } else {
1023
+ this.server = null;
1024
+ resolve();
1025
+ }
1026
+ });
1027
+ });
1028
+ }
1029
+ /**
1030
+ * Get the port the server is listening on
1031
+ */
1032
+ getPort() {
1033
+ return this.port;
1034
+ }
1035
+ /**
1036
+ * Get the host the server is bound to
1037
+ */
1038
+ getHost() {
1039
+ return this.host;
1040
+ }
1041
+ /**
1042
+ * Check if the server is running
1043
+ */
1044
+ isRunning() {
1045
+ return this.server !== null && this.server.listening;
1046
+ }
1047
+ /**
1048
+ * Handle incoming HTTP requests
1049
+ */
1050
+ handleRequest(req, res) {
1051
+ res.setHeader("Content-Type", "application/json");
1052
+ res.setHeader("Access-Control-Allow-Origin", `http://${this.host}:${this.port}`);
1053
+ res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
1054
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
1055
+ if (req.method === "OPTIONS") {
1056
+ res.writeHead(204);
1057
+ res.end();
1058
+ return;
1059
+ }
1060
+ if (req.method === "POST" && req.url === "/call") {
1061
+ this.handleCallRequest(req, res);
1062
+ } else if (req.method === "GET" && req.url === "/health") {
1063
+ this.handleHealthRequest(res);
1064
+ } else {
1065
+ this.sendError(res, 404, "Not Found");
1066
+ }
1067
+ }
1068
+ /**
1069
+ * Handle POST /call endpoint for tool invocation
1070
+ */
1071
+ handleCallRequest(req, res) {
1072
+ let body = "";
1073
+ let bodySize = 0;
1074
+ req.on("data", (chunk) => {
1075
+ bodySize += chunk.length;
1076
+ if (bodySize > MAX_REQUEST_BODY_SIZE) {
1077
+ req.destroy();
1078
+ this.sendError(res, 413, `Request body too large. Maximum size is ${MAX_REQUEST_BODY_SIZE / 1024 / 1024}MB`);
1079
+ return;
1080
+ }
1081
+ body += chunk.toString();
1082
+ });
1083
+ req.on("end", async () => {
1084
+ try {
1085
+ let request;
1086
+ try {
1087
+ request = JSON.parse(body);
1088
+ } catch {
1089
+ this.sendError(res, 400, "Invalid JSON body");
1090
+ return;
1091
+ }
1092
+ if (!request.server || typeof request.server !== "string") {
1093
+ this.sendError(res, 400, 'Missing or invalid "server" field');
1094
+ return;
1095
+ }
1096
+ if (!request.tool || typeof request.tool !== "string") {
1097
+ this.sendError(res, 400, 'Missing or invalid "tool" field');
1098
+ return;
1099
+ }
1100
+ if (request.args !== void 0 && (typeof request.args !== "object" || request.args === null || Array.isArray(request.args))) {
1101
+ this.sendError(res, 400, '"args" must be an object');
1102
+ return;
1103
+ }
1104
+ let connection;
1105
+ try {
1106
+ connection = await this.pool.getConnection(request.server);
1107
+ } catch (err) {
1108
+ const errorMsg = err instanceof Error ? err.message : String(err);
1109
+ if (errorMsg.includes("not found") || errorMsg.includes("No server configured") || errorMsg.includes("Unknown server")) {
1110
+ this.sendError(res, 404, buildServerNotFoundError(request.server));
1111
+ } else {
1112
+ this.sendError(res, 502, buildConnectionError(request.server, errorMsg));
1113
+ }
1114
+ return;
1115
+ }
1116
+ try {
1117
+ const serverConfig = getServerConfig(request.server);
1118
+ const defaultTimeout = process.env.MCP_DEFAULT_TIMEOUT ? parseInt(process.env.MCP_DEFAULT_TIMEOUT, 10) : void 0;
1119
+ const timeout = serverConfig?.timeout ?? defaultTimeout;
1120
+ const result = await connection.client.callTool(
1121
+ {
1122
+ name: request.tool,
1123
+ arguments: request.args ?? {}
1124
+ },
1125
+ void 0,
1126
+ // resultSchema
1127
+ timeout ? { timeout } : void 0
1128
+ );
1129
+ const response = {
1130
+ success: true,
1131
+ content: result.content,
1132
+ isError: result.isError
1133
+ };
1134
+ res.writeHead(200);
1135
+ res.end(JSON.stringify(response));
1136
+ } catch (err) {
1137
+ const errorMsg = err instanceof Error ? err.message : String(err);
1138
+ if (errorMsg.includes("not found") || errorMsg.includes("Unknown tool") || errorMsg.includes("no such tool")) {
1139
+ try {
1140
+ const tools = await connection.getTools();
1141
+ const toolNames = tools.map((t) => t.name);
1142
+ this.sendError(res, 404, buildToolNotFoundError(request.server, request.tool, toolNames));
1143
+ } catch {
1144
+ this.sendError(res, 500, `Tool '${request.tool}' not found on server '${request.server}'. Unable to fetch available tools.`);
1145
+ }
1146
+ } else {
1147
+ this.sendError(res, 500, `Tool execution failed: ${errorMsg}`);
1148
+ }
1149
+ } finally {
1150
+ this.pool.releaseConnection(request.server);
1151
+ }
1152
+ } catch (err) {
1153
+ const errorMsg = err instanceof Error ? err.message : String(err);
1154
+ this.sendError(res, 500, `Internal error: ${errorMsg}`);
1155
+ }
1156
+ });
1157
+ req.on("error", (err) => {
1158
+ this.sendError(res, 400, `Request error: ${err.message}`);
1159
+ });
1160
+ }
1161
+ /**
1162
+ * Handle GET /health endpoint
1163
+ */
1164
+ handleHealthRequest(res) {
1165
+ const response = {
1166
+ status: "ok",
1167
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1168
+ };
1169
+ res.writeHead(200);
1170
+ res.end(JSON.stringify(response));
1171
+ }
1172
+ /**
1173
+ * Send an error response
1174
+ */
1175
+ sendError(res, statusCode, message) {
1176
+ const response = {
1177
+ success: false,
1178
+ error: message
1179
+ };
1180
+ res.writeHead(statusCode);
1181
+ res.end(JSON.stringify(response));
1182
+ }
1183
+ };
1184
+
1185
+ // dist/types/execution.js
1186
+ var DEFAULT_TIMEOUT_MS = 3e4;
1187
+
1188
+ // dist/tools/execute-with-wrappers.js
1189
+ var executeCodeWithWrappersTool = {
1190
+ name: "execute_code_with_wrappers",
1191
+ description: 'Execute TypeScript/JavaScript code with auto-generated typed wrappers for specified MCP servers. Provides a typed API like github.createIssue({ title: "..." }) instead of raw mcp.callTool(). Multi-line code is supported - format naturally for readability.',
1192
+ inputSchema: {
1193
+ type: "object",
1194
+ properties: {
1195
+ code: {
1196
+ type: "string",
1197
+ description: "The TypeScript/JavaScript code to execute. Multi-line supported - format for readability."
1198
+ },
1199
+ wrappers: {
1200
+ type: "array",
1201
+ items: { type: "string" },
1202
+ description: "Array of MCP server names to generate typed wrappers for"
1203
+ },
1204
+ timeout_ms: {
1205
+ type: "number",
1206
+ description: `Maximum execution time in milliseconds (default: ${DEFAULT_TIMEOUT_MS})`
1207
+ }
1208
+ },
1209
+ required: ["code", "wrappers"]
1210
+ }
1211
+ };
1212
+ function isExecuteWithWrappersInput(args2) {
1213
+ return typeof args2 === "object" && args2 !== null && "code" in args2 && typeof args2.code === "string" && "wrappers" in args2 && Array.isArray(args2.wrappers) && args2.wrappers.every((w) => typeof w === "string");
1214
+ }
1215
+ __name(isExecuteWithWrappersInput, "isExecuteWithWrappersInput");
1216
+ function getMcpPreamble(bridgePort) {
1217
+ return `
1218
+ // MCP helper for calling tools via HTTP bridge
1219
+ declare global {
1220
+ var mcp: {
1221
+ callTool: (server: string, tool: string, args?: Record<string, unknown>) => Promise<unknown[]>;
1222
+ };
1223
+ }
1224
+
1225
+ globalThis.mcp = {
1226
+ callTool: async (server: string, tool: string, args: Record<string, unknown> = {}) => {
1227
+ const response = await fetch('http://127.0.0.1:${bridgePort}/call', {
1228
+ method: 'POST',
1229
+ headers: { 'Content-Type': 'application/json' },
1230
+ body: JSON.stringify({ server, tool, args }),
1231
+ });
1232
+ const data = await response.json() as { success: boolean; content?: unknown[]; error?: string };
1233
+ if (!data.success) {
1234
+ throw new Error(data.error || 'MCP tool call failed');
1235
+ }
1236
+ return data.content || [];
1237
+ },
1238
+ };
1239
+
1240
+ `;
1241
+ }
1242
+ __name(getMcpPreamble, "getMcpPreamble");
1243
+ function createExecuteWithWrappersHandler(pool, config = {}) {
1244
+ const preferredPort = config.bridgeConfig?.port ?? 3e3;
1245
+ return /* @__PURE__ */ __name(async function executeWithWrappersHandler(args2) {
1246
+ const { code, wrappers, timeout_ms = DEFAULT_TIMEOUT_MS } = args2;
1247
+ if (!code || typeof code !== "string") {
1248
+ return {
1249
+ content: [{ type: "text", text: "Error: code parameter is required and must be a string" }],
1250
+ isError: true
1251
+ };
1252
+ }
1253
+ if (!wrappers || !Array.isArray(wrappers)) {
1254
+ return {
1255
+ content: [{ type: "text", text: "Error: wrappers parameter is required and must be an array of strings" }],
1256
+ isError: true
1257
+ };
1258
+ }
1259
+ if (wrappers.length === 0) {
1260
+ return {
1261
+ content: [{ type: "text", text: "Error: wrappers array must contain at least one server name" }],
1262
+ isError: true
1263
+ };
1264
+ }
1265
+ if (timeout_ms !== void 0 && (typeof timeout_ms !== "number" || timeout_ms <= 0)) {
1266
+ return {
1267
+ content: [{ type: "text", text: "Error: timeout_ms must be a positive number" }],
1268
+ isError: true
1269
+ };
1270
+ }
1271
+ let result = null;
1272
+ const bridge = new MCPBridge(pool, {
1273
+ ...config.bridgeConfig,
1274
+ port: preferredPort
1275
+ });
1276
+ try {
1277
+ await bridge.start();
1278
+ const actualPort = bridge.getPort();
1279
+ const wrapperModules = [];
1280
+ for (const serverName of wrappers) {
1281
+ try {
1282
+ const connection = await pool.getConnection(serverName);
1283
+ const tools = await connection.getTools();
1284
+ const moduleCode = generateServerModule(tools, serverName, actualPort);
1285
+ wrapperModules.push(moduleCode);
1286
+ pool.releaseConnection(serverName);
1287
+ } catch (serverError) {
1288
+ const errorMessage = serverError instanceof Error ? serverError.message : String(serverError);
1289
+ await bridge.stop();
1290
+ return {
1291
+ content: [{ type: "text", text: `Error generating wrapper for server '${serverName}': ${errorMessage}` }],
1292
+ isError: true
1293
+ };
1294
+ }
1295
+ }
1296
+ const generatedWrappers = wrapperModules.join("\n\n");
1297
+ const mcpDictionary = generateMcpDictionary(wrappers);
1298
+ const mcpPreamble = getMcpPreamble(actualPort);
1299
+ const fullCode = `${generatedWrappers}
1300
+
1301
+ ${mcpDictionary}
1302
+
1303
+ ${mcpPreamble}
1304
+ ${code}`;
1305
+ const sandboxConfig = {
1306
+ ...config.sandboxConfig,
1307
+ mcpBridgePort: actualPort
1308
+ };
1309
+ const executor = new SandboxExecutor(sandboxConfig);
1310
+ result = await executor.execute(fullCode, timeout_ms);
1311
+ await bridge.stop();
1312
+ return formatResult(result);
1313
+ } catch (error) {
1314
+ try {
1315
+ if (bridge.isRunning()) {
1316
+ await bridge.stop();
1317
+ }
1318
+ } catch {
1319
+ }
1320
+ const errorMessage = error instanceof Error ? error.message : String(error);
1321
+ return formatErrorResult(errorMessage, result);
1322
+ }
1323
+ }, "executeWithWrappersHandler");
1324
+ }
1325
+ __name(createExecuteWithWrappersHandler, "createExecuteWithWrappersHandler");
1326
+ function formatResult(result) {
1327
+ const lines = [];
1328
+ if (result.output.length > 0) {
1329
+ lines.push(...result.output);
1330
+ }
1331
+ if (result.error) {
1332
+ lines.push(`[stderr]: ${result.error}`);
1333
+ }
1334
+ lines.push(`[Execution completed in ${result.durationMs}ms]`);
1335
+ const hasError = !!result.error;
1336
+ return {
1337
+ content: [{ type: "text", text: lines.join("\n") }],
1338
+ isError: hasError
1339
+ };
1340
+ }
1341
+ __name(formatResult, "formatResult");
1342
+ function formatErrorResult(errorMessage, partialResult) {
1343
+ const lines = [];
1344
+ if (partialResult?.output.length) {
1345
+ lines.push("[Partial output]:");
1346
+ lines.push(...partialResult.output);
1347
+ lines.push("");
1348
+ }
1349
+ lines.push(`Error: ${errorMessage}`);
1350
+ if (partialResult?.durationMs) {
1351
+ lines.push(`[Execution failed after ${partialResult.durationMs}ms]`);
1352
+ }
1353
+ return {
1354
+ content: [{ type: "text", text: lines.join("\n") }],
1355
+ isError: true
1356
+ };
1357
+ }
1358
+ __name(formatErrorResult, "formatErrorResult");
1359
+
1360
+ // dist/server.js
1361
+ var VERSION = "0.1.0";
1362
+ function createMcpExecServer(pool, config = {}) {
1363
+ const server = new Server({
1364
+ name: "mcp-exec",
1365
+ version: VERSION
1366
+ }, {
1367
+ capabilities: {
1368
+ tools: {}
1369
+ }
1370
+ });
1371
+ const listServersHandler = createListServersHandler();
1372
+ const getToolSchemaHandler = createGetToolSchemaHandler(pool);
1373
+ const executeWithWrappersHandler = createExecuteWithWrappersHandler(pool, config.handlerConfig);
1374
+ const tools = [
1375
+ listAvailableMcpServersTool,
1376
+ getMcpToolSchemaTool,
1377
+ executeCodeWithWrappersTool
1378
+ ];
1379
+ const listToolsHandler = /* @__PURE__ */ __name(async () => ({ tools }), "listToolsHandler");
1380
+ const callToolRequestHandler = /* @__PURE__ */ __name(async (params) => {
1381
+ const { name, arguments: args2 = {} } = params;
1382
+ switch (name) {
1383
+ case "list_available_mcp_servers": {
1384
+ if (!isListServersInput(args2)) {
1385
+ return {
1386
+ content: [{ type: "text", text: "Error: Invalid arguments for list_available_mcp_servers" }],
1387
+ isError: true
1388
+ };
1389
+ }
1390
+ const result = await listServersHandler(args2);
1391
+ return {
1392
+ content: result.content,
1393
+ isError: result.isError
1394
+ };
1395
+ }
1396
+ case "get_mcp_tool_schema": {
1397
+ if (!isGetToolSchemaInput(args2)) {
1398
+ return {
1399
+ content: [{ type: "text", text: "Error: Invalid arguments for get_mcp_tool_schema. Required: server (string), tool (string)" }],
1400
+ isError: true
1401
+ };
1402
+ }
1403
+ const result = await getToolSchemaHandler(args2);
1404
+ return {
1405
+ content: result.content,
1406
+ isError: result.isError
1407
+ };
1408
+ }
1409
+ case "execute_code_with_wrappers": {
1410
+ if (!isExecuteWithWrappersInput(args2)) {
1411
+ return {
1412
+ content: [{ type: "text", text: "Error: Invalid arguments for execute_code_with_wrappers. Required: code (string), wrappers (string[])" }],
1413
+ isError: true
1414
+ };
1415
+ }
1416
+ const result = await executeWithWrappersHandler(args2);
1417
+ return {
1418
+ content: result.content,
1419
+ isError: result.isError
1420
+ };
1421
+ }
1422
+ default:
1423
+ return {
1424
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
1425
+ isError: true
1426
+ };
1427
+ }
1428
+ }, "callToolRequestHandler");
1429
+ server.setRequestHandler(ListToolsRequestSchema, listToolsHandler);
1430
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1431
+ return callToolRequestHandler(request.params);
1432
+ });
1433
+ const shutdown = /* @__PURE__ */ __name(async () => {
1434
+ }, "shutdown");
1435
+ return {
1436
+ server,
1437
+ listToolsHandler,
1438
+ callToolHandler: callToolRequestHandler,
1439
+ shutdown
1440
+ };
1441
+ }
1442
+ __name(createMcpExecServer, "createMcpExecServer");
1443
+
1444
+ // dist/index.js
1445
+ import { ServerPool, createConnection, getServerConfig as getServerConfig2, loadServerManifest as loadServerManifest2 } from "@justanothermldude/meta-mcp-core";
1446
+ var APP_NAME = "mcp-exec";
1447
+ var VERSION2 = "1.2.0";
1448
+ var defaultExecutor = null;
31
1449
  function getDefaultExecutor() {
32
- if (!defaultExecutor) {
33
- defaultExecutor = new SandboxExecutor();
34
- }
35
- return defaultExecutor;
36
- }
37
- /**
38
- * Execute code in a sandboxed environment using @anthropic-ai/sandbox-runtime.
39
- * Provides OS-level isolation (sandbox-exec on macOS, bubblewrap on Linux).
40
- *
41
- * @param input - The code execution input parameters
42
- * @returns Promise resolving to execution result
43
- */
44
- export async function executeCode(input) {
45
- const { code, timeout_ms = DEFAULT_TIMEOUT_MS } = input;
46
- const executor = getDefaultExecutor();
47
- return executor.execute(code, timeout_ms);
48
- }
49
- /**
50
- * Create a new SandboxExecutor with custom configuration
51
- * @param config - Configuration options for the sandbox
52
- * @returns New SandboxExecutor instance
53
- */
54
- export function createExecutor(config) {
55
- return new SandboxExecutor(config);
56
- }
57
- // Handle --version and --help flags
58
- const args = process.argv.slice(2);
59
- if (args.includes('--version') || args.includes('-v')) {
60
- console.log(VERSION);
61
- process.exit(0);
1450
+ if (!defaultExecutor) {
1451
+ defaultExecutor = new SandboxExecutor();
1452
+ }
1453
+ return defaultExecutor;
1454
+ }
1455
+ __name(getDefaultExecutor, "getDefaultExecutor");
1456
+ async function executeCode(input) {
1457
+ const { code, timeout_ms = DEFAULT_TIMEOUT_MS } = input;
1458
+ const executor = getDefaultExecutor();
1459
+ return executor.execute(code, timeout_ms);
1460
+ }
1461
+ __name(executeCode, "executeCode");
1462
+ function createExecutor(config) {
1463
+ return new SandboxExecutor(config);
1464
+ }
1465
+ __name(createExecutor, "createExecutor");
1466
+ var args = process.argv.slice(2);
1467
+ if (args.includes("--version") || args.includes("-v")) {
1468
+ console.log(VERSION2);
1469
+ process.exit(0);
62
1470
  }
63
- if (args.includes('--help') || args.includes('-h')) {
64
- console.log(`${APP_NAME} v${VERSION}
1471
+ if (args.includes("--help") || args.includes("-h")) {
1472
+ console.log(`${APP_NAME} v${VERSION2}
65
1473
 
66
1474
  An MCP server for executing TypeScript/JavaScript code in sandboxed environments
67
1475
  with access to MCP tools via HTTP bridge.
@@ -75,51 +1483,76 @@ Environment:
75
1483
  SERVERS_CONFIG Path to servers.json config file
76
1484
  Default: ~/.meta-mcp/servers.json
77
1485
  `);
78
- process.exit(0);
1486
+ process.exit(0);
79
1487
  }
80
1488
  async function main() {
81
- // Load config on startup
82
- const configPath = process.env.SERVERS_CONFIG;
83
- if (configPath) {
84
- process.stderr.write(`Loading config from: ${configPath}\n`);
85
- }
86
- // Load server manifest (required before getServerConfig works)
87
- loadServerManifest();
88
- // Create connection factory with Gateway auth support
89
- const connectionFactory = async (serverId) => {
90
- const config = getServerConfig(serverId);
91
- if (!config) {
92
- throw new Error(`Server config not found: ${serverId}`);
93
- }
94
- return createConnection({ ...config, name: serverId }, {
95
- gatewayAuth: { useCursorToken: true }
96
- });
97
- };
98
- // Initialize pool
99
- const pool = new ServerPool(connectionFactory);
100
- // Create server
101
- const { server, shutdown } = createMcpExecServer(pool);
102
- // Graceful shutdown handlers
103
- const handleShutdown = async () => {
104
- process.stderr.write('Shutting down...\n');
105
- await shutdown();
106
- await pool.shutdown();
107
- await server.close();
108
- process.exit(0);
109
- };
110
- process.on('SIGINT', handleShutdown);
111
- process.on('SIGTERM', handleShutdown);
112
- // Connect via stdio
113
- const transport = new StdioServerTransport();
114
- await server.connect(transport);
115
- process.stderr.write('mcp-exec server running on stdio\n');
116
- }
117
- // Only run main if this is the entry point (not when imported as a module)
118
- const isMainModule = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('mcp-exec');
119
- if (isMainModule && !args.includes('--no-server')) {
120
- main().catch((error) => {
121
- console.error('Fatal error:', error);
122
- process.exit(1);
1489
+ const configPath = process.env.SERVERS_CONFIG;
1490
+ if (configPath) {
1491
+ process.stderr.write(`Loading config from: ${configPath}
1492
+ `);
1493
+ }
1494
+ loadServerManifest2();
1495
+ const connectionFactory = /* @__PURE__ */ __name(async (serverId) => {
1496
+ const config = getServerConfig2(serverId);
1497
+ if (!config) {
1498
+ throw new Error(`Server config not found: ${serverId}`);
1499
+ }
1500
+ return createConnection({ ...config, name: serverId }, {
1501
+ gatewayAuth: { useCursorToken: true }
123
1502
  });
1503
+ }, "connectionFactory");
1504
+ const pool = new ServerPool(connectionFactory);
1505
+ const { server, shutdown } = createMcpExecServer(pool);
1506
+ const handleShutdown = /* @__PURE__ */ __name(async () => {
1507
+ process.stderr.write("Shutting down...\n");
1508
+ await shutdown();
1509
+ await pool.shutdown();
1510
+ await server.close();
1511
+ process.exit(0);
1512
+ }, "handleShutdown");
1513
+ process.on("SIGINT", handleShutdown);
1514
+ process.on("SIGTERM", handleShutdown);
1515
+ const transport = new StdioServerTransport();
1516
+ await server.connect(transport);
1517
+ process.stderr.write("mcp-exec server running on stdio\n");
1518
+ }
1519
+ __name(main, "main");
1520
+ var isMainModule = process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("mcp-exec");
1521
+ if (isMainModule && !args.includes("--no-server")) {
1522
+ main().catch((error) => {
1523
+ console.error("Fatal error:", error);
1524
+ process.exit(1);
1525
+ });
124
1526
  }
125
- //# sourceMappingURL=index.js.map
1527
+ export {
1528
+ APP_NAME,
1529
+ DEFAULT_MCP_BRIDGE_PORT,
1530
+ DEFAULT_TIMEOUT_MS,
1531
+ MCPBridge,
1532
+ SandboxExecutor,
1533
+ VERSION2 as VERSION,
1534
+ VirtualModuleResolver,
1535
+ cleanupStaleProcess,
1536
+ createDefaultFilesystemConfig,
1537
+ createDefaultNetworkConfig,
1538
+ createExecuteWithWrappersHandler,
1539
+ createExecutor,
1540
+ createGetToolSchemaHandler,
1541
+ createListServersHandler,
1542
+ createMcpExecServer,
1543
+ createModuleResolver,
1544
+ createSandboxRuntimeConfig,
1545
+ executeCode,
1546
+ executeCodeWithWrappersTool,
1547
+ generateFromManifest,
1548
+ generateMcpDictionary,
1549
+ generateServerModule,
1550
+ generateToolWrapper,
1551
+ getMcpToolSchemaTool,
1552
+ isExecuteWithWrappersInput,
1553
+ isGetToolSchemaInput,
1554
+ isListServersInput,
1555
+ isPortInUse,
1556
+ listAvailableMcpServersTool,
1557
+ normalizeName
1558
+ };