@easynet/agent-tool 1.0.16 → 1.0.17
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/{chunk-FYJ5VD3Q.js → chunk-LIERSUS5.js} +3 -3
- package/dist/{chunk-FYJ5VD3Q.js.map → chunk-LIERSUS5.js.map} +1 -1
- package/dist/{chunk-F32XCT4U.cjs → chunk-RSDA7IAQ.cjs} +4 -4
- package/dist/{chunk-F32XCT4U.cjs.map → chunk-RSDA7IAQ.cjs.map} +1 -1
- package/dist/index.cjs +224 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +198 -6
- package/dist/index.js.map +1 -1
- package/dist/security/sandbox.d.ts +9 -0
- package/dist/security/sandbox.d.ts.map +1 -0
- package/dist/security/ssrf.d.ts +12 -0
- package/dist/security/ssrf.d.ts.map +1 -0
- package/dist/utils/cli/index.cjs +5 -5
- package/dist/utils/cli/index.js +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,205 @@
|
|
|
1
1
|
export { LangChainToolsHub, createAgentTools } from './chunk-M2VNTQHS.js';
|
|
2
|
-
import { loadMCPTool } from './chunk-
|
|
3
|
-
export { DirectoryScanner, DiscoveryError, SkillManifestError, buildFunctionToTool, buildMcpPackage, initProject, loadMCPTool, loadSkillDefinition, parseSkillMd, runGeneratedMCP, runMcpServer, scan, scanForTools, scanSkillResources, validateFrontmatter } from './chunk-
|
|
4
|
-
import { DEFAULT_OUTPUT_SCHEMA, DEFAULT_INPUT_SCHEMA } from './chunk-5SWSNVMI.js';
|
|
5
|
-
export { DEFAULT_INPUT_SCHEMA, DEFAULT_OUTPUT_SCHEMA, createToolSpec } from './chunk-5SWSNVMI.js';
|
|
2
|
+
import { loadMCPTool } from './chunk-LIERSUS5.js';
|
|
3
|
+
export { DirectoryScanner, DiscoveryError, SkillManifestError, buildFunctionToTool, buildMcpPackage, initProject, loadMCPTool, loadSkillDefinition, parseSkillMd, runGeneratedMCP, runMcpServer, scan, scanForTools, scanSkillResources, validateFrontmatter } from './chunk-LIERSUS5.js';
|
|
6
4
|
import { MCP_KIND } from './chunk-BNHLMLDH.js';
|
|
7
5
|
export { BudgetManager, EventLog, Metrics, PTCRuntime, PolicyDeniedError, PolicyEngine, SchemaValidationError, SchemaValidator, Tracing, buildEvidence, createLogger, ensurePackageInCache, expandToolDescriptorsToRegistryNames, getPackageEntryPath, importFromCache, isNpmToolDescriptor, loadToolConfig, normalizeToolList, parseNpmToolDescriptor, resolveNpmToolDescriptor, resolveToolDescriptor, sanitizeForLog, summarizeForLog } from './chunk-BNHLMLDH.js';
|
|
6
|
+
import { DEFAULT_OUTPUT_SCHEMA, DEFAULT_INPUT_SCHEMA } from './chunk-5SWSNVMI.js';
|
|
7
|
+
export { DEFAULT_INPUT_SCHEMA, DEFAULT_OUTPUT_SCHEMA, createToolSpec } from './chunk-5SWSNVMI.js';
|
|
8
|
+
import { createTaggedError } from './chunk-AE6FSNGY.js';
|
|
8
9
|
export { ToolRegistry, createTaggedError, isRetryable, withRetry } from './chunk-AE6FSNGY.js';
|
|
9
10
|
import './chunk-FR2CXERF.js';
|
|
10
|
-
import {
|
|
11
|
+
import { lookup } from 'dns/promises';
|
|
12
|
+
import { resolve, normalize, dirname, basename, join } from 'path';
|
|
13
|
+
import { realpath, access } from 'fs/promises';
|
|
14
|
+
|
|
15
|
+
async function validateUrl(url, allowedHosts, blockedCidrs) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = new URL(url);
|
|
19
|
+
} catch {
|
|
20
|
+
throw createTaggedError(
|
|
21
|
+
"HTTP_DISALLOWED_HOST",
|
|
22
|
+
`Invalid URL: ${url}`,
|
|
23
|
+
{ url }
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
27
|
+
throw createTaggedError(
|
|
28
|
+
"HTTP_DISALLOWED_HOST",
|
|
29
|
+
`Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,
|
|
30
|
+
{ url, protocol: parsed.protocol }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const hostname = parsed.hostname;
|
|
34
|
+
if (!isHostAllowed(hostname, allowedHosts)) {
|
|
35
|
+
throw createTaggedError(
|
|
36
|
+
"HTTP_DISALLOWED_HOST",
|
|
37
|
+
`Host "${hostname}" is not in the allowed hosts list`,
|
|
38
|
+
{ url, hostname, allowedHosts }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const { address } = await lookup(hostname);
|
|
43
|
+
if (isIpInBlockedCidrs(address, blockedCidrs)) {
|
|
44
|
+
throw createTaggedError(
|
|
45
|
+
"HTTP_DISALLOWED_HOST",
|
|
46
|
+
`Host "${hostname}" resolves to blocked IP: ${address}`,
|
|
47
|
+
{ url, hostname, resolvedIp: address }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
if (err instanceof Error && err.kind === "HTTP_DISALLOWED_HOST") {
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
throw createTaggedError(
|
|
55
|
+
"HTTP_DISALLOWED_HOST",
|
|
56
|
+
`DNS resolution failed for host "${hostname}": ${err instanceof Error ? err.message : String(err)}`,
|
|
57
|
+
{ url, hostname }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
function isHostAllowed(hostname, allowedHosts) {
|
|
63
|
+
for (const pattern of allowedHosts) {
|
|
64
|
+
if (pattern === "*") {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
if (pattern.startsWith("*.")) {
|
|
68
|
+
const suffix = pattern.slice(1);
|
|
69
|
+
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
} else if (hostname === pattern) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
function isIpInBlockedCidrs(ip, cidrs) {
|
|
79
|
+
const normalizedIp = normalizeIp(ip);
|
|
80
|
+
if (!normalizedIp) return false;
|
|
81
|
+
for (const cidr of cidrs) {
|
|
82
|
+
if (cidr.includes(":")) {
|
|
83
|
+
if (!ip.includes(":")) continue;
|
|
84
|
+
if (isIpv6InCidr(ip, cidr)) return true;
|
|
85
|
+
} else {
|
|
86
|
+
if (isIpv4InCidr(normalizedIp, cidr)) return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function normalizeIp(ip) {
|
|
92
|
+
if (ip.startsWith("::ffff:")) {
|
|
93
|
+
return ip.slice(7);
|
|
94
|
+
}
|
|
95
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) {
|
|
96
|
+
return ip;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function isIpv4InCidr(ip, cidr) {
|
|
101
|
+
const [cidrIp, prefixStr] = cidr.split("/");
|
|
102
|
+
if (!cidrIp || !prefixStr) return false;
|
|
103
|
+
const prefix = parseInt(prefixStr, 10);
|
|
104
|
+
if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;
|
|
105
|
+
const ipNum = ipv4ToNum(ip);
|
|
106
|
+
const cidrNum = ipv4ToNum(cidrIp);
|
|
107
|
+
if (ipNum === null || cidrNum === null) return false;
|
|
108
|
+
const mask = prefix === 0 ? 0 : -1 << 32 - prefix >>> 0;
|
|
109
|
+
return (ipNum & mask) === (cidrNum & mask);
|
|
110
|
+
}
|
|
111
|
+
function ipv4ToNum(ip) {
|
|
112
|
+
const parts = ip.split(".");
|
|
113
|
+
if (parts.length !== 4) return null;
|
|
114
|
+
let num = 0;
|
|
115
|
+
for (const part of parts) {
|
|
116
|
+
const n = parseInt(part, 10);
|
|
117
|
+
if (isNaN(n) || n < 0 || n > 255) return null;
|
|
118
|
+
num = num << 8 | n;
|
|
119
|
+
}
|
|
120
|
+
return num >>> 0;
|
|
121
|
+
}
|
|
122
|
+
function isIpv6InCidr(ip, cidr) {
|
|
123
|
+
const [cidrIp, prefixStr] = cidr.split("/");
|
|
124
|
+
if (!cidrIp || !prefixStr) return false;
|
|
125
|
+
const prefix = parseInt(prefixStr, 10);
|
|
126
|
+
if (isNaN(prefix)) return false;
|
|
127
|
+
const ipBytes = expandIpv6(ip);
|
|
128
|
+
const cidrBytes = expandIpv6(cidrIp);
|
|
129
|
+
if (!ipBytes || !cidrBytes) return false;
|
|
130
|
+
const fullBytes = Math.floor(prefix / 8);
|
|
131
|
+
for (let i = 0; i < fullBytes && i < 16; i++) {
|
|
132
|
+
if (ipBytes[i] !== cidrBytes[i]) return false;
|
|
133
|
+
}
|
|
134
|
+
const remainingBits = prefix % 8;
|
|
135
|
+
if (remainingBits > 0 && fullBytes < 16) {
|
|
136
|
+
const mask = -1 << 8 - remainingBits & 255;
|
|
137
|
+
if ((ipBytes[fullBytes] & mask) !== (cidrBytes[fullBytes] & mask)) return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function expandIpv6(ip) {
|
|
142
|
+
const zoneIdx = ip.indexOf("%");
|
|
143
|
+
if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);
|
|
144
|
+
const parts = ip.split("::");
|
|
145
|
+
if (parts.length > 2) return null;
|
|
146
|
+
const bytes = new Array(16).fill(0);
|
|
147
|
+
const expandGroup = (group) => {
|
|
148
|
+
if (!group) return [];
|
|
149
|
+
return group.split(":").flatMap((hex) => {
|
|
150
|
+
const val = parseInt(hex || "0", 16);
|
|
151
|
+
return [val >> 8 & 255, val & 255];
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
if (parts.length === 1) {
|
|
155
|
+
const expanded = expandGroup(parts[0]);
|
|
156
|
+
if (expanded.length !== 16) return null;
|
|
157
|
+
return expanded;
|
|
158
|
+
}
|
|
159
|
+
const left = expandGroup(parts[0]);
|
|
160
|
+
const right = expandGroup(parts[1]);
|
|
161
|
+
if (left.length + right.length > 16) return null;
|
|
162
|
+
for (let i = 0; i < left.length; i++) bytes[i] = left[i];
|
|
163
|
+
for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i];
|
|
164
|
+
return bytes;
|
|
165
|
+
}
|
|
166
|
+
async function resolveSandboxedPath(inputPath, sandboxRoot) {
|
|
167
|
+
let normalizedRoot;
|
|
168
|
+
try {
|
|
169
|
+
normalizedRoot = await realpath(resolve(sandboxRoot));
|
|
170
|
+
} catch {
|
|
171
|
+
normalizedRoot = normalize(resolve(sandboxRoot));
|
|
172
|
+
}
|
|
173
|
+
const resolved = resolve(normalizedRoot, inputPath);
|
|
174
|
+
let real;
|
|
175
|
+
try {
|
|
176
|
+
await access(resolved);
|
|
177
|
+
real = await realpath(resolved);
|
|
178
|
+
} catch {
|
|
179
|
+
const parentDir = dirname(resolved);
|
|
180
|
+
let realParent;
|
|
181
|
+
try {
|
|
182
|
+
await access(parentDir);
|
|
183
|
+
realParent = await realpath(parentDir);
|
|
184
|
+
} catch {
|
|
185
|
+
realParent = normalize(parentDir);
|
|
186
|
+
}
|
|
187
|
+
real = resolve(realParent, basename(resolved));
|
|
188
|
+
}
|
|
189
|
+
if (!isWithinRoot(real, normalizedRoot)) {
|
|
190
|
+
throw createTaggedError(
|
|
191
|
+
"PATH_OUTSIDE_SANDBOX",
|
|
192
|
+
`Path "${inputPath}" resolves to "${real}" which is outside sandbox "${normalizedRoot}"`,
|
|
193
|
+
{ inputPath, resolvedPath: real, sandboxRoot: normalizedRoot }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return real;
|
|
197
|
+
}
|
|
198
|
+
function isWithinRoot(path, root) {
|
|
199
|
+
const normalizedPath = normalize(path);
|
|
200
|
+
const normalizedRoot = normalize(root);
|
|
201
|
+
return normalizedPath === normalizedRoot || normalizedPath.startsWith(normalizedRoot + "/");
|
|
202
|
+
}
|
|
11
203
|
|
|
12
204
|
// src/tools/mcp/MCPClientAdapter.ts
|
|
13
205
|
function textFromCallToolResult(result) {
|
|
@@ -118,6 +310,6 @@ async function registerMCPToolsFromConfig(runtime, registry, options = {}) {
|
|
|
118
310
|
return { transport };
|
|
119
311
|
}
|
|
120
312
|
|
|
121
|
-
export { MCPClientAdapter, MCPProcessManager, connectMCP, mcpToolsToSpecs, registerMCPToolsFromConfig };
|
|
313
|
+
export { MCPClientAdapter, MCPProcessManager, connectMCP, isIpInBlockedCidrs, mcpToolsToSpecs, registerMCPToolsFromConfig, resolveSandboxedPath, validateUrl };
|
|
122
314
|
//# sourceMappingURL=index.js.map
|
|
123
315
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/mcp/MCPClientAdapter.ts","../src/tools/mcp/connectMCP.ts","../src/tools/mcp/MCPProcessManager.ts","../src/tools/mcp/registerMCPTools.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ToolAdapter for MCP tools: holds SDK Client reference, invoke() calls client.callTool.\n * Framework-level support so scripts use registerMCPToolsFromConfig instead of inline MCP logic.\n */\n\nimport type { ToolAdapter, ToolSpec } from \"../../core/types/ToolSpec.js\";\nimport type { ExecContext } from \"../../core/types/ToolIntent.js\";\nimport { MCP_KIND } from \"./types.js\";\n\n/** SDK Client type (avoid hard typing on @modelcontextprotocol/sdk in this file for optional dep). */\nexport interface MCPClientLike {\n callTool(params: { name: string; arguments: Record<string, unknown> }): Promise<CallToolResult>;\n}\n\nexport interface CallToolResult {\n content?: Array<{ type: string; text?: string }>;\n toolResult?: unknown;\n}\n\nfunction textFromCallToolResult(result: CallToolResult): string {\n if (result.content?.length) {\n const parts = result.content\n .filter((c) => c.type === \"text\" && c.text != null)\n .map((c) => c.text);\n if (parts.length) return parts.join(\"\\n\");\n }\n if (result.toolResult !== undefined) return JSON.stringify(result.toolResult);\n return JSON.stringify(result);\n}\n\n/**\n * Adapter that invokes MCP tools via the SDK Client (callTool).\n * Register with runtime after connecting and listing tools; specs must have kind MCP_KIND.\n */\nexport class MCPClientAdapter implements ToolAdapter {\n readonly kind = MCP_KIND;\n\n constructor(private readonly client: MCPClientLike) {}\n\n async invoke(\n spec: ToolSpec,\n args: unknown,\n _ctx: ExecContext\n ): Promise<{ result: unknown; raw?: unknown }> {\n const params = args != null && typeof args === \"object\" && !Array.isArray(args)\n ? (args as Record<string, unknown>)\n : {};\n const result = await this.client.callTool({ name: spec.name, arguments: params });\n const text = textFromCallToolResult(result);\n let resultValue: unknown = text;\n try {\n resultValue = JSON.parse(text) as unknown;\n } catch {\n // keep as string\n }\n return { result: resultValue, raw: result };\n }\n}\n","/**\n * Connect to an MCP server via stdio using MCPConnectionInfo.\n * Framework-level: uses @modelcontextprotocol/sdk Client + StdioClientTransport.\n */\n\nimport type { MCPConnectionInfo } from \"./MCPProcessManager.js\";\nimport type { ToolSpec } from \"../../core/types/ToolSpec.js\";\nimport { DEFAULT_INPUT_SCHEMA, DEFAULT_OUTPUT_SCHEMA } from \"../../core/types/ToolSpec.js\";\nimport { MCP_KIND } from \"./types.js\";\n\nexport interface MCPConnectResult {\n /** SDK Client; use listTools() and callTool(). */\n client: MCPClientLike;\n /** Transport; call close() when done. */\n transport: MCPTransportLike;\n}\n\nexport interface MCPClientLike {\n connect(transport: MCPTransportLike): Promise<void>;\n listTools(params?: unknown): Promise<{ tools: MCPToolDef[] }>;\n callTool(params: { name: string; arguments: Record<string, unknown> }): Promise<CallToolResult>;\n}\n\nexport interface MCPTransportLike {\n close(): Promise<void>;\n}\n\nexport interface MCPToolDef {\n name: string;\n description?: string;\n inputSchema?: object;\n}\n\nexport interface CallToolResult {\n content?: Array<{ type: string; text?: string }>;\n toolResult?: unknown;\n}\n\n/**\n * Connect to MCP server via stdio. Only stdio is supported; url/SSE not implemented here.\n * Requires @modelcontextprotocol/sdk. Returns client and transport; caller must call transport.close() when done.\n */\nexport async function connectMCP(connectionInfo: MCPConnectionInfo): Promise<MCPConnectResult> {\n if (connectionInfo.type !== \"stdio\" || !connectionInfo.command) {\n throw new Error(\n `MCP stdio requires connectionInfo.type \"stdio\" and command; got type=${connectionInfo.type}`\n );\n }\n\n const { Client } = await import(\"@modelcontextprotocol/sdk/client\");\n const { StdioClientTransport } = await import(\n \"@modelcontextprotocol/sdk/client/stdio.js\"\n );\n\n const serverParams = {\n command: connectionInfo.command,\n args: connectionInfo.args ?? [],\n env: connectionInfo.env,\n cwd: connectionInfo.cwd,\n };\n const transport = new StdioClientTransport(serverParams);\n const client = new Client({ name: \"agent-tool\", version: \"1.0.0\" });\n await client.connect(transport);\n return {\n client: client as unknown as MCPClientLike,\n transport: transport as unknown as MCPTransportLike,\n };\n}\n\n/**\n * Convert MCP listTools() result to ToolSpec[] (kind MCP_KIND) for registry.\n */\nexport function mcpToolsToSpecs(tools: MCPToolDef[]): ToolSpec[] {\n return tools.map((t) => ({\n name: t.name,\n version: \"1.0.0\",\n kind: MCP_KIND,\n description: t.description ?? `MCP tool: ${t.name}`,\n inputSchema: t.inputSchema ?? DEFAULT_INPUT_SCHEMA,\n outputSchema: DEFAULT_OUTPUT_SCHEMA,\n capabilities: [],\n }));\n}\n","import type { MCPServerConfig } from \"../discoveryFactory.js\";\n\n/**\n * Connection info for an MCP server.\n */\nexport interface MCPConnectionInfo {\n /** Transport type: \"url\" for SSE/HTTP, \"stdio\" for command-based */\n type: \"url\" | \"stdio\";\n /** SSE/HTTP URL (when type=\"url\") */\n url?: string;\n /** Command to spawn (when type=\"stdio\") */\n command?: string;\n /** Command arguments (when type=\"stdio\") */\n args?: string[];\n /** Environment variables (when type=\"stdio\") */\n env?: Record<string, string>;\n /** Working directory when spawning (when type=\"stdio\") */\n cwd?: string;\n}\n\n/**\n * Manages MCP server connection information extracted from directory discovery.\n */\nexport class MCPProcessManager {\n private readonly connections = new Map<string, MCPConnectionInfo>();\n\n getConnectionInfo(toolName: string, config: MCPServerConfig): MCPConnectionInfo {\n const cached = this.connections.get(toolName);\n if (cached) return cached;\n const info: MCPConnectionInfo = config.url\n ? { type: \"url\", url: config.url }\n : {\n type: \"stdio\",\n command: config.command!,\n args: config.args,\n env: config.env,\n cwd: config.cwd,\n };\n this.connections.set(toolName, info);\n return info;\n }\n\n remove(toolName: string): boolean {\n return this.connections.delete(toolName);\n }\n\n getToolNames(): string[] {\n return [...this.connections.keys()];\n }\n\n dispose(): void {\n this.connections.clear();\n }\n}\n","/**\n * High-level API: load mcp.json, connect to MCP server, register tools with runtime.\n * Framework-level so scripts call registerMCPToolsFromConfig() instead of inline MCP logic.\n */\n\nimport { basename, dirname, join } from \"node:path\";\nimport type { PTCRuntime } from \"../../core/runtime/PTCRuntime.js\";\nimport type { ToolRegistry } from \"../../core/registry/ToolRegistry.js\";\nimport { MCP_KIND } from \"./types.js\";\nimport { loadMCPTool } from \"./MCPLoader.js\";\nimport { MCPProcessManager } from \"./MCPProcessManager.js\";\nimport { connectMCP, mcpToolsToSpecs } from \"./connectMCP.js\";\nimport { MCPClientAdapter } from \"./MCPClientAdapter.js\";\n\nexport interface RegisterMCPToolsOptions {\n /** Path to mcp.json (or dir containing it). Default: process.env.MCP_CONFIG_PATH or \"mcp.json\" in cwd. */\n configPath?: string;\n /** Tool name for getConnectionInfo (default \"mcp\"). */\n toolName?: string;\n}\n\nexport interface RegisterMCPToolsResult {\n /** Transport; caller must call transport.close() when done. */\n transport: { close(): Promise<void> };\n}\n\n/**\n * Load mcp config (from configPath or MCP_CONFIG_PATH), connect via stdio, list tools,\n * register ToolSpec[] with registry and MCPClientAdapter with runtime. Returns transport for cleanup.\n */\nexport async function registerMCPToolsFromConfig(\n runtime: PTCRuntime,\n registry: ToolRegistry,\n options: RegisterMCPToolsOptions = {}\n): Promise<RegisterMCPToolsResult> {\n const configPath = options.configPath ?? process.env.MCP_CONFIG_PATH ?? join(process.cwd(), \"mcp.json\");\n const dirPath = dirname(configPath);\n const entryPoint = basename(configPath);\n const toolName = options.toolName ?? MCP_KIND;\n\n const loaded = await loadMCPTool(dirPath, { kind: MCP_KIND, name: toolName, entryPoint });\n if (!loaded.mcpConfig) {\n throw new Error(\"mcp.json must have command or url\");\n }\n const manager = new MCPProcessManager();\n const connectionInfo = manager.getConnectionInfo(toolName, loaded.mcpConfig);\n\n const { client, transport } = await connectMCP(connectionInfo);\n const { tools } = await client.listTools();\n const specs = mcpToolsToSpecs(tools);\n registry.bulkRegister(specs);\n runtime.registerAdapter(new MCPClientAdapter(client));\n\n return { transport };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/security/ssrf.ts","../src/security/sandbox.ts","../src/tools/mcp/MCPClientAdapter.ts","../src/tools/mcp/connectMCP.ts","../src/tools/mcp/MCPProcessManager.ts","../src/tools/mcp/registerMCPTools.ts"],"names":["dirname","basename"],"mappings":"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","file":"index.js","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { createTaggedError } from \"../core/runtime/Retry.js\";\n\n/**\n * Validate a URL against allowed hosts and blocked CIDRs.\n * Prevents SSRF by checking both hostname allowlist and resolved IP addresses.\n *\n * @throws HTTP_DISALLOWED_HOST if the URL is blocked\n */\nexport async function validateUrl(\n url: string,\n allowedHosts: string[],\n blockedCidrs: string[],\n): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Invalid URL: ${url}`,\n { url },\n );\n }\n\n // Only allow http/https\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,\n { url, protocol: parsed.protocol },\n );\n }\n\n const hostname = parsed.hostname;\n\n // Check allowlist\n if (!isHostAllowed(hostname, allowedHosts)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" is not in the allowed hosts list`,\n { url, hostname, allowedHosts },\n );\n }\n\n // DNS resolve and check against blocked CIDRs\n try {\n const { address } = await lookup(hostname);\n if (isIpInBlockedCidrs(address, blockedCidrs)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" resolves to blocked IP: ${address}`,\n { url, hostname, resolvedIp: address },\n );\n }\n } catch (err) {\n // Re-throw our tagged errors\n if (err instanceof Error && (err as any).kind === \"HTTP_DISALLOWED_HOST\") {\n throw err;\n }\n // DNS resolution failure — block by default\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `DNS resolution failed for host \"${hostname}\": ${err instanceof Error ? err.message : String(err)}`,\n { url, hostname },\n );\n }\n\n return parsed;\n}\n\n/**\n * Check if a hostname matches any entry in the allowed hosts list.\n * Supports: exact \"*\" (allow any host), wildcard prefix (e.g. \"*.github.com\"), or exact host.\n */\nfunction isHostAllowed(hostname: string, allowedHosts: string[]): boolean {\n for (const pattern of allowedHosts) {\n if (pattern === \"*\") {\n return true;\n }\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1); // \".github.com\"\n if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {\n return true;\n }\n } else if (hostname === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if an IPv4 address falls within any blocked CIDR range.\n */\nexport function isIpInBlockedCidrs(ip: string, cidrs: string[]): boolean {\n // Handle IPv4-mapped IPv6\n const normalizedIp = normalizeIp(ip);\n if (!normalizedIp) return false;\n\n for (const cidr of cidrs) {\n if (cidr.includes(\":\")) {\n // IPv6 CIDR — skip for IPv4 addresses\n if (!ip.includes(\":\")) continue;\n if (isIpv6InCidr(ip, cidr)) return true;\n } else {\n if (isIpv4InCidr(normalizedIp, cidr)) return true;\n }\n }\n return false;\n}\n\nfunction normalizeIp(ip: string): string | null {\n // Handle IPv4-mapped IPv6 (e.g. \"::ffff:127.0.0.1\")\n if (ip.startsWith(\"::ffff:\")) {\n return ip.slice(7);\n }\n // Pure IPv4\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n return ip;\n }\n return null;\n}\n\nfunction isIpv4InCidr(ip: string, cidr: string): boolean {\n const [cidrIp, prefixStr] = cidr.split(\"/\");\n if (!cidrIp || !prefixStr) return false;\n\n const prefix = parseInt(prefixStr, 10);\n if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;\n\n const ipNum = ipv4ToNum(ip);\n const cidrNum = ipv4ToNum(cidrIp);\n if (ipNum === null || cidrNum === null) return false;\n\n const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;\n return (ipNum & mask) === (cidrNum & mask);\n}\n\nfunction ipv4ToNum(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n let num = 0;\n for (const part of parts) {\n const n = parseInt(part, 10);\n if (isNaN(n) || n < 0 || n > 255) return null;\n num = (num << 8) | n;\n }\n return num >>> 0;\n}\n\nfunction isIpv6InCidr(ip: string, cidr: string): boolean {\n // Simplified IPv6 CIDR matching for common cases (::1, fc00::, fe80::)\n const [cidrIp, prefixStr] = cidr.split(\"/\");\n if (!cidrIp || !prefixStr) return false;\n\n const prefix = parseInt(prefixStr, 10);\n if (isNaN(prefix)) return false;\n\n const ipBytes = expandIpv6(ip);\n const cidrBytes = expandIpv6(cidrIp);\n if (!ipBytes || !cidrBytes) return false;\n\n // Compare prefix bits\n const fullBytes = Math.floor(prefix / 8);\n for (let i = 0; i < fullBytes && i < 16; i++) {\n if (ipBytes[i] !== cidrBytes[i]) return false;\n }\n\n const remainingBits = prefix % 8;\n if (remainingBits > 0 && fullBytes < 16) {\n const mask = (~0 << (8 - remainingBits)) & 0xff;\n if ((ipBytes[fullBytes]! & mask) !== (cidrBytes[fullBytes]! & mask)) return false;\n }\n\n return true;\n}\n\nfunction expandIpv6(ip: string): number[] | null {\n // Remove zone ID\n const zoneIdx = ip.indexOf(\"%\");\n if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);\n\n const parts = ip.split(\"::\");\n if (parts.length > 2) return null;\n\n const bytes: number[] = new Array(16).fill(0);\n\n const expandGroup = (group: string): number[] => {\n if (!group) return [];\n return group.split(\":\").flatMap((hex) => {\n const val = parseInt(hex || \"0\", 16);\n return [(val >> 8) & 0xff, val & 0xff];\n });\n };\n\n if (parts.length === 1) {\n const expanded = expandGroup(parts[0]!);\n if (expanded.length !== 16) return null;\n return expanded;\n }\n\n const left = expandGroup(parts[0]!);\n const right = expandGroup(parts[1]!);\n\n if (left.length + right.length > 16) return null;\n\n for (let i = 0; i < left.length; i++) bytes[i] = left[i]!;\n for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i]!;\n\n return bytes;\n}\n","import { resolve, normalize, dirname, basename } from \"node:path\";\nimport { realpath, access } from \"node:fs/promises\";\nimport { createTaggedError } from \"../core/runtime/Retry.js\";\n\n/**\n * Resolve an input path to an absolute path within the sandbox.\n * Throws PATH_OUTSIDE_SANDBOX if the resolved path escapes the sandbox root.\n *\n * For existing files: uses realpath to resolve symlinks.\n * For non-existing files (write targets): resolves the parent directory.\n */\nexport async function resolveSandboxedPath(\n inputPath: string,\n sandboxRoot: string,\n): Promise<string> {\n // Resolve the sandbox root itself with realpath to handle platform symlinks\n // (e.g. macOS /var -> /private/var)\n let normalizedRoot: string;\n try {\n normalizedRoot = await realpath(resolve(sandboxRoot));\n } catch {\n normalizedRoot = normalize(resolve(sandboxRoot));\n }\n\n // Resolve against sandbox root\n const resolved = resolve(normalizedRoot, inputPath);\n\n let real: string;\n try {\n // Try to resolve symlinks for existing paths\n await access(resolved);\n real = await realpath(resolved);\n } catch {\n // Path does not exist — resolve parent to check containment\n const parentDir = dirname(resolved);\n let realParent: string;\n try {\n await access(parentDir);\n realParent = await realpath(parentDir);\n } catch {\n // Parent also doesn't exist — use normalized resolved path\n // (will fail at actual FS operation if truly invalid)\n realParent = normalize(parentDir);\n }\n real = resolve(realParent, basename(resolved));\n }\n\n if (!isWithinRoot(real, normalizedRoot)) {\n throw createTaggedError(\n \"PATH_OUTSIDE_SANDBOX\",\n `Path \"${inputPath}\" resolves to \"${real}\" which is outside sandbox \"${normalizedRoot}\"`,\n { inputPath, resolvedPath: real, sandboxRoot: normalizedRoot },\n );\n }\n\n return real;\n}\n\nfunction isWithinRoot(path: string, root: string): boolean {\n const normalizedPath = normalize(path);\n const normalizedRoot = normalize(root);\n return normalizedPath === normalizedRoot || normalizedPath.startsWith(normalizedRoot + \"/\");\n}\n","/**\n * ToolAdapter for MCP tools: holds SDK Client reference, invoke() calls client.callTool.\n * Framework-level support so scripts use registerMCPToolsFromConfig instead of inline MCP logic.\n */\n\nimport type { ToolAdapter, ToolSpec } from \"../../core/types/ToolSpec.js\";\nimport type { ExecContext } from \"../../core/types/ToolIntent.js\";\nimport { MCP_KIND } from \"./types.js\";\n\n/** SDK Client type (avoid hard typing on @modelcontextprotocol/sdk in this file for optional dep). */\nexport interface MCPClientLike {\n callTool(params: { name: string; arguments: Record<string, unknown> }): Promise<CallToolResult>;\n}\n\nexport interface CallToolResult {\n content?: Array<{ type: string; text?: string }>;\n toolResult?: unknown;\n}\n\nfunction textFromCallToolResult(result: CallToolResult): string {\n if (result.content?.length) {\n const parts = result.content\n .filter((c) => c.type === \"text\" && c.text != null)\n .map((c) => c.text);\n if (parts.length) return parts.join(\"\\n\");\n }\n if (result.toolResult !== undefined) return JSON.stringify(result.toolResult);\n return JSON.stringify(result);\n}\n\n/**\n * Adapter that invokes MCP tools via the SDK Client (callTool).\n * Register with runtime after connecting and listing tools; specs must have kind MCP_KIND.\n */\nexport class MCPClientAdapter implements ToolAdapter {\n readonly kind = MCP_KIND;\n\n constructor(private readonly client: MCPClientLike) {}\n\n async invoke(\n spec: ToolSpec,\n args: unknown,\n _ctx: ExecContext\n ): Promise<{ result: unknown; raw?: unknown }> {\n const params = args != null && typeof args === \"object\" && !Array.isArray(args)\n ? (args as Record<string, unknown>)\n : {};\n const result = await this.client.callTool({ name: spec.name, arguments: params });\n const text = textFromCallToolResult(result);\n let resultValue: unknown = text;\n try {\n resultValue = JSON.parse(text) as unknown;\n } catch {\n // keep as string\n }\n return { result: resultValue, raw: result };\n }\n}\n","/**\n * Connect to an MCP server via stdio using MCPConnectionInfo.\n * Framework-level: uses @modelcontextprotocol/sdk Client + StdioClientTransport.\n */\n\nimport type { MCPConnectionInfo } from \"./MCPProcessManager.js\";\nimport type { ToolSpec } from \"../../core/types/ToolSpec.js\";\nimport { DEFAULT_INPUT_SCHEMA, DEFAULT_OUTPUT_SCHEMA } from \"../../core/types/ToolSpec.js\";\nimport { MCP_KIND } from \"./types.js\";\n\nexport interface MCPConnectResult {\n /** SDK Client; use listTools() and callTool(). */\n client: MCPClientLike;\n /** Transport; call close() when done. */\n transport: MCPTransportLike;\n}\n\nexport interface MCPClientLike {\n connect(transport: MCPTransportLike): Promise<void>;\n listTools(params?: unknown): Promise<{ tools: MCPToolDef[] }>;\n callTool(params: { name: string; arguments: Record<string, unknown> }): Promise<CallToolResult>;\n}\n\nexport interface MCPTransportLike {\n close(): Promise<void>;\n}\n\nexport interface MCPToolDef {\n name: string;\n description?: string;\n inputSchema?: object;\n}\n\nexport interface CallToolResult {\n content?: Array<{ type: string; text?: string }>;\n toolResult?: unknown;\n}\n\n/**\n * Connect to MCP server via stdio. Only stdio is supported; url/SSE not implemented here.\n * Requires @modelcontextprotocol/sdk. Returns client and transport; caller must call transport.close() when done.\n */\nexport async function connectMCP(connectionInfo: MCPConnectionInfo): Promise<MCPConnectResult> {\n if (connectionInfo.type !== \"stdio\" || !connectionInfo.command) {\n throw new Error(\n `MCP stdio requires connectionInfo.type \"stdio\" and command; got type=${connectionInfo.type}`\n );\n }\n\n const { Client } = await import(\"@modelcontextprotocol/sdk/client\");\n const { StdioClientTransport } = await import(\n \"@modelcontextprotocol/sdk/client/stdio.js\"\n );\n\n const serverParams = {\n command: connectionInfo.command,\n args: connectionInfo.args ?? [],\n env: connectionInfo.env,\n cwd: connectionInfo.cwd,\n };\n const transport = new StdioClientTransport(serverParams);\n const client = new Client({ name: \"agent-tool\", version: \"1.0.0\" });\n await client.connect(transport);\n return {\n client: client as unknown as MCPClientLike,\n transport: transport as unknown as MCPTransportLike,\n };\n}\n\n/**\n * Convert MCP listTools() result to ToolSpec[] (kind MCP_KIND) for registry.\n */\nexport function mcpToolsToSpecs(tools: MCPToolDef[]): ToolSpec[] {\n return tools.map((t) => ({\n name: t.name,\n version: \"1.0.0\",\n kind: MCP_KIND,\n description: t.description ?? `MCP tool: ${t.name}`,\n inputSchema: t.inputSchema ?? DEFAULT_INPUT_SCHEMA,\n outputSchema: DEFAULT_OUTPUT_SCHEMA,\n capabilities: [],\n }));\n}\n","import type { MCPServerConfig } from \"../discoveryFactory.js\";\n\n/**\n * Connection info for an MCP server.\n */\nexport interface MCPConnectionInfo {\n /** Transport type: \"url\" for SSE/HTTP, \"stdio\" for command-based */\n type: \"url\" | \"stdio\";\n /** SSE/HTTP URL (when type=\"url\") */\n url?: string;\n /** Command to spawn (when type=\"stdio\") */\n command?: string;\n /** Command arguments (when type=\"stdio\") */\n args?: string[];\n /** Environment variables (when type=\"stdio\") */\n env?: Record<string, string>;\n /** Working directory when spawning (when type=\"stdio\") */\n cwd?: string;\n}\n\n/**\n * Manages MCP server connection information extracted from directory discovery.\n */\nexport class MCPProcessManager {\n private readonly connections = new Map<string, MCPConnectionInfo>();\n\n getConnectionInfo(toolName: string, config: MCPServerConfig): MCPConnectionInfo {\n const cached = this.connections.get(toolName);\n if (cached) return cached;\n const info: MCPConnectionInfo = config.url\n ? { type: \"url\", url: config.url }\n : {\n type: \"stdio\",\n command: config.command!,\n args: config.args,\n env: config.env,\n cwd: config.cwd,\n };\n this.connections.set(toolName, info);\n return info;\n }\n\n remove(toolName: string): boolean {\n return this.connections.delete(toolName);\n }\n\n getToolNames(): string[] {\n return [...this.connections.keys()];\n }\n\n dispose(): void {\n this.connections.clear();\n }\n}\n","/**\n * High-level API: load mcp.json, connect to MCP server, register tools with runtime.\n * Framework-level so scripts call registerMCPToolsFromConfig() instead of inline MCP logic.\n */\n\nimport { basename, dirname, join } from \"node:path\";\nimport type { PTCRuntime } from \"../../core/runtime/PTCRuntime.js\";\nimport type { ToolRegistry } from \"../../core/registry/ToolRegistry.js\";\nimport { MCP_KIND } from \"./types.js\";\nimport { loadMCPTool } from \"./MCPLoader.js\";\nimport { MCPProcessManager } from \"./MCPProcessManager.js\";\nimport { connectMCP, mcpToolsToSpecs } from \"./connectMCP.js\";\nimport { MCPClientAdapter } from \"./MCPClientAdapter.js\";\n\nexport interface RegisterMCPToolsOptions {\n /** Path to mcp.json (or dir containing it). Default: process.env.MCP_CONFIG_PATH or \"mcp.json\" in cwd. */\n configPath?: string;\n /** Tool name for getConnectionInfo (default \"mcp\"). */\n toolName?: string;\n}\n\nexport interface RegisterMCPToolsResult {\n /** Transport; caller must call transport.close() when done. */\n transport: { close(): Promise<void> };\n}\n\n/**\n * Load mcp config (from configPath or MCP_CONFIG_PATH), connect via stdio, list tools,\n * register ToolSpec[] with registry and MCPClientAdapter with runtime. Returns transport for cleanup.\n */\nexport async function registerMCPToolsFromConfig(\n runtime: PTCRuntime,\n registry: ToolRegistry,\n options: RegisterMCPToolsOptions = {}\n): Promise<RegisterMCPToolsResult> {\n const configPath = options.configPath ?? process.env.MCP_CONFIG_PATH ?? join(process.cwd(), \"mcp.json\");\n const dirPath = dirname(configPath);\n const entryPoint = basename(configPath);\n const toolName = options.toolName ?? MCP_KIND;\n\n const loaded = await loadMCPTool(dirPath, { kind: MCP_KIND, name: toolName, entryPoint });\n if (!loaded.mcpConfig) {\n throw new Error(\"mcp.json must have command or url\");\n }\n const manager = new MCPProcessManager();\n const connectionInfo = manager.getConnectionInfo(toolName, loaded.mcpConfig);\n\n const { client, transport } = await connectMCP(connectionInfo);\n const { tools } = await client.listTools();\n const specs = mcpToolsToSpecs(tools);\n registry.bulkRegister(specs);\n runtime.registerAdapter(new MCPClientAdapter(client));\n\n return { transport };\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve an input path to an absolute path within the sandbox.
|
|
3
|
+
* Throws PATH_OUTSIDE_SANDBOX if the resolved path escapes the sandbox root.
|
|
4
|
+
*
|
|
5
|
+
* For existing files: uses realpath to resolve symlinks.
|
|
6
|
+
* For non-existing files (write targets): resolves the parent directory.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveSandboxedPath(inputPath: string, sandboxRoot: string): Promise<string>;
|
|
9
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/security/sandbox.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CA0CjB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a URL against allowed hosts and blocked CIDRs.
|
|
3
|
+
* Prevents SSRF by checking both hostname allowlist and resolved IP addresses.
|
|
4
|
+
*
|
|
5
|
+
* @throws HTTP_DISALLOWED_HOST if the URL is blocked
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateUrl(url: string, allowedHosts: string[], blockedCidrs: string[]): Promise<URL>;
|
|
8
|
+
/**
|
|
9
|
+
* Check if an IPv4 address falls within any blocked CIDR range.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isIpInBlockedCidrs(ip: string, cidrs: string[]): boolean;
|
|
12
|
+
//# sourceMappingURL=ssrf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf.d.ts","sourceRoot":"","sources":["../../src/security/ssrf.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EAAE,EACtB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,GAAG,CAAC,CAwDd;AAuBD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAevE"}
|
package/dist/utils/cli/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
require('../../chunk-SOFUWEZ6.cjs');
|
|
4
|
+
var chunkRSDA7IAQ_cjs = require('../../chunk-RSDA7IAQ.cjs');
|
|
6
5
|
var chunkPVVUTEZP_cjs = require('../../chunk-PVVUTEZP.cjs');
|
|
7
6
|
var chunkCDTLERB6_cjs = require('../../chunk-CDTLERB6.cjs');
|
|
7
|
+
require('../../chunk-SOFUWEZ6.cjs');
|
|
8
8
|
require('../../chunk-ZNJBRLKN.cjs');
|
|
9
9
|
var chunk6F5JHLZ7_cjs = require('../../chunk-6F5JHLZ7.cjs');
|
|
10
10
|
var path = require('path');
|
|
@@ -165,7 +165,7 @@ For agent run (ReAct + agent.yaml): npx @easynet/agent-orchestra run "task" -c a
|
|
|
165
165
|
}
|
|
166
166
|
async function cmdInit(pathOpt) {
|
|
167
167
|
try {
|
|
168
|
-
const { targetPath, filesCreated } = await
|
|
168
|
+
const { targetPath, filesCreated } = await chunkRSDA7IAQ_cjs.initProject({ targetPath: pathOpt || void 0 });
|
|
169
169
|
process.stdout.write(`Initialized project at ${targetPath}
|
|
170
170
|
Created: ${filesCreated.join(", ")}
|
|
171
171
|
Next: npm install && npm run build
|
|
@@ -179,7 +179,7 @@ Next: npm install && npm run build
|
|
|
179
179
|
}
|
|
180
180
|
async function cmdBuild(pathOpt, outDir, include, includeN8n) {
|
|
181
181
|
try {
|
|
182
|
-
const result = await
|
|
182
|
+
const result = await chunkRSDA7IAQ_cjs.buildMcpPackage({
|
|
183
183
|
projectPath: pathOpt || void 0,
|
|
184
184
|
outDir: outDir || void 0,
|
|
185
185
|
include: include.length > 0 ? include : void 0,
|
|
@@ -200,7 +200,7 @@ mcp.json: ${result.mcpJsonPath}
|
|
|
200
200
|
}
|
|
201
201
|
async function cmdServe(pathOpt) {
|
|
202
202
|
try {
|
|
203
|
-
const { process: child } = await
|
|
203
|
+
const { process: child } = await chunkRSDA7IAQ_cjs.runMcpServer({ path: pathOpt || void 0 });
|
|
204
204
|
child.stdin?.pipe(process.stdin);
|
|
205
205
|
child.stdout?.pipe(process.stdout);
|
|
206
206
|
process.stdin?.unref();
|
package/dist/utils/cli/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { initProject, buildMcpPackage, runMcpServer } from '../../chunk-
|
|
3
|
-
import '../../chunk-5SWSNVMI.js';
|
|
2
|
+
import { initProject, buildMcpPackage, runMcpServer } from '../../chunk-LIERSUS5.js';
|
|
4
3
|
import { runMCPServerOverStdio, createMCPServerStreamableHttp, createHttpService } from '../../chunk-SIN5JHMP.js';
|
|
5
4
|
import { findAndLoadToolConfig, resolveToolDescriptor, resolveSandboxedPath, createRuntimeFromConfig, getDisplayScope } from '../../chunk-BNHLMLDH.js';
|
|
5
|
+
import '../../chunk-5SWSNVMI.js';
|
|
6
6
|
import '../../chunk-AE6FSNGY.js';
|
|
7
7
|
import { enrichSpecWithCanonicalSchema } from '../../chunk-FR2CXERF.js';
|
|
8
8
|
import path from 'path';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easynet/agent-tool",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "MCP build: init → build → run. Turn your project (@tool, SKILL, n8n) into a standalone MCP npm server (no framework embedding, no package conflict).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|