@easynet/agent-tool 1.0.74 → 1.0.76
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/README.md +21 -10
- package/dist/api/adapters/LangChainToolsHub.d.ts +1 -1
- package/dist/api/createAgentTools.d.ts +55 -16
- package/dist/api/createAgentTools.d.ts.map +1 -1
- package/dist/api/expose/index.d.ts +2 -2
- package/dist/api/expose/index.d.ts.map +1 -1
- package/dist/api/expose/mcpServer.d.ts +3 -4
- package/dist/api/expose/mcpServer.d.ts.map +1 -1
- package/dist/api/expose/openapiHttp.d.ts +15 -31
- package/dist/api/expose/openapiHttp.d.ts.map +1 -1
- package/dist/api/main.cjs +20 -24
- package/dist/api/main.d.ts +16 -15
- package/dist/api/main.d.ts.map +1 -1
- package/dist/api/main.js +2 -2
- package/dist/api/runtimeFromConfig.d.ts +1 -1
- package/dist/api/runtimeFromConfig.d.ts.map +1 -1
- package/dist/{chunk-JYADGZQP.js → chunk-BMAYX22K.js} +298 -268
- package/dist/chunk-BMAYX22K.js.map +1 -0
- package/dist/{chunk-SE6IMOIE.cjs → chunk-CEVOKZFT.cjs} +301 -269
- package/dist/chunk-CEVOKZFT.cjs.map +1 -0
- package/dist/{chunk-KTQTDKWU.cjs → chunk-EZDDKTCF.cjs} +84 -89
- package/dist/chunk-EZDDKTCF.cjs.map +1 -0
- package/dist/{chunk-ECHW6AWF.js → chunk-LUKSY7VK.js} +73 -79
- package/dist/chunk-LUKSY7VK.js.map +1 -0
- package/dist/index.cjs +19 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/tools/util/toolConfig.d.ts +13 -0
- package/dist/tools/util/toolConfig.d.ts.map +1 -1
- package/dist/utils/cli/index.cjs +12 -12
- package/dist/utils/cli/index.js +1 -1
- package/package.json +4 -1
- package/dist/chunk-ECHW6AWF.js.map +0 -1
- package/dist/chunk-JYADGZQP.js.map +0 -1
- package/dist/chunk-KTQTDKWU.cjs.map +0 -1
- package/dist/chunk-SE6IMOIE.cjs.map +0 -1
|
@@ -16,8 +16,27 @@ import pTimeout from 'p-timeout';
|
|
|
16
16
|
import { createRequire } from 'module';
|
|
17
17
|
import { pathToFileURL } from 'url';
|
|
18
18
|
import { resolveLatestVersionFromRegistry, ensurePackageInCache, getPackageEntryPath, importFromCache } from '@easynet/agent-common/npm';
|
|
19
|
-
import
|
|
19
|
+
import express from 'express';
|
|
20
20
|
|
|
21
|
+
function normalizeMcpConfig(raw) {
|
|
22
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
23
|
+
const source = raw;
|
|
24
|
+
const out = {};
|
|
25
|
+
if (typeof source.name === "string" && source.name.trim()) out.name = source.name.trim();
|
|
26
|
+
if (typeof source.version === "string" && source.version.trim()) out.version = source.version.trim();
|
|
27
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
28
|
+
}
|
|
29
|
+
function normalizeOpenApiConfig(raw) {
|
|
30
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
31
|
+
const source = raw;
|
|
32
|
+
const out = {};
|
|
33
|
+
if (typeof source.host === "string" && source.host.trim()) out.host = source.host.trim();
|
|
34
|
+
if (typeof source.basePath === "string") out.basePath = source.basePath;
|
|
35
|
+
if (typeof source.title === "string" && source.title.trim()) out.title = source.title.trim();
|
|
36
|
+
if (typeof source.version === "string" && source.version.trim()) out.version = source.version.trim();
|
|
37
|
+
if (typeof source.port === "number" && Number.isFinite(source.port)) out.port = source.port;
|
|
38
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
39
|
+
}
|
|
21
40
|
function loadToolConfig(toolYamlPath) {
|
|
22
41
|
const abs = resolve(toolYamlPath);
|
|
23
42
|
const raw = readFileSync(abs, "utf8");
|
|
@@ -26,6 +45,8 @@ function loadToolConfig(toolYamlPath) {
|
|
|
26
45
|
});
|
|
27
46
|
if (!parsed || typeof parsed !== "object") return {};
|
|
28
47
|
const source = parsed.spec && typeof parsed.spec === "object" && !Array.isArray(parsed.spec) ? parsed.spec : parsed;
|
|
48
|
+
const mcpConfig = normalizeMcpConfig(source.expose?.mcp ?? source.mcp);
|
|
49
|
+
const openApiConfig = normalizeOpenApiConfig(source.expose?.openapi ?? source.openapi);
|
|
29
50
|
const toolsBlock = source.tools;
|
|
30
51
|
if (toolsBlock != null && typeof toolsBlock === "object" && !Array.isArray(toolsBlock)) {
|
|
31
52
|
const toolDefaults = toolsBlock.defaults != null && typeof toolsBlock.defaults === "object" && !Array.isArray(toolsBlock.defaults) ? toolsBlock.defaults : void 0;
|
|
@@ -39,7 +60,9 @@ function loadToolConfig(toolYamlPath) {
|
|
|
39
60
|
blockedHosts: Array.isArray(toolsBlock.blockedHosts) ? toolsBlock.blockedHosts : source.blockedHosts,
|
|
40
61
|
blockedCidrs: Array.isArray(toolsBlock.blockedCidrs) ? toolsBlock.blockedCidrs : source.blockedCidrs,
|
|
41
62
|
toolDefaults,
|
|
42
|
-
packageToolDefaults: packageToolDefaults2
|
|
63
|
+
packageToolDefaults: packageToolDefaults2,
|
|
64
|
+
mcp: mcpConfig,
|
|
65
|
+
openapi: openApiConfig
|
|
43
66
|
};
|
|
44
67
|
}
|
|
45
68
|
const packageToolDefaults = typeof source.packageToolDefaults === "object" && !Array.isArray(source.packageToolDefaults) ? source.packageToolDefaults : void 0;
|
|
@@ -52,7 +75,9 @@ function loadToolConfig(toolYamlPath) {
|
|
|
52
75
|
blockedHosts: Array.isArray(source.blockedHosts) ? source.blockedHosts : void 0,
|
|
53
76
|
blockedCidrs: Array.isArray(source.blockedCidrs) ? source.blockedCidrs : void 0,
|
|
54
77
|
toolDefaults: typeof source.toolDefaults === "object" && !Array.isArray(source.toolDefaults) ? source.toolDefaults : void 0,
|
|
55
|
-
packageToolDefaults
|
|
78
|
+
packageToolDefaults,
|
|
79
|
+
mcp: mcpConfig,
|
|
80
|
+
openapi: openApiConfig
|
|
56
81
|
};
|
|
57
82
|
}
|
|
58
83
|
function resolveSandboxedPath(toolYamlPath, sandboxedPath) {
|
|
@@ -2195,6 +2220,137 @@ async function createRuntimeFromConfig(options = {}) {
|
|
|
2195
2220
|
return createRuntimeFromConfigSync(options);
|
|
2196
2221
|
}
|
|
2197
2222
|
|
|
2223
|
+
// src/api/expose/mcpServer.ts
|
|
2224
|
+
var DEFAULT_CTX = {
|
|
2225
|
+
requestId: `mcp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
2226
|
+
taskId: `task-${Date.now()}`,
|
|
2227
|
+
permissions: [
|
|
2228
|
+
"read:web",
|
|
2229
|
+
"read:fs",
|
|
2230
|
+
"write:fs",
|
|
2231
|
+
"read:db",
|
|
2232
|
+
"write:db",
|
|
2233
|
+
"network",
|
|
2234
|
+
"workflow",
|
|
2235
|
+
"danger:destructive"
|
|
2236
|
+
]
|
|
2237
|
+
};
|
|
2238
|
+
function toMcpToolName(registryName) {
|
|
2239
|
+
return normalizeToolName(registryName);
|
|
2240
|
+
}
|
|
2241
|
+
async function createMcpServerWithTools(runtime, options) {
|
|
2242
|
+
const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
|
|
2243
|
+
const name = options.name ?? "agent-tool";
|
|
2244
|
+
const version = options.version ?? "1.0.0";
|
|
2245
|
+
const ctxFactory = options.execContextFactory ?? (() => ({ ...DEFAULT_CTX }));
|
|
2246
|
+
const server = new McpServer({ name, version });
|
|
2247
|
+
const registry = runtime.getRegistry();
|
|
2248
|
+
const specs = registry.snapshot();
|
|
2249
|
+
for (const spec of specs) {
|
|
2250
|
+
const mcpName = toMcpToolName(spec.name);
|
|
2251
|
+
server.registerTool(
|
|
2252
|
+
mcpName,
|
|
2253
|
+
{
|
|
2254
|
+
description: spec.description ?? `Tool: ${spec.name}`,
|
|
2255
|
+
inputSchema: spec.inputSchema,
|
|
2256
|
+
_meta: spec._meta
|
|
2257
|
+
},
|
|
2258
|
+
async (args) => {
|
|
2259
|
+
const ctx = ctxFactory();
|
|
2260
|
+
const result = await runtime.invoke(
|
|
2261
|
+
{ tool: spec.name, args: args ?? {}, purpose: MCP_KIND },
|
|
2262
|
+
ctx
|
|
2263
|
+
);
|
|
2264
|
+
if (result.ok) {
|
|
2265
|
+
return { content: [{ type: "text", text: JSON.stringify(result.result) }] };
|
|
2266
|
+
}
|
|
2267
|
+
const err = result.error;
|
|
2268
|
+
return {
|
|
2269
|
+
content: [
|
|
2270
|
+
{
|
|
2271
|
+
type: "text",
|
|
2272
|
+
text: JSON.stringify({ error: err?.message ?? "Tool failed", kind: err?.kind })
|
|
2273
|
+
}
|
|
2274
|
+
],
|
|
2275
|
+
isError: true
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
return { server };
|
|
2281
|
+
}
|
|
2282
|
+
async function createMCPServer(runtimeOrConfig, options = {}) {
|
|
2283
|
+
const runtime = "invoke" in runtimeOrConfig && typeof runtimeOrConfig.invoke === "function" ? runtimeOrConfig : (await createRuntimeFromConfig(runtimeOrConfig)).runtime;
|
|
2284
|
+
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
2285
|
+
const { server } = await createMcpServerWithTools(runtime, options);
|
|
2286
|
+
return {
|
|
2287
|
+
server,
|
|
2288
|
+
async connectStdio() {
|
|
2289
|
+
const transport = new StdioServerTransport();
|
|
2290
|
+
await server.connect(transport);
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
function createMCPStreamableHttpHandler(runtimeOrConfig, options = {}) {
|
|
2295
|
+
if ("invoke" in runtimeOrConfig && typeof runtimeOrConfig.invoke === "function") {
|
|
2296
|
+
const runtime = runtimeOrConfig;
|
|
2297
|
+
return async function streamableHttpHandler(req, res, parsedBody) {
|
|
2298
|
+
const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
2299
|
+
const { server } = await createMcpServerWithTools(runtime, options);
|
|
2300
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
2301
|
+
await server.connect(transport);
|
|
2302
|
+
const onClose = () => {
|
|
2303
|
+
transport.close().catch(() => {
|
|
2304
|
+
});
|
|
2305
|
+
server.close().catch(() => {
|
|
2306
|
+
});
|
|
2307
|
+
res.removeListener("close", onClose);
|
|
2308
|
+
res.removeListener("finish", onClose);
|
|
2309
|
+
};
|
|
2310
|
+
res.once("close", onClose);
|
|
2311
|
+
res.once("finish", onClose);
|
|
2312
|
+
await transport.handleRequest(
|
|
2313
|
+
req,
|
|
2314
|
+
res,
|
|
2315
|
+
parsedBody
|
|
2316
|
+
);
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
return (async () => {
|
|
2320
|
+
const { runtime } = await createRuntimeFromConfig(runtimeOrConfig);
|
|
2321
|
+
return createMCPStreamableHttpHandler(runtime, options);
|
|
2322
|
+
})();
|
|
2323
|
+
}
|
|
2324
|
+
async function createMCPServerStreamableHttp(runtimeOrConfig, options = {}) {
|
|
2325
|
+
const path = options.path ?? "/mcp";
|
|
2326
|
+
const host = options.host ?? "127.0.0.1";
|
|
2327
|
+
const port = options.port ?? 3e3;
|
|
2328
|
+
const { createMcpExpressApp } = await import('@modelcontextprotocol/sdk/server/express.js');
|
|
2329
|
+
const handler = "invoke" in runtimeOrConfig && typeof runtimeOrConfig.invoke === "function" ? createMCPStreamableHttpHandler(runtimeOrConfig, options) : await createMCPStreamableHttpHandler(runtimeOrConfig, options);
|
|
2330
|
+
const app = createMcpExpressApp({ host });
|
|
2331
|
+
app.post(path, handler);
|
|
2332
|
+
return {
|
|
2333
|
+
app,
|
|
2334
|
+
path,
|
|
2335
|
+
async listen(listenPort, listenHost) {
|
|
2336
|
+
const p = listenPort ?? port;
|
|
2337
|
+
const h = listenHost ?? host;
|
|
2338
|
+
return new Promise((resolve4, reject) => {
|
|
2339
|
+
const server = app.listen(p, h, () => {
|
|
2340
|
+
const addr = server.address();
|
|
2341
|
+
const actualPort = typeof addr === "object" && addr !== null && "port" in addr ? addr.port : p;
|
|
2342
|
+
resolve4({ url: `http://${h}:${actualPort}${path}`, port: actualPort });
|
|
2343
|
+
});
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
async function runMCPServerOverStdio(runtime, options = {}) {
|
|
2349
|
+
const result = await createMCPServer(runtime, options);
|
|
2350
|
+
await result.connectStdio();
|
|
2351
|
+
return result;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2198
2354
|
// src/api/expose/openapi.ts
|
|
2199
2355
|
function toolNameToSlug(name) {
|
|
2200
2356
|
return name.replace(/\./g, "~");
|
|
@@ -2332,7 +2488,7 @@ function createPerToolPathSpec(spec, key) {
|
|
|
2332
2488
|
}
|
|
2333
2489
|
|
|
2334
2490
|
// src/api/expose/openapiHttp.ts
|
|
2335
|
-
var
|
|
2491
|
+
var DEFAULT_CTX2 = {
|
|
2336
2492
|
requestId: `http-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
2337
2493
|
taskId: `task-${Date.now()}`,
|
|
2338
2494
|
permissions: [
|
|
@@ -2346,8 +2502,17 @@ var DEFAULT_CTX = {
|
|
|
2346
2502
|
"danger:destructive"
|
|
2347
2503
|
]
|
|
2348
2504
|
};
|
|
2505
|
+
var BodyParseError = class extends Error {
|
|
2506
|
+
hint;
|
|
2507
|
+
constructor(message, hint) {
|
|
2508
|
+
super(message);
|
|
2509
|
+
this.name = "BodyParseError";
|
|
2510
|
+
this.hint = hint;
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2349
2513
|
function safeParseToolArgs(raw) {
|
|
2350
2514
|
const text = String(raw ?? "").trim();
|
|
2515
|
+
if (!text) return { ok: true, value: {} };
|
|
2351
2516
|
const firstBrace = text.indexOf("{");
|
|
2352
2517
|
if (firstBrace === -1) {
|
|
2353
2518
|
return {
|
|
@@ -2357,9 +2522,7 @@ function safeParseToolArgs(raw) {
|
|
|
2357
2522
|
}
|
|
2358
2523
|
let jsonText = text.slice(firstBrace);
|
|
2359
2524
|
const lastBrace = jsonText.lastIndexOf("}");
|
|
2360
|
-
if (lastBrace !== -1)
|
|
2361
|
-
jsonText = jsonText.slice(0, lastBrace + 1);
|
|
2362
|
-
}
|
|
2525
|
+
if (lastBrace !== -1) jsonText = jsonText.slice(0, lastBrace + 1);
|
|
2363
2526
|
try {
|
|
2364
2527
|
return { ok: true, value: JSON.parse(jsonText) };
|
|
2365
2528
|
} catch {
|
|
@@ -2369,37 +2532,8 @@ function safeParseToolArgs(raw) {
|
|
|
2369
2532
|
};
|
|
2370
2533
|
}
|
|
2371
2534
|
}
|
|
2372
|
-
var BodyParseError = class extends Error {
|
|
2373
|
-
hint;
|
|
2374
|
-
constructor(message, hint) {
|
|
2375
|
-
super(message);
|
|
2376
|
-
this.name = "BodyParseError";
|
|
2377
|
-
this.hint = hint;
|
|
2378
|
-
}
|
|
2379
|
-
};
|
|
2380
|
-
function parseBody(req) {
|
|
2381
|
-
return new Promise((resolve4, reject) => {
|
|
2382
|
-
const chunks = [];
|
|
2383
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
2384
|
-
req.on("end", () => {
|
|
2385
|
-
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
2386
|
-
if (!raw.trim()) {
|
|
2387
|
-
resolve4({});
|
|
2388
|
-
return;
|
|
2389
|
-
}
|
|
2390
|
-
const parsed = safeParseToolArgs(raw);
|
|
2391
|
-
if (parsed.ok) {
|
|
2392
|
-
resolve4(parsed.value);
|
|
2393
|
-
return;
|
|
2394
|
-
}
|
|
2395
|
-
reject(new BodyParseError("Invalid JSON body", parsed.hint));
|
|
2396
|
-
});
|
|
2397
|
-
req.on("error", reject);
|
|
2398
|
-
});
|
|
2399
|
-
}
|
|
2400
2535
|
function sendJson(res, status, data) {
|
|
2401
|
-
res.
|
|
2402
|
-
res.end(JSON.stringify(data));
|
|
2536
|
+
res.status(status).json(data);
|
|
2403
2537
|
}
|
|
2404
2538
|
function swaggerUiHtml(specUrl) {
|
|
2405
2539
|
const specUrlEscaped = specUrl.replace(/"/g, """);
|
|
@@ -2432,99 +2566,13 @@ function swaggerUiHtml(specUrl) {
|
|
|
2432
2566
|
</body>
|
|
2433
2567
|
</html>`;
|
|
2434
2568
|
}
|
|
2435
|
-
function
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
const
|
|
2439
|
-
|
|
2440
|
-
);
|
|
2441
|
-
|
|
2442
|
-
}
|
|
2443
|
-
async function handleOpenApiHttpRequest(input) {
|
|
2444
|
-
const norm = normalizePath(input.req.url, input.basePath);
|
|
2445
|
-
try {
|
|
2446
|
-
if (await maybeHandleDocsRoute(input, norm)) return;
|
|
2447
|
-
if (await maybeHandleToolsRoute(input, norm)) return;
|
|
2448
|
-
if (await maybeHandleInvokeRoute(input, norm)) return;
|
|
2449
|
-
sendJson(input.res, 404, { error: "Not found", path: norm });
|
|
2450
|
-
} catch (err) {
|
|
2451
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2452
|
-
sendJson(input.res, 500, { error: message, kind: "INTERNAL_ERROR" });
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
function normalizePath(url, basePath) {
|
|
2456
|
-
const path = (url ?? "/").split("?")[0] ?? "/";
|
|
2457
|
-
return basePath ? path === basePath ? "" : path.replace(basePath, "") || "/" : path;
|
|
2458
|
-
}
|
|
2459
|
-
async function maybeHandleDocsRoute(input, norm) {
|
|
2460
|
-
if (input.req.method === "GET" && (norm === "/" || norm === "/swagger")) {
|
|
2461
|
-
const specPath = input.basePath ? `${input.basePath}/openapi.json` : "/openapi.json";
|
|
2462
|
-
input.res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2463
|
-
input.res.end(swaggerUiHtml(specPath));
|
|
2464
|
-
return true;
|
|
2465
|
-
}
|
|
2466
|
-
if (input.req.method === "GET" && (norm === "/openapi.json" || norm === "/spec")) {
|
|
2467
|
-
sendJson(
|
|
2468
|
-
input.res,
|
|
2469
|
-
200,
|
|
2470
|
-
toolsToOpenAPISpec(input.runtime.getRegistry(), {
|
|
2471
|
-
title: "Tool API",
|
|
2472
|
-
version: "1.0.0",
|
|
2473
|
-
basePath: input.basePath || void 0
|
|
2474
|
-
})
|
|
2475
|
-
);
|
|
2476
|
-
return true;
|
|
2477
|
-
}
|
|
2478
|
-
return false;
|
|
2479
|
-
}
|
|
2480
|
-
async function maybeHandleToolsRoute(input, norm) {
|
|
2481
|
-
if (input.req.method !== "GET" || norm !== "/tools") return false;
|
|
2482
|
-
const { enrichSpecWithCanonicalSchema: enrichSpecWithCanonicalSchema2 } = await import('./canonicalCoreSchemas-PHGTNPN5.js');
|
|
2483
|
-
const tools = input.runtime.getRegistry().snapshot().map(enrichSpecWithCanonicalSchema2).map((s) => ({
|
|
2484
|
-
name: s.name,
|
|
2485
|
-
description: s.description,
|
|
2486
|
-
kind: s.kind,
|
|
2487
|
-
inputSchema: s.inputSchema
|
|
2488
|
-
}));
|
|
2489
|
-
sendJson(input.res, 200, { tools });
|
|
2490
|
-
return true;
|
|
2491
|
-
}
|
|
2492
|
-
async function maybeHandleInvokeRoute(input, norm) {
|
|
2493
|
-
if (input.req.method !== "POST") return false;
|
|
2494
|
-
if (norm === "/invoke") {
|
|
2495
|
-
const body = await parseBodyOrSendError(input.req, input.res);
|
|
2496
|
-
if (!body) return true;
|
|
2497
|
-
const tool = body.tool;
|
|
2498
|
-
if (typeof tool !== "string" || !tool.trim()) {
|
|
2499
|
-
sendJson(input.res, 400, { error: "Missing or invalid 'tool' in body", kind: "BAD_REQUEST" });
|
|
2500
|
-
return true;
|
|
2501
|
-
}
|
|
2502
|
-
await invokeAndSend(input.runtime, input.ctxFactory(input.req), input.res, tool.trim(), body.args ?? {});
|
|
2503
|
-
return true;
|
|
2504
|
-
}
|
|
2505
|
-
if (norm.startsWith("/invoke/") && norm.length > "/invoke/".length) {
|
|
2506
|
-
const args = await parseArgsOrSendError(input.req, input.res);
|
|
2507
|
-
if (args === void 0) return true;
|
|
2508
|
-
await invokeAndSend(input.runtime, input.ctxFactory(input.req), input.res, slugToToolName(norm.slice(8)), args);
|
|
2509
|
-
return true;
|
|
2510
|
-
}
|
|
2511
|
-
return false;
|
|
2512
|
-
}
|
|
2513
|
-
async function parseBodyOrSendError(req, res) {
|
|
2514
|
-
try {
|
|
2515
|
-
return await parseBody(req);
|
|
2516
|
-
} catch (err) {
|
|
2517
|
-
sendBadJsonBody(res, err);
|
|
2518
|
-
return null;
|
|
2519
|
-
}
|
|
2520
|
-
}
|
|
2521
|
-
async function parseArgsOrSendError(req, res) {
|
|
2522
|
-
try {
|
|
2523
|
-
return await parseBody(req) ?? {};
|
|
2524
|
-
} catch (err) {
|
|
2525
|
-
sendBadJsonBody(res, err);
|
|
2526
|
-
return void 0;
|
|
2527
|
-
}
|
|
2569
|
+
function parseBodyFromExpress(raw) {
|
|
2570
|
+
if (raw == null) return {};
|
|
2571
|
+
if (typeof raw === "object") return raw;
|
|
2572
|
+
const text = Buffer.isBuffer(raw) ? raw.toString("utf-8") : String(raw);
|
|
2573
|
+
const parsed = safeParseToolArgs(text);
|
|
2574
|
+
if (parsed.ok) return parsed.value;
|
|
2575
|
+
throw new BodyParseError("Invalid JSON body", parsed.hint);
|
|
2528
2576
|
}
|
|
2529
2577
|
function sendBadJsonBody(res, err) {
|
|
2530
2578
|
const hint = err instanceof BodyParseError ? err.hint : void 0;
|
|
@@ -2547,16 +2595,117 @@ async function invokeAndSend(runtime, ctx, res, tool, args) {
|
|
|
2547
2595
|
details: result.error?.details
|
|
2548
2596
|
});
|
|
2549
2597
|
}
|
|
2550
|
-
function
|
|
2598
|
+
function createOpenAPIHttpServer(runtime, options = {}) {
|
|
2599
|
+
const app = express();
|
|
2600
|
+
const router = express.Router();
|
|
2601
|
+
const basePath = (options.basePath ?? "").replace(/\/$/, "");
|
|
2602
|
+
const ctxFactory = options.execContextFactory ?? (() => ({ ...DEFAULT_CTX2 }));
|
|
2603
|
+
app.use(express.text({ type: "*/*" }));
|
|
2604
|
+
router.get("/", (_req, res) => {
|
|
2605
|
+
const specPath = basePath ? `${basePath}/openapi.json` : "/openapi.json";
|
|
2606
|
+
res.type("text/html; charset=utf-8").send(swaggerUiHtml(specPath));
|
|
2607
|
+
});
|
|
2608
|
+
router.get("/swagger", (_req, res) => {
|
|
2609
|
+
const specPath = basePath ? `${basePath}/openapi.json` : "/openapi.json";
|
|
2610
|
+
res.type("text/html; charset=utf-8").send(swaggerUiHtml(specPath));
|
|
2611
|
+
});
|
|
2612
|
+
router.get("/openapi.json", (_req, res) => {
|
|
2613
|
+
sendJson(
|
|
2614
|
+
res,
|
|
2615
|
+
200,
|
|
2616
|
+
toolsToOpenAPISpec(runtime.getRegistry(), {
|
|
2617
|
+
title: "Tool API",
|
|
2618
|
+
version: "1.0.0",
|
|
2619
|
+
basePath: basePath || void 0
|
|
2620
|
+
})
|
|
2621
|
+
);
|
|
2622
|
+
});
|
|
2623
|
+
router.get("/spec", (_req, res) => {
|
|
2624
|
+
sendJson(
|
|
2625
|
+
res,
|
|
2626
|
+
200,
|
|
2627
|
+
toolsToOpenAPISpec(runtime.getRegistry(), {
|
|
2628
|
+
title: "Tool API",
|
|
2629
|
+
version: "1.0.0",
|
|
2630
|
+
basePath: basePath || void 0
|
|
2631
|
+
})
|
|
2632
|
+
);
|
|
2633
|
+
});
|
|
2634
|
+
router.get("/tools", async (_req, res, next) => {
|
|
2635
|
+
try {
|
|
2636
|
+
const { enrichSpecWithCanonicalSchema: enrichSpecWithCanonicalSchema2 } = await import('./canonicalCoreSchemas-PHGTNPN5.js');
|
|
2637
|
+
const tools = runtime.getRegistry().snapshot().map(enrichSpecWithCanonicalSchema2).map((s) => ({
|
|
2638
|
+
name: s.name,
|
|
2639
|
+
description: s.description,
|
|
2640
|
+
kind: s.kind,
|
|
2641
|
+
inputSchema: s.inputSchema
|
|
2642
|
+
}));
|
|
2643
|
+
sendJson(res, 200, { tools });
|
|
2644
|
+
} catch (error) {
|
|
2645
|
+
next(error);
|
|
2646
|
+
}
|
|
2647
|
+
});
|
|
2648
|
+
router.post("/invoke", async (req, res) => {
|
|
2649
|
+
let body = null;
|
|
2650
|
+
try {
|
|
2651
|
+
body = parseBodyFromExpress(req.body);
|
|
2652
|
+
} catch (err) {
|
|
2653
|
+
sendBadJsonBody(res, err);
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
const tool = body.tool;
|
|
2657
|
+
if (typeof tool !== "string" || !tool.trim()) {
|
|
2658
|
+
sendJson(res, 400, { error: "Missing or invalid 'tool' in body", kind: "BAD_REQUEST" });
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
await invokeAndSend(runtime, ctxFactory(req), res, tool.trim(), body.args ?? {});
|
|
2662
|
+
});
|
|
2663
|
+
router.post("/invoke/:slug", async (req, res) => {
|
|
2664
|
+
let args;
|
|
2665
|
+
try {
|
|
2666
|
+
args = parseBodyFromExpress(req.body);
|
|
2667
|
+
} catch (err) {
|
|
2668
|
+
sendBadJsonBody(res, err);
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
await invokeAndSend(runtime, ctxFactory(req), res, slugToToolName(req.params?.slug ?? ""), args);
|
|
2672
|
+
});
|
|
2673
|
+
if (basePath) {
|
|
2674
|
+
app.use(basePath, router);
|
|
2675
|
+
} else {
|
|
2676
|
+
app.use(router);
|
|
2677
|
+
}
|
|
2678
|
+
app.use((req, res) => {
|
|
2679
|
+
sendJson(res, 404, { error: "Not found", path: req.url ?? "/" });
|
|
2680
|
+
});
|
|
2681
|
+
app.use((err, _req, res, _next) => {
|
|
2682
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2683
|
+
sendJson(res, 500, { error: message, kind: "INTERNAL_ERROR" });
|
|
2684
|
+
});
|
|
2685
|
+
const originalListen = app.listen.bind(app);
|
|
2686
|
+
const defaultPort = options.port;
|
|
2687
|
+
const defaultHost = options.host;
|
|
2688
|
+
app.listen = ((...args) => {
|
|
2689
|
+
if (args.length === 0) {
|
|
2690
|
+
return originalListen(defaultPort ?? 0, defaultHost ?? "localhost");
|
|
2691
|
+
}
|
|
2692
|
+
if (args.length === 1 && typeof args[0] === "function") {
|
|
2693
|
+
return originalListen(defaultPort ?? 0, defaultHost ?? "localhost", args[0]);
|
|
2694
|
+
}
|
|
2695
|
+
return originalListen(...args);
|
|
2696
|
+
});
|
|
2697
|
+
return app;
|
|
2698
|
+
}
|
|
2699
|
+
function listenOpenAPIHttpServer(app, options = {}) {
|
|
2551
2700
|
return new Promise((resolve4, reject) => {
|
|
2552
2701
|
const port = options.port ?? 0;
|
|
2553
2702
|
const host = options.host ?? "localhost";
|
|
2554
|
-
|
|
2555
|
-
const addr =
|
|
2703
|
+
const httpServer = app.listen(port, host, () => {
|
|
2704
|
+
const addr = httpServer.address();
|
|
2556
2705
|
const actualPort = typeof addr === "object" && addr?.port != null ? addr.port : port;
|
|
2557
|
-
resolve4({ port: actualPort, host });
|
|
2706
|
+
resolve4({ port: actualPort, host, httpServer });
|
|
2558
2707
|
});
|
|
2559
|
-
|
|
2708
|
+
httpServer.on("error", reject);
|
|
2560
2709
|
});
|
|
2561
2710
|
}
|
|
2562
2711
|
async function createHttpService(runtimeOrConfig, options = {}) {
|
|
@@ -2567,144 +2716,25 @@ async function createHttpService(runtimeOrConfig, options = {}) {
|
|
|
2567
2716
|
version: options.version ?? "1.0.0",
|
|
2568
2717
|
basePath: options.basePath
|
|
2569
2718
|
});
|
|
2719
|
+
let activeHttpServer = null;
|
|
2570
2720
|
return {
|
|
2571
2721
|
server,
|
|
2572
2722
|
openApiSpec,
|
|
2573
|
-
listen: (listenOpts) =>
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
"read:web",
|
|
2583
|
-
"read:fs",
|
|
2584
|
-
"write:fs",
|
|
2585
|
-
"read:db",
|
|
2586
|
-
"write:db",
|
|
2587
|
-
"network",
|
|
2588
|
-
"workflow",
|
|
2589
|
-
"danger:destructive"
|
|
2590
|
-
]
|
|
2591
|
-
};
|
|
2592
|
-
function toMcpToolName(registryName) {
|
|
2593
|
-
return normalizeToolName(registryName);
|
|
2594
|
-
}
|
|
2595
|
-
async function createMcpServerWithTools(runtime, options) {
|
|
2596
|
-
const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
|
|
2597
|
-
const name = options.name ?? "agent-tool";
|
|
2598
|
-
const version = options.version ?? "1.0.0";
|
|
2599
|
-
const ctxFactory = options.execContextFactory ?? (() => ({ ...DEFAULT_CTX2 }));
|
|
2600
|
-
const server = new McpServer({ name, version });
|
|
2601
|
-
const registry = runtime.getRegistry();
|
|
2602
|
-
const specs = registry.snapshot();
|
|
2603
|
-
for (const spec of specs) {
|
|
2604
|
-
const mcpName = toMcpToolName(spec.name);
|
|
2605
|
-
server.registerTool(
|
|
2606
|
-
mcpName,
|
|
2607
|
-
{
|
|
2608
|
-
description: spec.description ?? `Tool: ${spec.name}`,
|
|
2609
|
-
inputSchema: spec.inputSchema,
|
|
2610
|
-
_meta: spec._meta
|
|
2611
|
-
},
|
|
2612
|
-
async (args) => {
|
|
2613
|
-
const ctx = ctxFactory();
|
|
2614
|
-
const result = await runtime.invoke(
|
|
2615
|
-
{ tool: spec.name, args: args ?? {}, purpose: MCP_KIND },
|
|
2616
|
-
ctx
|
|
2617
|
-
);
|
|
2618
|
-
if (result.ok) {
|
|
2619
|
-
return { content: [{ type: "text", text: JSON.stringify(result.result) }] };
|
|
2620
|
-
}
|
|
2621
|
-
const err = result.error;
|
|
2622
|
-
return {
|
|
2623
|
-
content: [
|
|
2624
|
-
{
|
|
2625
|
-
type: "text",
|
|
2626
|
-
text: JSON.stringify({ error: err?.message ?? "Tool failed", kind: err?.kind })
|
|
2627
|
-
}
|
|
2628
|
-
],
|
|
2629
|
-
isError: true
|
|
2630
|
-
};
|
|
2631
|
-
}
|
|
2632
|
-
);
|
|
2633
|
-
}
|
|
2634
|
-
return { server };
|
|
2635
|
-
}
|
|
2636
|
-
async function createMCPServer(runtimeOrConfig, options = {}) {
|
|
2637
|
-
const runtime = "invoke" in runtimeOrConfig && typeof runtimeOrConfig.invoke === "function" ? runtimeOrConfig : (await createRuntimeFromConfig(runtimeOrConfig)).runtime;
|
|
2638
|
-
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
2639
|
-
const { server } = await createMcpServerWithTools(runtime, options);
|
|
2640
|
-
return {
|
|
2641
|
-
server,
|
|
2642
|
-
async connectStdio() {
|
|
2643
|
-
const transport = new StdioServerTransport();
|
|
2644
|
-
await server.connect(transport);
|
|
2645
|
-
}
|
|
2646
|
-
};
|
|
2647
|
-
}
|
|
2648
|
-
function createMCPStreamableHttpHandler(runtimeOrConfig, options = {}) {
|
|
2649
|
-
if ("invoke" in runtimeOrConfig && typeof runtimeOrConfig.invoke === "function") {
|
|
2650
|
-
const runtime = runtimeOrConfig;
|
|
2651
|
-
return async function streamableHttpHandler(req, res, parsedBody) {
|
|
2652
|
-
const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
2653
|
-
const { server } = await createMcpServerWithTools(runtime, options);
|
|
2654
|
-
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
2655
|
-
await server.connect(transport);
|
|
2656
|
-
const onClose = () => {
|
|
2657
|
-
transport.close().catch(() => {
|
|
2658
|
-
});
|
|
2659
|
-
server.close().catch(() => {
|
|
2660
|
-
});
|
|
2661
|
-
res.removeListener("close", onClose);
|
|
2662
|
-
res.removeListener("finish", onClose);
|
|
2663
|
-
};
|
|
2664
|
-
res.once("close", onClose);
|
|
2665
|
-
res.once("finish", onClose);
|
|
2666
|
-
await transport.handleRequest(
|
|
2667
|
-
req,
|
|
2668
|
-
res,
|
|
2669
|
-
parsedBody
|
|
2670
|
-
);
|
|
2671
|
-
};
|
|
2672
|
-
}
|
|
2673
|
-
return (async () => {
|
|
2674
|
-
const { runtime } = await createRuntimeFromConfig(runtimeOrConfig);
|
|
2675
|
-
return createMCPStreamableHttpHandler(runtime, options);
|
|
2676
|
-
})();
|
|
2677
|
-
}
|
|
2678
|
-
async function createMCPServerStreamableHttp(runtimeOrConfig, options = {}) {
|
|
2679
|
-
const path = options.path ?? "/mcp";
|
|
2680
|
-
const host = options.host ?? "127.0.0.1";
|
|
2681
|
-
const port = options.port ?? 3e3;
|
|
2682
|
-
const { createMcpExpressApp } = await import('@modelcontextprotocol/sdk/server/express.js');
|
|
2683
|
-
const handler = "invoke" in runtimeOrConfig && typeof runtimeOrConfig.invoke === "function" ? createMCPStreamableHttpHandler(runtimeOrConfig, options) : await createMCPStreamableHttpHandler(runtimeOrConfig, options);
|
|
2684
|
-
const app = createMcpExpressApp({ host });
|
|
2685
|
-
app.post(path, handler);
|
|
2686
|
-
return {
|
|
2687
|
-
app,
|
|
2688
|
-
path,
|
|
2689
|
-
async listen(listenPort, listenHost) {
|
|
2690
|
-
const p = listenPort ?? port;
|
|
2691
|
-
const h = listenHost ?? host;
|
|
2692
|
-
return new Promise((resolve4, reject) => {
|
|
2693
|
-
const server = app.listen(p, h, () => {
|
|
2694
|
-
const addr = server.address();
|
|
2695
|
-
const actualPort = typeof addr === "object" && addr !== null && "port" in addr ? addr.port : p;
|
|
2696
|
-
resolve4({ url: `http://${h}:${actualPort}${path}`, port: actualPort });
|
|
2697
|
-
});
|
|
2723
|
+
listen: async (listenOpts) => {
|
|
2724
|
+
const { port, host, httpServer } = await listenOpenAPIHttpServer(server, listenOpts);
|
|
2725
|
+
activeHttpServer = httpServer;
|
|
2726
|
+
return { port, host };
|
|
2727
|
+
},
|
|
2728
|
+
close: async () => {
|
|
2729
|
+
if (!activeHttpServer) return;
|
|
2730
|
+
await new Promise((resolve4, reject) => {
|
|
2731
|
+
activeHttpServer.close((err) => err ? reject(err) : resolve4());
|
|
2698
2732
|
});
|
|
2733
|
+
activeHttpServer = null;
|
|
2699
2734
|
}
|
|
2700
2735
|
};
|
|
2701
2736
|
}
|
|
2702
|
-
async function runMCPServerOverStdio(runtime, options = {}) {
|
|
2703
|
-
const result = await createMCPServer(runtime, options);
|
|
2704
|
-
await result.connectStdio();
|
|
2705
|
-
return result;
|
|
2706
|
-
}
|
|
2707
2737
|
|
|
2708
|
-
export { PTCRuntime, createHttpService, createMCPServer, createMCPServerStreamableHttp, createMCPStreamableHttpHandler, createRuntimeFromConfig, createRuntimeFromConfigSync, expandToolDescriptorsToRegistryNames, fileDescriptorToPackagePrefix, findAndLoadToolConfig, getDisplayScope, isBarePackageDescriptor, loadToolConfig, npmDescriptorToPackagePrefixWithVersion, resolveSandboxedPath, resolveToolDescriptor, runMCPServerOverStdio };
|
|
2709
|
-
//# sourceMappingURL=chunk-
|
|
2710
|
-
//# sourceMappingURL=chunk-
|
|
2738
|
+
export { PTCRuntime, createHttpService, createMCPServer, createMCPServerStreamableHttp, createMCPStreamableHttpHandler, createOpenAPIHttpServer, createRuntimeFromConfig, createRuntimeFromConfigSync, expandToolDescriptorsToRegistryNames, fileDescriptorToPackagePrefix, findAndLoadToolConfig, getDisplayScope, isBarePackageDescriptor, loadToolConfig, npmDescriptorToPackagePrefixWithVersion, resolveSandboxedPath, resolveToolDescriptor, runMCPServerOverStdio };
|
|
2739
|
+
//# sourceMappingURL=chunk-BMAYX22K.js.map
|
|
2740
|
+
//# sourceMappingURL=chunk-BMAYX22K.js.map
|