@justanothermldude/mcp-exec 1.3.6 → 1.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/bridge/server.d.ts.map +1 -1
- package/dist/bridge/server.js +83 -36
- package/dist/bridge/server.js.map +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/codegen/index.d.ts.map +1 -1
- package/dist/codegen/index.js +1 -1
- package/dist/codegen/index.js.map +1 -1
- package/dist/codegen/wrapper-generator.d.ts +13 -1
- package/dist/codegen/wrapper-generator.d.ts.map +1 -1
- package/dist/codegen/wrapper-generator.js +186 -19
- package/dist/codegen/wrapper-generator.js.map +1 -1
- package/dist/index.js +359 -83
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/execute-with-wrappers.d.ts +29 -1
- package/dist/tools/execute-with-wrappers.d.ts.map +1 -1
- package/dist/tools/execute-with-wrappers.js +147 -46
- package/dist/tools/execute-with-wrappers.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-servers.d.ts.map +1 -1
- package/dist/tools/list-servers.js +51 -2
- package/dist/tools/list-servers.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,10 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
|
|
|
11
11
|
|
|
12
12
|
// dist/tools/list-servers.js
|
|
13
13
|
import { listServers } from "@justanothermldude/meta-mcp-core";
|
|
14
|
+
function escapeMarkdownCell(text) {
|
|
15
|
+
return text.replace(/\|/g, "\\|");
|
|
16
|
+
}
|
|
17
|
+
__name(escapeMarkdownCell, "escapeMarkdownCell");
|
|
14
18
|
var listAvailableMcpServersTool = {
|
|
15
19
|
name: "list_available_mcp_servers",
|
|
16
20
|
description: "List available MCP servers with their names, descriptions, and tags. Optionally filter by name or tag.",
|
|
@@ -45,8 +49,37 @@ function createListServersHandler() {
|
|
|
45
49
|
return false;
|
|
46
50
|
});
|
|
47
51
|
}
|
|
52
|
+
if (servers.length === 0) {
|
|
53
|
+
const noMatchMsg = filter ? `No servers matched filter: '${filter}'. Try list_available_mcp_servers without a filter.` : "No servers are configured. Check that servers.json is properly set up.";
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: noMatchMsg }],
|
|
56
|
+
isError: false
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const rows = servers.map((server) => {
|
|
60
|
+
const desc = (() => {
|
|
61
|
+
if (!server.description)
|
|
62
|
+
return "";
|
|
63
|
+
if (server.description.length <= 80)
|
|
64
|
+
return server.description;
|
|
65
|
+
let truncated = server.description.substring(0, 77);
|
|
66
|
+
truncated = truncated.replace(/[\uD800-\uDBFF]$/, "");
|
|
67
|
+
return truncated + "...";
|
|
68
|
+
})();
|
|
69
|
+
let tagsStr = "";
|
|
70
|
+
if (server.tags && server.tags.length > 0) {
|
|
71
|
+
const displayTags = server.tags.slice(0, 5);
|
|
72
|
+
const hasMore = server.tags.length > 5;
|
|
73
|
+
tagsStr = displayTags.join(", ") + (hasMore ? ` +${server.tags.length - 5} more` : "");
|
|
74
|
+
}
|
|
75
|
+
const escapedName = escapeMarkdownCell(server.name);
|
|
76
|
+
const escapedDesc = escapeMarkdownCell(desc);
|
|
77
|
+
const escapedTags = escapeMarkdownCell(tagsStr);
|
|
78
|
+
return `| \`${escapedName}\` | ${escapedDesc} | ${escapedTags} |`;
|
|
79
|
+
});
|
|
80
|
+
const table = "## Available MCP Servers\n\n| Name | Description | Tags |\n|------|-------------|------|\n" + rows.join("\n") + '\n\nTo use a server, pass its exact name in the `wrappers` array of `execute_code_with_wrappers`.\nExample: `wrappers: ["adobe-mcp-gateway"]`\n\nTo see tools on a server: `get_mcp_tool_schema({ server: "adobe-mcp-gateway" })`';
|
|
48
81
|
return {
|
|
49
|
-
content: [{ type: "text", text:
|
|
82
|
+
content: [{ type: "text", text: table }],
|
|
50
83
|
isError: false
|
|
51
84
|
};
|
|
52
85
|
} catch (error) {
|
|
@@ -164,12 +197,29 @@ function isGetToolSchemaInput(args2) {
|
|
|
164
197
|
}
|
|
165
198
|
__name(isGetToolSchemaInput, "isGetToolSchemaInput");
|
|
166
199
|
|
|
200
|
+
// dist/tools/execute-with-wrappers.js
|
|
201
|
+
import { listServers as listServers3 } from "@justanothermldude/meta-mcp-core";
|
|
202
|
+
|
|
167
203
|
// dist/codegen/wrapper-generator.js
|
|
168
204
|
var BRIDGE_ENDPOINT = "http://127.0.0.1:3000/call";
|
|
169
|
-
function jsonSchemaToTs(prop, _required = true) {
|
|
205
|
+
function jsonSchemaToTs(prop, _required = true, definitions, visited = /* @__PURE__ */ new Set()) {
|
|
170
206
|
if (!prop) {
|
|
171
207
|
return "unknown";
|
|
172
208
|
}
|
|
209
|
+
if (prop.$ref) {
|
|
210
|
+
const refKey = prop.$ref.replace(/^#\/(definitions|\$defs)\//, "");
|
|
211
|
+
if (definitions && refKey in definitions) {
|
|
212
|
+
if (visited.has(refKey))
|
|
213
|
+
return "unknown";
|
|
214
|
+
const nextVisited = new Set(visited);
|
|
215
|
+
nextVisited.add(refKey);
|
|
216
|
+
return jsonSchemaToTs(definitions[refKey], _required, definitions, nextVisited);
|
|
217
|
+
}
|
|
218
|
+
return "unknown";
|
|
219
|
+
}
|
|
220
|
+
if ("const" in prop && prop.const !== void 0) {
|
|
221
|
+
return JSON.stringify(prop.const);
|
|
222
|
+
}
|
|
173
223
|
if (Array.isArray(prop.type)) {
|
|
174
224
|
const types = prop.type.map((t) => primitiveToTs(t));
|
|
175
225
|
return types.join(" | ");
|
|
@@ -177,20 +227,60 @@ function jsonSchemaToTs(prop, _required = true) {
|
|
|
177
227
|
if (prop.enum) {
|
|
178
228
|
return prop.enum.map((v) => JSON.stringify(v)).join(" | ");
|
|
179
229
|
}
|
|
230
|
+
if (prop.oneOf) {
|
|
231
|
+
const types = prop.oneOf.map((s) => jsonSchemaToTs(s, _required, definitions, visited)).filter((t) => t !== "unknown");
|
|
232
|
+
if (types.length > 0) {
|
|
233
|
+
return types.join(" | ");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (prop.anyOf) {
|
|
237
|
+
const types = prop.anyOf.map((s) => jsonSchemaToTs(s, _required, definitions, visited)).filter((t) => t !== "unknown");
|
|
238
|
+
if (types.length > 0) {
|
|
239
|
+
return types.join(" | ");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (prop.allOf) {
|
|
243
|
+
const types = prop.allOf.map((s) => jsonSchemaToTs(s, _required, definitions, visited)).filter((t) => t !== "unknown");
|
|
244
|
+
if (types.length > 0) {
|
|
245
|
+
const primitives = ["string", "number", "boolean", "null"];
|
|
246
|
+
const incompatible = types.filter((t) => primitives.includes(t)).length > 1;
|
|
247
|
+
if (incompatible)
|
|
248
|
+
return "unknown";
|
|
249
|
+
return types.join(" & ");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
180
252
|
if (prop.type === "object" && prop.properties) {
|
|
181
253
|
const propLines = Object.entries(prop.properties).map(([key, value]) => {
|
|
182
254
|
const isRequired = prop.required?.includes(key) ?? false;
|
|
183
|
-
const tsType = jsonSchemaToTs(value, isRequired);
|
|
255
|
+
const tsType = jsonSchemaToTs(value, isRequired, definitions, visited);
|
|
184
256
|
const optionalMark = isRequired ? "" : "?";
|
|
185
257
|
return `${key}${optionalMark}: ${tsType}`;
|
|
186
258
|
});
|
|
259
|
+
if (prop.additionalProperties === true) {
|
|
260
|
+
propLines.push("[key: string]: unknown");
|
|
261
|
+
} else if (typeof prop.additionalProperties === "object" && prop.additionalProperties !== null) {
|
|
262
|
+
propLines.push("[key: string]: unknown");
|
|
263
|
+
} else if (Object.keys(prop.properties).length === 0 && !prop.additionalProperties) {
|
|
264
|
+
return "Record<string, unknown>";
|
|
265
|
+
}
|
|
187
266
|
return `{ ${propLines.join("; ")} }`;
|
|
188
267
|
}
|
|
268
|
+
if (prop.type === "object") {
|
|
269
|
+
if (prop.additionalProperties === true || typeof prop.additionalProperties === "object" && prop.additionalProperties !== null) {
|
|
270
|
+
return "Record<string, unknown>";
|
|
271
|
+
}
|
|
272
|
+
}
|
|
189
273
|
if (prop.type === "array") {
|
|
190
|
-
const itemType = prop.items ? jsonSchemaToTs(prop.items, true) : "unknown";
|
|
274
|
+
const itemType = prop.items ? jsonSchemaToTs(prop.items, true, definitions, visited) : "unknown";
|
|
191
275
|
return `${itemType}[]`;
|
|
192
276
|
}
|
|
193
|
-
|
|
277
|
+
let result = primitiveToTs(prop.type ?? "unknown");
|
|
278
|
+
if (prop.nullable === true) {
|
|
279
|
+
if (result !== "unknown" && !result.includes("| null") && !result.includes("null |")) {
|
|
280
|
+
result = `${result} | null`;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
194
284
|
}
|
|
195
285
|
__name(jsonSchemaToTs, "jsonSchemaToTs");
|
|
196
286
|
function primitiveToTs(type) {
|
|
@@ -213,7 +303,8 @@ function primitiveToTs(type) {
|
|
|
213
303
|
}
|
|
214
304
|
}
|
|
215
305
|
__name(primitiveToTs, "primitiveToTs");
|
|
216
|
-
function generateInterface(name, schema) {
|
|
306
|
+
function generateInterface(name, schema, definitions) {
|
|
307
|
+
const defs = definitions || schema.definitions;
|
|
217
308
|
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
218
309
|
return `interface ${name} {}`;
|
|
219
310
|
}
|
|
@@ -223,7 +314,7 @@ function generateInterface(name, schema) {
|
|
|
223
314
|
const prop = propValue;
|
|
224
315
|
const isRequired = schema.required?.includes(propName) ?? false;
|
|
225
316
|
const optionalMark = isRequired ? "" : "?";
|
|
226
|
-
const tsType = jsonSchemaToTs(prop, isRequired);
|
|
317
|
+
const tsType = jsonSchemaToTs(prop, isRequired, defs);
|
|
227
318
|
if (prop.description) {
|
|
228
319
|
lines.push(` /** ${sanitizeJsDoc(prop.description)} */`);
|
|
229
320
|
}
|
|
@@ -318,10 +409,10 @@ function toPascalCase(name) {
|
|
|
318
409
|
return name.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
319
410
|
}
|
|
320
411
|
__name(toPascalCase, "toPascalCase");
|
|
321
|
-
function generateToolInterface(tool) {
|
|
412
|
+
function generateToolInterface(tool, rootDefinitions) {
|
|
322
413
|
const interfaceName = `${toPascalCase(tool.name)}Input`;
|
|
323
414
|
if (tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0) {
|
|
324
|
-
return generateInterface(interfaceName, tool.inputSchema);
|
|
415
|
+
return generateInterface(interfaceName, tool.inputSchema, rootDefinitions);
|
|
325
416
|
}
|
|
326
417
|
return "";
|
|
327
418
|
}
|
|
@@ -333,17 +424,41 @@ function generateMethodDefinition(tool, serverName, bridgePort) {
|
|
|
333
424
|
const inputParam = hasInput ? `input: ${interfaceName}` : "";
|
|
334
425
|
const inputArg = hasInput ? "input" : "{}";
|
|
335
426
|
const lines = [];
|
|
427
|
+
const requiredParams = [];
|
|
428
|
+
const optionalParams = [];
|
|
429
|
+
if (tool.inputSchema?.properties) {
|
|
430
|
+
for (const propName of Object.keys(tool.inputSchema.properties)) {
|
|
431
|
+
if (tool.inputSchema.required?.includes(propName)) {
|
|
432
|
+
requiredParams.push(propName);
|
|
433
|
+
} else {
|
|
434
|
+
optionalParams.push(propName);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
336
438
|
if (tool.description) {
|
|
337
439
|
lines.push(" /**");
|
|
338
440
|
lines.push(` * ${sanitizeJsDoc(tool.description)}`);
|
|
339
441
|
if (tool.inputSchema?.properties) {
|
|
340
442
|
for (const [propName, propValue] of Object.entries(tool.inputSchema.properties)) {
|
|
341
443
|
const prop = propValue;
|
|
444
|
+
const isRequired = tool.inputSchema.required?.includes(propName) ?? false;
|
|
445
|
+
const marker = isRequired ? "[required]" : "[optional]";
|
|
342
446
|
if (prop.description) {
|
|
343
|
-
lines.push(` * @param input.${propName} - ${sanitizeJsDoc(prop.description)}`);
|
|
447
|
+
lines.push(` * @param input.${propName} ${marker} - ${sanitizeJsDoc(prop.description)}`);
|
|
448
|
+
} else {
|
|
449
|
+
lines.push(` * @param input.${propName} ${marker}`);
|
|
344
450
|
}
|
|
345
451
|
}
|
|
346
452
|
}
|
|
453
|
+
if (requiredParams.length > 0 || optionalParams.length > 0) {
|
|
454
|
+
lines.push(` * Required: ${requiredParams.length > 0 ? requiredParams.join(", ") : "none"}`);
|
|
455
|
+
if (optionalParams.length > 0) {
|
|
456
|
+
lines.push(` * Optional: ${optionalParams.join(", ")}`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const safeServerName2 = JSON.stringify(serverName);
|
|
460
|
+
const safeToolName2 = JSON.stringify(tool.name);
|
|
461
|
+
lines.push(` * @returns Promise<unknown>. To inspect response shape: get_mcp_tool_schema({ server: ${safeServerName2}, tool: ${safeToolName2} })`);
|
|
347
462
|
lines.push(" */");
|
|
348
463
|
}
|
|
349
464
|
const safeServerName = JSON.stringify(serverName);
|
|
@@ -359,15 +474,15 @@ function generateMethodDefinition(tool, serverName, bridgePort) {
|
|
|
359
474
|
lines.push(` }),`);
|
|
360
475
|
lines.push(` });`);
|
|
361
476
|
lines.push(` if (!response.ok) {`);
|
|
362
|
-
lines.push(
|
|
477
|
+
lines.push(` throw new Error(\`[${safeServerName}.${safeToolName}] HTTP \${response.status}: \${response.statusText}\`);`);
|
|
363
478
|
lines.push(` }`);
|
|
364
479
|
lines.push(` const data = await response.json() as { success: boolean; content?: unknown; error?: string; isError?: boolean };`);
|
|
365
480
|
lines.push(` if (!data.success) {`);
|
|
366
|
-
lines.push(` throw new Error(data.error || 'Tool call failed');`);
|
|
481
|
+
lines.push(` throw new Error(\`[${safeServerName}.${safeToolName}] \${data.error || 'Tool call failed'}\`);`);
|
|
367
482
|
lines.push(` }`);
|
|
368
483
|
lines.push(` if (data.isError) {`);
|
|
369
484
|
lines.push(` const errText = Array.isArray(data.content) && data.content[0]?.text ? data.content[0].text : 'Tool returned isError';`);
|
|
370
|
-
lines.push(` throw new Error(errText);`);
|
|
485
|
+
lines.push(` throw new Error(\`[${safeServerName}.${safeToolName}] \${errText}\`);`);
|
|
371
486
|
lines.push(` }`);
|
|
372
487
|
lines.push(` // Auto-parse JSON from MCP text content blocks for convenience`);
|
|
373
488
|
lines.push(` const content = data.content;`);
|
|
@@ -435,9 +550,9 @@ function generateToolWrapper(tool, serverName) {
|
|
|
435
550
|
return lines.join("\n");
|
|
436
551
|
}
|
|
437
552
|
__name(generateToolWrapper, "generateToolWrapper");
|
|
438
|
-
function generateServerModule(tools, serverName, bridgePort = 3e3) {
|
|
553
|
+
function generateServerModule(tools, serverName, bridgePort = 3e3, variableNameOverride) {
|
|
439
554
|
const lines = [];
|
|
440
|
-
const namespaceName = sanitizeIdentifier(serverName);
|
|
555
|
+
const namespaceName = variableNameOverride ?? sanitizeIdentifier(serverName);
|
|
441
556
|
lines.push("/**");
|
|
442
557
|
lines.push(` * Auto-generated TypeScript wrappers for ${serverName} MCP server tools.`);
|
|
443
558
|
lines.push(` * Case-insensitive: methodName, method_name, and method-name all work.`);
|
|
@@ -446,7 +561,8 @@ function generateServerModule(tools, serverName, bridgePort = 3e3) {
|
|
|
446
561
|
lines.push("");
|
|
447
562
|
lines.push(generateFieldGuard());
|
|
448
563
|
for (const tool of tools) {
|
|
449
|
-
const
|
|
564
|
+
const rootDefs = tool.inputSchema?.definitions ?? tool.inputSchema?.$defs;
|
|
565
|
+
const interfaceCode = generateToolInterface(tool, rootDefs);
|
|
450
566
|
if (interfaceCode) {
|
|
451
567
|
lines.push(interfaceCode);
|
|
452
568
|
lines.push("");
|
|
@@ -464,8 +580,51 @@ function generateServerModule(tools, serverName, bridgePort = 3e3) {
|
|
|
464
580
|
return lines.join("\n");
|
|
465
581
|
}
|
|
466
582
|
__name(generateServerModule, "generateServerModule");
|
|
583
|
+
function generateMcpDictionaryFromMap(serverNames, nameMap) {
|
|
584
|
+
const lines = [];
|
|
585
|
+
lines.push("/**");
|
|
586
|
+
lines.push(" * MCP Server Dictionary - Access all MCP servers via case-agnostic lookup.");
|
|
587
|
+
lines.push(` * Available: ${serverNames.map((n) => `mcp['${n}']`).join(", ")}`);
|
|
588
|
+
lines.push(" */");
|
|
589
|
+
lines.push("");
|
|
590
|
+
lines.push("const mcp_servers_raw: Record<string, unknown> = {");
|
|
591
|
+
for (const serverName of serverNames) {
|
|
592
|
+
const varName = nameMap.get(serverName) ?? sanitizeIdentifier(serverName);
|
|
593
|
+
lines.push(` ${JSON.stringify(serverName)}: ${varName},`);
|
|
594
|
+
}
|
|
595
|
+
lines.push("};");
|
|
596
|
+
lines.push("");
|
|
597
|
+
lines.push(`const mcp = ${generateFuzzyProxy("mcp_servers_raw", "mcp")};`);
|
|
598
|
+
lines.push("");
|
|
599
|
+
return lines.join("\n");
|
|
600
|
+
}
|
|
601
|
+
__name(generateMcpDictionaryFromMap, "generateMcpDictionaryFromMap");
|
|
467
602
|
function generateMcpDictionary(serverNames) {
|
|
468
603
|
const lines = [];
|
|
604
|
+
const sanitizedToOriginal = /* @__PURE__ */ new Map();
|
|
605
|
+
const uniqueNames = /* @__PURE__ */ new Map();
|
|
606
|
+
for (let i = 0; i < serverNames.length; i++) {
|
|
607
|
+
const originalName = serverNames[i];
|
|
608
|
+
const sanitized = sanitizeIdentifier(originalName);
|
|
609
|
+
if (!sanitizedToOriginal.has(sanitized)) {
|
|
610
|
+
sanitizedToOriginal.set(sanitized, []);
|
|
611
|
+
}
|
|
612
|
+
sanitizedToOriginal.get(sanitized).push(originalName);
|
|
613
|
+
}
|
|
614
|
+
let collisionIndex = 0;
|
|
615
|
+
for (const originalName of serverNames) {
|
|
616
|
+
const sanitized = sanitizeIdentifier(originalName);
|
|
617
|
+
const conflictingNames = sanitizedToOriginal.get(sanitized);
|
|
618
|
+
if (conflictingNames.length > 1) {
|
|
619
|
+
const uniqueName = `${sanitized}_${collisionIndex}`;
|
|
620
|
+
uniqueNames.set(originalName, uniqueName);
|
|
621
|
+
lines.push(`// WARNING: Server name collision: '${originalName}' and previous server both sanitize to '${sanitized}'. Using '${uniqueName}'.`);
|
|
622
|
+
collisionIndex++;
|
|
623
|
+
} else {
|
|
624
|
+
uniqueNames.set(originalName, sanitized);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
lines.push("");
|
|
469
628
|
const sanitizedNames = serverNames.map(sanitizeIdentifier);
|
|
470
629
|
lines.push("/**");
|
|
471
630
|
lines.push(" * MCP Server Dictionary - Access all MCP servers via case-agnostic lookup.");
|
|
@@ -475,8 +634,8 @@ function generateMcpDictionary(serverNames) {
|
|
|
475
634
|
lines.push("");
|
|
476
635
|
lines.push("const mcp_servers_raw: Record<string, unknown> = {");
|
|
477
636
|
for (const serverName of serverNames) {
|
|
478
|
-
const
|
|
479
|
-
lines.push(` ${JSON.stringify(serverName)}: ${
|
|
637
|
+
const uniqueName = uniqueNames.get(serverName);
|
|
638
|
+
lines.push(` ${JSON.stringify(serverName)}: ${uniqueName},`);
|
|
480
639
|
}
|
|
481
640
|
lines.push("};");
|
|
482
641
|
lines.push("");
|
|
@@ -1128,45 +1287,70 @@ var MCPBridge = class {
|
|
|
1128
1287
|
handleCallRequest(req, res) {
|
|
1129
1288
|
let body = "";
|
|
1130
1289
|
let bodySize = 0;
|
|
1290
|
+
let responseSent = false;
|
|
1131
1291
|
req.on("data", (chunk) => {
|
|
1132
1292
|
bodySize += chunk.length;
|
|
1133
1293
|
if (bodySize > MAX_REQUEST_BODY_SIZE) {
|
|
1134
1294
|
req.destroy();
|
|
1135
|
-
|
|
1295
|
+
if (!responseSent) {
|
|
1296
|
+
responseSent = true;
|
|
1297
|
+
this.sendError(res, 413, `Request body too large. Maximum size is ${MAX_REQUEST_BODY_SIZE / 1024 / 1024}MB`);
|
|
1298
|
+
}
|
|
1136
1299
|
return;
|
|
1137
1300
|
}
|
|
1138
1301
|
body += chunk.toString();
|
|
1139
1302
|
});
|
|
1140
1303
|
req.on("end", async () => {
|
|
1304
|
+
if (responseSent)
|
|
1305
|
+
return;
|
|
1141
1306
|
try {
|
|
1142
1307
|
let request;
|
|
1143
1308
|
try {
|
|
1144
1309
|
request = JSON.parse(body);
|
|
1145
1310
|
} catch {
|
|
1146
|
-
|
|
1311
|
+
if (!responseSent) {
|
|
1312
|
+
responseSent = true;
|
|
1313
|
+
this.sendError(res, 400, "Invalid JSON body");
|
|
1314
|
+
}
|
|
1147
1315
|
return;
|
|
1148
1316
|
}
|
|
1149
1317
|
if (!request.server || typeof request.server !== "string") {
|
|
1150
|
-
|
|
1318
|
+
if (!responseSent) {
|
|
1319
|
+
responseSent = true;
|
|
1320
|
+
this.sendError(res, 400, 'Missing or invalid "server" field');
|
|
1321
|
+
}
|
|
1151
1322
|
return;
|
|
1152
1323
|
}
|
|
1153
1324
|
if (!request.tool || typeof request.tool !== "string") {
|
|
1154
|
-
|
|
1325
|
+
if (!responseSent) {
|
|
1326
|
+
responseSent = true;
|
|
1327
|
+
this.sendError(res, 400, 'Missing or invalid "tool" field');
|
|
1328
|
+
}
|
|
1155
1329
|
return;
|
|
1156
1330
|
}
|
|
1157
1331
|
if (request.args !== void 0 && (typeof request.args !== "object" || request.args === null || Array.isArray(request.args))) {
|
|
1158
|
-
|
|
1332
|
+
if (!responseSent) {
|
|
1333
|
+
responseSent = true;
|
|
1334
|
+
this.sendError(res, 400, '"args" must be an object');
|
|
1335
|
+
}
|
|
1159
1336
|
return;
|
|
1160
1337
|
}
|
|
1161
1338
|
let connection;
|
|
1162
1339
|
try {
|
|
1163
1340
|
connection = await this.pool.getConnection(request.server);
|
|
1164
1341
|
} catch (err) {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1342
|
+
if (!responseSent) {
|
|
1343
|
+
responseSent = true;
|
|
1344
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1345
|
+
if (errorMsg.includes("not found") || errorMsg.includes("No server configured") || errorMsg.includes("Unknown server")) {
|
|
1346
|
+
this.sendError(res, 404, buildServerNotFoundError(request.server));
|
|
1347
|
+
} else if (errorMsg.includes("403") || errorMsg.includes("Forbidden") || errorMsg.includes("PermissionError") || errorMsg.includes("Unauthorized")) {
|
|
1348
|
+
this.sendError(res, 403, `Authentication failed for server '${request.server}'. Check X-MCP-Client header and backend auth config.`);
|
|
1349
|
+
} else if (errorMsg.includes("503") || errorMsg.includes("Service Unavailable")) {
|
|
1350
|
+
this.sendError(res, 503, `Server '${request.server}' is unavailable. It may be starting up \u2014 retry in a few seconds.`);
|
|
1351
|
+
} else {
|
|
1352
|
+
this.sendError(res, 502, buildConnectionError(request.server, errorMsg));
|
|
1353
|
+
}
|
|
1170
1354
|
}
|
|
1171
1355
|
return;
|
|
1172
1356
|
}
|
|
@@ -1183,36 +1367,52 @@ var MCPBridge = class {
|
|
|
1183
1367
|
// resultSchema
|
|
1184
1368
|
timeout ? { timeout } : void 0
|
|
1185
1369
|
);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1370
|
+
if (!responseSent) {
|
|
1371
|
+
responseSent = true;
|
|
1372
|
+
const response = {
|
|
1373
|
+
success: true,
|
|
1374
|
+
content: result.content,
|
|
1375
|
+
isError: result.isError
|
|
1376
|
+
};
|
|
1377
|
+
res.writeHead(200);
|
|
1378
|
+
res.end(JSON.stringify(response));
|
|
1379
|
+
}
|
|
1193
1380
|
} catch (err) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1381
|
+
if (!responseSent) {
|
|
1382
|
+
responseSent = true;
|
|
1383
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1384
|
+
if (errorMsg.includes("not found") || errorMsg.includes("Unknown tool") || errorMsg.includes("no such tool")) {
|
|
1385
|
+
try {
|
|
1386
|
+
const tools = await connection.getTools();
|
|
1387
|
+
const toolNames = tools.map((t) => t.name);
|
|
1388
|
+
this.sendError(res, 404, buildToolNotFoundError(request.server, request.tool, toolNames));
|
|
1389
|
+
} catch {
|
|
1390
|
+
this.sendError(res, 500, `Tool '${request.tool}' not found on server '${request.server}'. Unable to fetch available tools.`);
|
|
1391
|
+
}
|
|
1392
|
+
} else if (errorMsg.includes("403") || errorMsg.includes("Forbidden") || errorMsg.includes("PermissionError") || errorMsg.includes("Unauthorized")) {
|
|
1393
|
+
this.sendError(res, 403, `Authorization failed calling '${request.tool}' on '${request.server}': ${errorMsg}. Ensure X-MCP-Client header is being sent.`);
|
|
1394
|
+
} else if (errorMsg.includes("503") || errorMsg.includes("504") || errorMsg.includes("Gateway")) {
|
|
1395
|
+
this.sendError(res, 503, `Server '${request.server}' temporarily unavailable during tool '${request.tool}'. Retry after a short delay.`);
|
|
1396
|
+
} else {
|
|
1397
|
+
this.sendError(res, 500, `[${request.server}.${request.tool}] Tool execution failed: ${errorMsg}`);
|
|
1202
1398
|
}
|
|
1203
|
-
} else {
|
|
1204
|
-
this.sendError(res, 500, `Tool execution failed: ${errorMsg}`);
|
|
1205
1399
|
}
|
|
1206
1400
|
} finally {
|
|
1207
1401
|
this.pool.releaseConnection(request.server);
|
|
1208
1402
|
}
|
|
1209
1403
|
} catch (err) {
|
|
1210
|
-
|
|
1211
|
-
|
|
1404
|
+
if (!responseSent) {
|
|
1405
|
+
responseSent = true;
|
|
1406
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1407
|
+
this.sendError(res, 500, `Internal error: ${errorMsg}`);
|
|
1408
|
+
}
|
|
1212
1409
|
}
|
|
1213
1410
|
});
|
|
1214
1411
|
req.on("error", (err) => {
|
|
1215
|
-
|
|
1412
|
+
if (!responseSent) {
|
|
1413
|
+
responseSent = true;
|
|
1414
|
+
this.sendError(res, 400, `Request error: ${err.message}`);
|
|
1415
|
+
}
|
|
1216
1416
|
});
|
|
1217
1417
|
}
|
|
1218
1418
|
/**
|
|
@@ -1266,6 +1466,89 @@ var executeCodeWithWrappersTool = {
|
|
|
1266
1466
|
required: ["code", "wrappers"]
|
|
1267
1467
|
}
|
|
1268
1468
|
};
|
|
1469
|
+
function buildServerListString() {
|
|
1470
|
+
try {
|
|
1471
|
+
const servers = listServers3();
|
|
1472
|
+
if (!servers || servers.length === 0) {
|
|
1473
|
+
return "";
|
|
1474
|
+
}
|
|
1475
|
+
const serverLines = servers.slice(0, 7).map((s) => {
|
|
1476
|
+
const desc = s.description || "No description";
|
|
1477
|
+
return ` \u2022 ${s.name} \u2014 ${desc}`;
|
|
1478
|
+
});
|
|
1479
|
+
const moreCount = Math.max(0, servers.length - 7);
|
|
1480
|
+
const moreStr = moreCount > 0 ? `
|
|
1481
|
+
[+${moreCount} more]` : "";
|
|
1482
|
+
return `
|
|
1483
|
+
Available MCP servers (use exact names in wrappers array):
|
|
1484
|
+
${serverLines.join("\n")}${moreStr}
|
|
1485
|
+
|
|
1486
|
+
Use list_available_mcp_servers to discover tools on each server before calling execute_code_with_wrappers.`;
|
|
1487
|
+
} catch {
|
|
1488
|
+
return "";
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
__name(buildServerListString, "buildServerListString");
|
|
1492
|
+
function createExecuteCodeWithWrappersToolDefinition() {
|
|
1493
|
+
const baseDescription = '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.';
|
|
1494
|
+
const serverList = buildServerListString();
|
|
1495
|
+
const environmentNote = '\n\nExecution environment: Node.js (not browser). Top-level await is supported.\n- Use ES modules syntax (import/export NOT supported \u2014 modules are pre-injected as namespace vars)\n- DO NOT use require(), __dirname, __filename, or browser APIs (window, document, localStorage)\n- Available globals: fetch (Node 18+), console, process, Buffer, setTimeout, clearTimeout\n- Server namespaces are pre-injected: use serverName.toolName(args) directly\n- mcp is the server dictionary: mcp["server-name"].toolName(args) for dynamic lookup\n- Low-level fallback (avoid if typed wrapper exists): globalThis.mcp.callTool(serverName, toolName, args)';
|
|
1496
|
+
return {
|
|
1497
|
+
name: "execute_code_with_wrappers",
|
|
1498
|
+
description: baseDescription + serverList + environmentNote,
|
|
1499
|
+
inputSchema: {
|
|
1500
|
+
type: "object",
|
|
1501
|
+
properties: {
|
|
1502
|
+
code: {
|
|
1503
|
+
type: "string",
|
|
1504
|
+
description: "The TypeScript/JavaScript code to execute. Multi-line supported - format for readability."
|
|
1505
|
+
},
|
|
1506
|
+
wrappers: {
|
|
1507
|
+
type: "array",
|
|
1508
|
+
items: { type: "string" },
|
|
1509
|
+
description: "Array of MCP server names to generate typed wrappers for"
|
|
1510
|
+
},
|
|
1511
|
+
timeout_ms: {
|
|
1512
|
+
type: "number",
|
|
1513
|
+
description: `Maximum execution time in milliseconds (default: ${DEFAULT_TIMEOUT_MS})`
|
|
1514
|
+
}
|
|
1515
|
+
},
|
|
1516
|
+
required: ["code", "wrappers"]
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
__name(createExecuteCodeWithWrappersToolDefinition, "createExecuteCodeWithWrappersToolDefinition");
|
|
1521
|
+
function sanitizeIdentifier2(name) {
|
|
1522
|
+
let s = name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
1523
|
+
if (/^[0-9]/.test(s)) {
|
|
1524
|
+
s = "_" + s;
|
|
1525
|
+
}
|
|
1526
|
+
return s;
|
|
1527
|
+
}
|
|
1528
|
+
__name(sanitizeIdentifier2, "sanitizeIdentifier");
|
|
1529
|
+
function buildUniqueNameMap(names) {
|
|
1530
|
+
const sanitizedToOriginals = /* @__PURE__ */ new Map();
|
|
1531
|
+
for (const name of names) {
|
|
1532
|
+
const s = sanitizeIdentifier2(name);
|
|
1533
|
+
if (!sanitizedToOriginals.has(s)) {
|
|
1534
|
+
sanitizedToOriginals.set(s, []);
|
|
1535
|
+
}
|
|
1536
|
+
sanitizedToOriginals.get(s).push(name);
|
|
1537
|
+
}
|
|
1538
|
+
const nameMap = /* @__PURE__ */ new Map();
|
|
1539
|
+
for (const name of names) {
|
|
1540
|
+
const s = sanitizeIdentifier2(name);
|
|
1541
|
+
const group = sanitizedToOriginals.get(s);
|
|
1542
|
+
if (group.length === 1) {
|
|
1543
|
+
nameMap.set(name, s);
|
|
1544
|
+
} else {
|
|
1545
|
+
const idx = group.indexOf(name);
|
|
1546
|
+
nameMap.set(name, `${s}_${idx}`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return nameMap;
|
|
1550
|
+
}
|
|
1551
|
+
__name(buildUniqueNameMap, "buildUniqueNameMap");
|
|
1269
1552
|
function isExecuteWithWrappersInput(args2) {
|
|
1270
1553
|
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");
|
|
1271
1554
|
}
|
|
@@ -1300,33 +1583,6 @@ ${indent}}`;
|
|
|
1300
1583
|
return lines.join("\n");
|
|
1301
1584
|
}
|
|
1302
1585
|
__name(wrapUserCodeForReturnCapture, "wrapUserCodeForReturnCapture");
|
|
1303
|
-
function getMcpPreamble(bridgePort) {
|
|
1304
|
-
return `
|
|
1305
|
-
// MCP helper for calling tools via HTTP bridge
|
|
1306
|
-
declare global {
|
|
1307
|
-
var mcp: {
|
|
1308
|
-
callTool: (server: string, tool: string, args?: Record<string, unknown>) => Promise<unknown[]>;
|
|
1309
|
-
};
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
globalThis.mcp = {
|
|
1313
|
-
callTool: async (server: string, tool: string, args: Record<string, unknown> = {}) => {
|
|
1314
|
-
const response = await fetch('http://127.0.0.1:${bridgePort}/call', {
|
|
1315
|
-
method: 'POST',
|
|
1316
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1317
|
-
body: JSON.stringify({ server, tool, args }),
|
|
1318
|
-
});
|
|
1319
|
-
const data = await response.json() as { success: boolean; content?: unknown[]; error?: string };
|
|
1320
|
-
if (!data.success) {
|
|
1321
|
-
throw new Error(data.error || 'MCP tool call failed');
|
|
1322
|
-
}
|
|
1323
|
-
return data.content || [];
|
|
1324
|
-
},
|
|
1325
|
-
};
|
|
1326
|
-
|
|
1327
|
-
`;
|
|
1328
|
-
}
|
|
1329
|
-
__name(getMcpPreamble, "getMcpPreamble");
|
|
1330
1586
|
function createExecuteWithWrappersHandler(pool, config = {}) {
|
|
1331
1587
|
const preferredPort = config.bridgeConfig?.port ?? 3e3;
|
|
1332
1588
|
let activeBridge = null;
|
|
@@ -1341,6 +1597,7 @@ function createExecuteWithWrappersHandler(pool, config = {}) {
|
|
|
1341
1597
|
}
|
|
1342
1598
|
__name(stopActiveBridge, "stopActiveBridge");
|
|
1343
1599
|
async function executeWithWrappersHandler(args2) {
|
|
1600
|
+
const TOOLS_FETCH_TIMEOUT_MS = 15e3;
|
|
1344
1601
|
const { code, wrappers, timeout_ms = DEFAULT_TIMEOUT_MS } = args2;
|
|
1345
1602
|
if (!code || typeof code !== "string") {
|
|
1346
1603
|
return {
|
|
@@ -1375,17 +1632,36 @@ function createExecuteWithWrappersHandler(pool, config = {}) {
|
|
|
1375
1632
|
try {
|
|
1376
1633
|
await bridge.start();
|
|
1377
1634
|
const actualPort = bridge.getPort();
|
|
1635
|
+
const uniqueNameMap = buildUniqueNameMap(wrappers);
|
|
1378
1636
|
const wrapperModules = [];
|
|
1379
1637
|
for (const serverName of wrappers) {
|
|
1638
|
+
let connection;
|
|
1380
1639
|
try {
|
|
1381
|
-
|
|
1382
|
-
const
|
|
1383
|
-
const
|
|
1640
|
+
connection = await pool.getConnection(serverName);
|
|
1641
|
+
const conn = connection;
|
|
1642
|
+
const tools = await new Promise((resolve, reject) => {
|
|
1643
|
+
const timeoutHandle = setTimeout(() => {
|
|
1644
|
+
reject(new Error(`Timed out fetching tools from server '${serverName}' after ${TOOLS_FETCH_TIMEOUT_MS}ms`));
|
|
1645
|
+
}, TOOLS_FETCH_TIMEOUT_MS);
|
|
1646
|
+
conn.getTools().then((result2) => {
|
|
1647
|
+
clearTimeout(timeoutHandle);
|
|
1648
|
+
resolve(result2);
|
|
1649
|
+
}, (err) => {
|
|
1650
|
+
clearTimeout(timeoutHandle);
|
|
1651
|
+
reject(err);
|
|
1652
|
+
});
|
|
1653
|
+
});
|
|
1654
|
+
const uniqueName = uniqueNameMap.get(serverName) ?? sanitizeIdentifier2(serverName);
|
|
1655
|
+
const moduleCode = generateServerModule(tools, serverName, actualPort, uniqueName);
|
|
1384
1656
|
wrapperModules.push(moduleCode);
|
|
1385
1657
|
pool.releaseConnection(serverName);
|
|
1386
1658
|
} catch (serverError) {
|
|
1387
1659
|
const errorMessage = serverError instanceof Error ? serverError.message : String(serverError);
|
|
1660
|
+
if (connection !== void 0) {
|
|
1661
|
+
pool.releaseConnection(serverName);
|
|
1662
|
+
}
|
|
1388
1663
|
await bridge.stop();
|
|
1664
|
+
activeBridge = null;
|
|
1389
1665
|
return {
|
|
1390
1666
|
content: [{ type: "text", text: `Error generating wrapper for server '${serverName}': ${errorMessage}` }],
|
|
1391
1667
|
isError: true
|
|
@@ -1393,14 +1669,12 @@ function createExecuteWithWrappersHandler(pool, config = {}) {
|
|
|
1393
1669
|
}
|
|
1394
1670
|
}
|
|
1395
1671
|
const generatedWrappers = wrapperModules.join("\n\n");
|
|
1396
|
-
const mcpDictionary =
|
|
1397
|
-
const mcpPreamble = getMcpPreamble(actualPort);
|
|
1672
|
+
const mcpDictionary = generateMcpDictionaryFromMap(wrappers, uniqueNameMap);
|
|
1398
1673
|
const instrumentedCode = wrapUserCodeForReturnCapture(code);
|
|
1399
1674
|
const fullCode = `${generatedWrappers}
|
|
1400
1675
|
|
|
1401
1676
|
${mcpDictionary}
|
|
1402
1677
|
|
|
1403
|
-
${mcpPreamble}
|
|
1404
1678
|
${instrumentedCode}`;
|
|
1405
1679
|
const sandboxConfig = {
|
|
1406
1680
|
...config.sandboxConfig,
|
|
@@ -1478,7 +1752,7 @@ function createMcpExecServer(pool, config = {}) {
|
|
|
1478
1752
|
const tools = [
|
|
1479
1753
|
listAvailableMcpServersTool,
|
|
1480
1754
|
getMcpToolSchemaTool,
|
|
1481
|
-
|
|
1755
|
+
createExecuteCodeWithWrappersToolDefinition()
|
|
1482
1756
|
];
|
|
1483
1757
|
const listToolsHandler = /* @__PURE__ */ __name(async () => ({ tools }), "listToolsHandler");
|
|
1484
1758
|
const callToolRequestHandler = /* @__PURE__ */ __name(async (params) => {
|
|
@@ -1549,7 +1823,7 @@ __name(createMcpExecServer, "createMcpExecServer");
|
|
|
1549
1823
|
// dist/index.js
|
|
1550
1824
|
import { ServerPool, createConnection, getServerConfig as getServerConfig2, loadServerManifest as loadServerManifest2, cleanupOrphanedProcesses } from "@justanothermldude/meta-mcp-core";
|
|
1551
1825
|
var APP_NAME = "mcp-exec";
|
|
1552
|
-
var VERSION2 = "1.3.
|
|
1826
|
+
var VERSION2 = "1.3.5";
|
|
1553
1827
|
var defaultExecutor = null;
|
|
1554
1828
|
function getDefaultExecutor() {
|
|
1555
1829
|
if (!defaultExecutor) {
|
|
@@ -1651,6 +1925,7 @@ export {
|
|
|
1651
1925
|
cleanupStaleProcess,
|
|
1652
1926
|
createDefaultFilesystemConfig,
|
|
1653
1927
|
createDefaultNetworkConfig,
|
|
1928
|
+
createExecuteCodeWithWrappersToolDefinition,
|
|
1654
1929
|
createExecuteWithWrappersHandler,
|
|
1655
1930
|
createExecutor,
|
|
1656
1931
|
createGetToolSchemaHandler,
|
|
@@ -1662,6 +1937,7 @@ export {
|
|
|
1662
1937
|
executeCodeWithWrappersTool,
|
|
1663
1938
|
generateFromManifest,
|
|
1664
1939
|
generateMcpDictionary,
|
|
1940
|
+
generateMcpDictionaryFromMap,
|
|
1665
1941
|
generateServerModule,
|
|
1666
1942
|
generateToolWrapper,
|
|
1667
1943
|
getMcpToolSchemaTool,
|