@agimon-ai/mcp-proxy 0.7.1 → 0.7.3
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/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +39 -20
- package/dist/index.d.mts +38 -20
- package/dist/index.mjs +1 -1
- package/dist/{src-DyWwAp8e.cjs → src-Dp2m9_I_.cjs} +182 -90
- package/dist/{src-NOWQwaRe.mjs → src-Y-cyyxaw.mjs} +182 -90
- package/package.json +6 -5
|
@@ -25,13 +25,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
|
|
27
27
|
//#endregion
|
|
28
|
+
let __agimon_ai_foundation_validator = require("@agimon-ai/foundation-validator");
|
|
28
29
|
let __modelcontextprotocol_sdk_server_index_js = require("@modelcontextprotocol/sdk/server/index.js");
|
|
29
30
|
let __modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
|
|
31
|
+
let zod = require("zod");
|
|
30
32
|
let node_fs = require("node:fs");
|
|
31
33
|
let node_fs_promises = require("node:fs/promises");
|
|
32
34
|
let js_yaml = require("js-yaml");
|
|
33
35
|
js_yaml = __toESM(js_yaml);
|
|
34
|
-
let zod = require("zod");
|
|
35
36
|
let node_crypto = require("node:crypto");
|
|
36
37
|
let node_os = require("node:os");
|
|
37
38
|
let node_path = require("node:path");
|
|
@@ -52,7 +53,7 @@ let __modelcontextprotocol_sdk_server_sse_js = require("@modelcontextprotocol/sd
|
|
|
52
53
|
let __modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
53
54
|
|
|
54
55
|
//#region package.json
|
|
55
|
-
var version = "0.7.
|
|
56
|
+
var version = "0.7.2";
|
|
56
57
|
|
|
57
58
|
//#endregion
|
|
58
59
|
//#region src/utils/mcpConfigSchema.ts
|
|
@@ -1365,6 +1366,9 @@ var DefinitionsCacheService = class {
|
|
|
1365
1366
|
async getServersForTool(toolName) {
|
|
1366
1367
|
return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.tools.some((tool) => tool.name === toolName)).map((serverDefinition) => serverDefinition.serverName);
|
|
1367
1368
|
}
|
|
1369
|
+
async getToolSchema(serverName, toolName) {
|
|
1370
|
+
return (await this.getDefinitions()).servers[serverName]?.tools.find((t) => t.name === toolName)?.inputSchema;
|
|
1371
|
+
}
|
|
1368
1372
|
async getServersForResource(uri) {
|
|
1369
1373
|
return (await this.getServerDefinitions()).filter((serverDefinition) => serverDefinition.resources.some((resource) => resource.uri === uri)).map((serverDefinition) => serverDefinition.serverName);
|
|
1370
1374
|
}
|
|
@@ -1810,7 +1814,7 @@ var McpClientManagerService = class {
|
|
|
1810
1814
|
async createConnection(serverName, config) {
|
|
1811
1815
|
const timeoutMs = config.timeout ?? DEFAULT_CONNECTION_TIMEOUT_MS;
|
|
1812
1816
|
const client = new __modelcontextprotocol_sdk_client_index_js.Client({
|
|
1813
|
-
name:
|
|
1817
|
+
name: "@agimon-ai/mcp-proxy-client",
|
|
1814
1818
|
version: "0.1.0"
|
|
1815
1819
|
}, { capabilities: {} });
|
|
1816
1820
|
const mcpClient = new McpClient(serverName, config.transport, client, this.logger, {
|
|
@@ -2994,7 +2998,7 @@ var PrefetchService = class {
|
|
|
2994
2998
|
|
|
2995
2999
|
//#endregion
|
|
2996
3000
|
//#region src/templates/toolkit-description.liquid?raw
|
|
2997
|
-
var toolkit_description_default = "<toolkit id=\"{{ serverId }}\">\n<instruction>\nBefore you use any capabilities below, you MUST call this tool with a list of names to learn how to use them properly; this includes:\n- For tools: Arguments schema needed to pass to use_tool\n- For skills: Detailed instructions that will expand when invoked (Prefer to be explored first when relevant)\n\nThis tool is optimized for batch queries - you can request multiple capabilities at once for better performance.\n\nHow to invoke:\n- For MCP tools: Use use_tool with toolName and toolArgs based on the schema\n- For skills: Use this tool with the skill name to get expanded instructions\n</instruction>\n\n<available_capabilities>\n{% for server in servers -%}\n<group
|
|
3001
|
+
var toolkit_description_default = "<toolkit id=\"{{ serverId }}\">\n<instruction>\nBefore you use any capabilities below, you MUST call this tool with a list of names to learn how to use them properly; this includes:\n- For tools: Arguments schema needed to pass to use_tool\n- For skills: Detailed instructions that will expand when invoked (Prefer to be explored first when relevant)\n\nThis tool is optimized for batch queries - you can request multiple capabilities at once for better performance.\n\nHow to invoke:\n- For MCP tools: Use use_tool with toolName and toolArgs based on the schema\n- For skills: Use this tool with the skill name to get expanded instructions\n</instruction>\n\n<available_capabilities>\n{% for server in servers -%}\n<group original-mcp-server=\"{{ server.name }}\">\n{% if server.instruction -%}\n<group_instruction>{{ server.instruction }}</group_instruction>\n{% endif -%}\n{% if server.omitToolDescription -%}\n{% for toolName in server.toolNames -%}\n<item name=\"{{ toolName }}\"></item>\n{% endfor -%}\n{% else -%}\n{% for tool in server.tools -%}\n<item name=\"{{ tool.displayName }}\"><description>{{ tool.description | default: \"No description\" }}</description></item>\n{% endfor -%}\n{% endif -%}\n</group>\n{% endfor -%}\n{% if skills.size > 0 -%}\n<group name=\"skills\">\n{% for skill in skills -%}\n<item name=\"{{ skill.displayName }}\"><description>{{ skill.description }}</description></item>\n{% endfor -%}\n</group>\n{% endif -%}\n</available_capabilities>\n</toolkit>\n";
|
|
2998
3002
|
|
|
2999
3003
|
//#endregion
|
|
3000
3004
|
//#region src/tools/DescribeToolsTool.ts
|
|
@@ -3010,6 +3014,11 @@ function formatSkillInstructions(name, instructions) {
|
|
|
3010
3014
|
return `<command-message>The "${name}" skill is loading</command-message>\n${instructions}`;
|
|
3011
3015
|
}
|
|
3012
3016
|
/**
|
|
3017
|
+
* Input schema for the DescribeToolsTool
|
|
3018
|
+
* @property toolNames - Array of tool names to get detailed information about
|
|
3019
|
+
*/
|
|
3020
|
+
const DescribeToolsToolInputSchema = zod.z.object({ toolNames: zod.z.array(zod.z.string().min(1)).min(1).describe("List of tool names to get detailed information about") });
|
|
3021
|
+
/**
|
|
3013
3022
|
* DescribeToolsTool provides progressive disclosure of MCP tools and skills.
|
|
3014
3023
|
*
|
|
3015
3024
|
* This tool lists available tools from all connected MCP servers and skills
|
|
@@ -3266,25 +3275,15 @@ var DescribeToolsTool = class DescribeToolsTool {
|
|
|
3266
3275
|
*
|
|
3267
3276
|
* @returns Tool definition with description and input schema
|
|
3268
3277
|
*/
|
|
3278
|
+
getInputSchema() {
|
|
3279
|
+
return DescribeToolsToolInputSchema;
|
|
3280
|
+
}
|
|
3269
3281
|
async getDefinition() {
|
|
3270
3282
|
const { content } = await this.buildToolkitDescription();
|
|
3271
3283
|
return {
|
|
3272
3284
|
name: DescribeToolsTool.TOOL_NAME,
|
|
3273
3285
|
description: content,
|
|
3274
|
-
inputSchema: {
|
|
3275
|
-
type: "object",
|
|
3276
|
-
properties: { toolNames: {
|
|
3277
|
-
type: "array",
|
|
3278
|
-
items: {
|
|
3279
|
-
type: "string",
|
|
3280
|
-
minLength: 1
|
|
3281
|
-
},
|
|
3282
|
-
description: "List of tool names to get detailed information about",
|
|
3283
|
-
minItems: 1
|
|
3284
|
-
} },
|
|
3285
|
-
required: ["toolNames"],
|
|
3286
|
-
additionalProperties: false
|
|
3287
|
-
}
|
|
3286
|
+
inputSchema: zod.z.toJSONSchema(DescribeToolsToolInputSchema, { reused: "inline" })
|
|
3288
3287
|
};
|
|
3289
3288
|
}
|
|
3290
3289
|
/**
|
|
@@ -3298,9 +3297,9 @@ var DescribeToolsTool = class DescribeToolsTool {
|
|
|
3298
3297
|
* @param input - Object containing toolNames array
|
|
3299
3298
|
* @returns CallToolResult with tool/skill descriptions or error
|
|
3300
3299
|
*/
|
|
3301
|
-
async execute(
|
|
3300
|
+
async execute(rawInput) {
|
|
3302
3301
|
try {
|
|
3303
|
-
const { toolNames } =
|
|
3302
|
+
const { toolNames } = DescribeToolsToolInputSchema.parse(rawInput);
|
|
3304
3303
|
const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
|
|
3305
3304
|
if (!toolNames || toolNames.length === 0) return {
|
|
3306
3305
|
content: [{
|
|
@@ -3474,6 +3473,10 @@ function getUniqueSortedCapabilities(tools) {
|
|
|
3474
3473
|
|
|
3475
3474
|
//#endregion
|
|
3476
3475
|
//#region src/tools/SearchListToolsTool.ts
|
|
3476
|
+
const SearchListToolsToolInputSchema = zod.z.object({
|
|
3477
|
+
capability: zod.z.string().optional().describe("Optional capability filter. Matches explicit capability tags first, then server summaries, server names, tool names, and tool descriptions."),
|
|
3478
|
+
serverName: zod.z.string().optional().describe("Optional server name filter.")
|
|
3479
|
+
});
|
|
3477
3480
|
var SearchListToolsTool = class SearchListToolsTool {
|
|
3478
3481
|
static TOOL_NAME = "list_tools";
|
|
3479
3482
|
constructor(_clientManager, definitionsCacheService) {
|
|
@@ -3483,6 +3486,9 @@ var SearchListToolsTool = class SearchListToolsTool {
|
|
|
3483
3486
|
formatToolName(toolName, serverName, toolToServers) {
|
|
3484
3487
|
return (toolToServers.get(toolName) || []).length > 1 ? `${serverName}__${toolName}` : toolName;
|
|
3485
3488
|
}
|
|
3489
|
+
getInputSchema() {
|
|
3490
|
+
return SearchListToolsToolInputSchema;
|
|
3491
|
+
}
|
|
3486
3492
|
async getDefinition() {
|
|
3487
3493
|
const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
|
|
3488
3494
|
const capabilitySummary = serverDefinitions.length > 0 ? serverDefinitions.map((server) => {
|
|
@@ -3493,23 +3499,11 @@ var SearchListToolsTool = class SearchListToolsTool {
|
|
|
3493
3499
|
return {
|
|
3494
3500
|
name: SearchListToolsTool.TOOL_NAME,
|
|
3495
3501
|
description: `Search proxied MCP tools by server capability summary.\n\nAvailable capabilities:\n${capabilitySummary}`,
|
|
3496
|
-
inputSchema: {
|
|
3497
|
-
type: "object",
|
|
3498
|
-
properties: {
|
|
3499
|
-
capability: {
|
|
3500
|
-
type: "string",
|
|
3501
|
-
description: "Optional capability filter. Matches explicit capability tags first, then server summaries, server names, tool names, and tool descriptions."
|
|
3502
|
-
},
|
|
3503
|
-
serverName: {
|
|
3504
|
-
type: "string",
|
|
3505
|
-
description: "Optional server name filter."
|
|
3506
|
-
}
|
|
3507
|
-
},
|
|
3508
|
-
additionalProperties: false
|
|
3509
|
-
}
|
|
3502
|
+
inputSchema: zod.z.toJSONSchema(SearchListToolsToolInputSchema, { reused: "inline" })
|
|
3510
3503
|
};
|
|
3511
3504
|
}
|
|
3512
|
-
async execute(
|
|
3505
|
+
async execute(rawInput) {
|
|
3506
|
+
const input = SearchListToolsToolInputSchema.parse(rawInput);
|
|
3513
3507
|
const serverDefinitions = await this.definitionsCacheService.getServerDefinitions();
|
|
3514
3508
|
const capabilityFilter = input.capability?.trim().toLowerCase();
|
|
3515
3509
|
const serverNameFilter = input.serverName?.trim().toLowerCase();
|
|
@@ -3552,6 +3546,39 @@ var SearchListToolsTool = class SearchListToolsTool {
|
|
|
3552
3546
|
//#endregion
|
|
3553
3547
|
//#region src/tools/UseToolTool.ts
|
|
3554
3548
|
/**
|
|
3549
|
+
* UseToolTool - Progressive disclosure tool for calling MCP tools and skills
|
|
3550
|
+
*
|
|
3551
|
+
* DESIGN PATTERNS:
|
|
3552
|
+
* - Tool pattern with getDefinition() and execute() methods
|
|
3553
|
+
* - Dependency injection for client manager and skill service
|
|
3554
|
+
* - Progressive disclosure pattern
|
|
3555
|
+
* - Proxy pattern for forwarding tool calls
|
|
3556
|
+
*
|
|
3557
|
+
* CODING STANDARDS:
|
|
3558
|
+
* - Implement Tool interface from ../types
|
|
3559
|
+
* - Use TOOL_NAME constant with snake_case
|
|
3560
|
+
* - Return CallToolResult with content array
|
|
3561
|
+
* - Handle errors with isError flag
|
|
3562
|
+
*
|
|
3563
|
+
* AVOID:
|
|
3564
|
+
* - Complex business logic in execute method
|
|
3565
|
+
* - Unhandled promise rejections
|
|
3566
|
+
* - Missing error handling
|
|
3567
|
+
*
|
|
3568
|
+
* NAMING CONVENTIONS:
|
|
3569
|
+
* - Tools from MCP servers use serverName__toolName format when clashing
|
|
3570
|
+
* - Skills use skill__skillName format (skill__ prefix)
|
|
3571
|
+
*/
|
|
3572
|
+
/**
|
|
3573
|
+
* Input schema for UseToolTool
|
|
3574
|
+
* @property toolName - Name of the tool or skill to execute
|
|
3575
|
+
* @property toolArgs - Arguments to pass to the tool (from describe_tools schema)
|
|
3576
|
+
*/
|
|
3577
|
+
const UseToolToolInputSchema = zod.z.object({
|
|
3578
|
+
toolName: zod.z.string().min(1).describe("Name of the tool to execute"),
|
|
3579
|
+
toolArgs: zod.z.record(zod.z.string(), zod.z.unknown()).optional().describe("Arguments to pass to the tool, as discovered from describe_tools")
|
|
3580
|
+
});
|
|
3581
|
+
/**
|
|
3555
3582
|
* UseToolTool executes MCP tools and skills with proper error handling.
|
|
3556
3583
|
*
|
|
3557
3584
|
* This tool supports three invocation patterns:
|
|
@@ -3591,6 +3618,9 @@ var UseToolTool = class UseToolTool {
|
|
|
3591
3618
|
*
|
|
3592
3619
|
* @returns The tool definition conforming to MCP spec
|
|
3593
3620
|
*/
|
|
3621
|
+
getInputSchema() {
|
|
3622
|
+
return UseToolToolInputSchema;
|
|
3623
|
+
}
|
|
3594
3624
|
getDefinition() {
|
|
3595
3625
|
return {
|
|
3596
3626
|
name: UseToolTool.TOOL_NAME,
|
|
@@ -3600,22 +3630,7 @@ var UseToolTool = class UseToolTool {
|
|
|
3600
3630
|
|
|
3601
3631
|
IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverId}".
|
|
3602
3632
|
`,
|
|
3603
|
-
inputSchema: {
|
|
3604
|
-
type: "object",
|
|
3605
|
-
properties: {
|
|
3606
|
-
toolName: {
|
|
3607
|
-
type: "string",
|
|
3608
|
-
description: "Name of the tool to execute",
|
|
3609
|
-
minLength: 1
|
|
3610
|
-
},
|
|
3611
|
-
toolArgs: {
|
|
3612
|
-
type: "object",
|
|
3613
|
-
description: "Arguments to pass to the tool, as discovered from describe_tools"
|
|
3614
|
-
}
|
|
3615
|
-
},
|
|
3616
|
-
required: ["toolName"],
|
|
3617
|
-
additionalProperties: false
|
|
3618
|
-
}
|
|
3633
|
+
inputSchema: zod.z.toJSONSchema(UseToolToolInputSchema, { reused: "inline" })
|
|
3619
3634
|
};
|
|
3620
3635
|
}
|
|
3621
3636
|
/**
|
|
@@ -3628,6 +3643,16 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
|
|
|
3628
3643
|
* @param skill - The skill that was requested
|
|
3629
3644
|
* @returns CallToolResult with guidance message
|
|
3630
3645
|
*/
|
|
3646
|
+
/**
|
|
3647
|
+
* Coerce toolArgs using the downstream tool's cached JSON Schema.
|
|
3648
|
+
* Converts the schema to Zod and runs coerceArgs so that string-encoded
|
|
3649
|
+
* objects/arrays are parsed before being forwarded to the downstream server.
|
|
3650
|
+
*/
|
|
3651
|
+
async coerceToolArgs(serverName, toolName, toolArgs) {
|
|
3652
|
+
const jsonSchema = await this.definitionsCacheService.getToolSchema(serverName, toolName);
|
|
3653
|
+
if (!jsonSchema) return toolArgs;
|
|
3654
|
+
return (0, __agimon_ai_foundation_validator.coerceArgs)(toolArgs, (0, __agimon_ai_foundation_validator.jsonSchemaToZod)(jsonSchema));
|
|
3655
|
+
}
|
|
3631
3656
|
executeSkill(skill) {
|
|
3632
3657
|
return { content: [{
|
|
3633
3658
|
type: "text",
|
|
@@ -3673,9 +3698,9 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
|
|
|
3673
3698
|
* @param input - The tool/skill name and optional arguments
|
|
3674
3699
|
* @returns CallToolResult with execution output or error
|
|
3675
3700
|
*/
|
|
3676
|
-
async execute(
|
|
3701
|
+
async execute(rawInput) {
|
|
3677
3702
|
try {
|
|
3678
|
-
const { toolName: inputToolName, toolArgs = {} } =
|
|
3703
|
+
const { toolName: inputToolName, toolArgs = {} } = UseToolToolInputSchema.parse(rawInput);
|
|
3679
3704
|
if (inputToolName.startsWith(SKILL_PREFIX)) {
|
|
3680
3705
|
const skillName = inputToolName.slice(SKILL_PREFIX.length);
|
|
3681
3706
|
if (this.skillService) {
|
|
@@ -3704,7 +3729,8 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
|
|
|
3704
3729
|
isError: true
|
|
3705
3730
|
};
|
|
3706
3731
|
const reqTimeout = this.clientManager.getServerRequestTimeout(serverName);
|
|
3707
|
-
|
|
3732
|
+
const coercedArgs = await this.coerceToolArgs(serverName, actualToolName, toolArgs);
|
|
3733
|
+
return await client.callTool(actualToolName, coercedArgs, reqTimeout ? { timeout: reqTimeout } : void 0);
|
|
3708
3734
|
} catch (error) {
|
|
3709
3735
|
return {
|
|
3710
3736
|
content: [{
|
|
@@ -3741,7 +3767,8 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
|
|
|
3741
3767
|
const targetServerName = matchingServers[0];
|
|
3742
3768
|
const client = await this.clientManager.ensureConnected(targetServerName);
|
|
3743
3769
|
const targetReqTimeout = this.clientManager.getServerRequestTimeout(targetServerName);
|
|
3744
|
-
|
|
3770
|
+
const coercedArgs = await this.coerceToolArgs(targetServerName, actualToolName, toolArgs);
|
|
3771
|
+
return await client.callTool(actualToolName, coercedArgs, targetReqTimeout ? { timeout: targetReqTimeout } : void 0);
|
|
3745
3772
|
} catch (error) {
|
|
3746
3773
|
return {
|
|
3747
3774
|
content: [{
|
|
@@ -4422,19 +4449,38 @@ var StdioTransportHandler = class {
|
|
|
4422
4449
|
* - Transport handler pattern implementing TransportHandler interface
|
|
4423
4450
|
* - STDIO transport with MCP request forwarding to HTTP backend
|
|
4424
4451
|
* - Graceful cleanup with error isolation
|
|
4452
|
+
* - Reconnection with exponential backoff on connection loss
|
|
4425
4453
|
*
|
|
4426
4454
|
* CODING STANDARDS:
|
|
4427
4455
|
* - Use StdioServerTransport for stdio communication
|
|
4428
4456
|
* - Reuse a single StreamableHTTP client connection
|
|
4429
4457
|
* - Wrap async operations with try-catch and descriptive errors
|
|
4458
|
+
* - Reconnect transparently on connection reset
|
|
4430
4459
|
*
|
|
4431
4460
|
* AVOID:
|
|
4432
4461
|
* - Starting HTTP server lifecycle in this transport entry point
|
|
4433
4462
|
* - Recreating HTTP client per request
|
|
4434
4463
|
* - Swallowing cleanup failures silently
|
|
4435
4464
|
*/
|
|
4465
|
+
const CONNECTION_ERROR_PATTERNS = [
|
|
4466
|
+
"econnrefused",
|
|
4467
|
+
"econnreset",
|
|
4468
|
+
"enotfound",
|
|
4469
|
+
"connection refused",
|
|
4470
|
+
"fetch failed",
|
|
4471
|
+
"socket hang up",
|
|
4472
|
+
"network error",
|
|
4473
|
+
"failed to fetch"
|
|
4474
|
+
];
|
|
4475
|
+
function isConnectionError(error) {
|
|
4476
|
+
if (!(error instanceof Error)) return false;
|
|
4477
|
+
const message = error.message.toLowerCase();
|
|
4478
|
+
return CONNECTION_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
|
|
4479
|
+
}
|
|
4436
4480
|
/**
|
|
4437
4481
|
* Transport that serves MCP over stdio and forwards MCP requests to an HTTP endpoint.
|
|
4482
|
+
* Automatically reconnects to the HTTP backend with exponential backoff when the
|
|
4483
|
+
* connection is lost (e.g. backend crash + restart).
|
|
4438
4484
|
*/
|
|
4439
4485
|
var StdioHttpTransportHandler = class {
|
|
4440
4486
|
endpoint;
|
|
@@ -4442,24 +4488,19 @@ var StdioHttpTransportHandler = class {
|
|
|
4442
4488
|
stdioTransport = null;
|
|
4443
4489
|
httpClient = null;
|
|
4444
4490
|
logger;
|
|
4491
|
+
MAX_RECONNECT_ATTEMPTS = 5;
|
|
4492
|
+
RECONNECT_BASE_MS = 1e3;
|
|
4493
|
+
RECONNECT_MAX_MS = 3e4;
|
|
4445
4494
|
constructor(config, logger = console) {
|
|
4446
4495
|
this.endpoint = config.endpoint;
|
|
4447
4496
|
this.logger = logger;
|
|
4448
4497
|
}
|
|
4449
4498
|
async start() {
|
|
4450
4499
|
try {
|
|
4451
|
-
const
|
|
4452
|
-
const client = new __modelcontextprotocol_sdk_client_index_js.Client({
|
|
4453
|
-
name: "@agimon-ai/mcp-proxy-stdio-http-proxy",
|
|
4454
|
-
version: "0.1.0"
|
|
4455
|
-
}, { capabilities: { elicitation: {} } });
|
|
4456
|
-
await client.connect(httpClientTransport);
|
|
4500
|
+
const client = await this.createAndConnectClient();
|
|
4457
4501
|
this.httpClient = client;
|
|
4458
|
-
this.stdioProxyServer = this.createProxyServer(
|
|
4459
|
-
|
|
4460
|
-
client.setRequestHandler(__modelcontextprotocol_sdk_types_js.ElicitRequestSchema, async (request) => {
|
|
4461
|
-
return await proxyServer.elicitInput(request.params);
|
|
4462
|
-
});
|
|
4502
|
+
this.stdioProxyServer = this.createProxyServer();
|
|
4503
|
+
this.registerElicitationHandler(client);
|
|
4463
4504
|
this.stdioTransport = new __modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
|
|
4464
4505
|
await this.stdioProxyServer.connect(this.stdioTransport);
|
|
4465
4506
|
this.logger.info(`@agimon-ai/mcp-proxy MCP stdio proxy connected to ${this.endpoint.toString()}`);
|
|
@@ -4501,7 +4542,52 @@ var StdioHttpTransportHandler = class {
|
|
|
4501
4542
|
]);
|
|
4502
4543
|
if (cleanupErrors.length > 0) throw new Error(`Failed to stop stdio-http proxy transport: ${cleanupErrors.join("; ")}`);
|
|
4503
4544
|
}
|
|
4504
|
-
|
|
4545
|
+
async createAndConnectClient() {
|
|
4546
|
+
const httpClientTransport = new __modelcontextprotocol_sdk_client_streamableHttp_js.StreamableHTTPClientTransport(this.endpoint);
|
|
4547
|
+
const client = new __modelcontextprotocol_sdk_client_index_js.Client({
|
|
4548
|
+
name: "@agimon-ai/mcp-proxy-stdio-http-proxy",
|
|
4549
|
+
version: "0.1.0"
|
|
4550
|
+
}, { capabilities: { elicitation: {} } });
|
|
4551
|
+
await client.connect(httpClientTransport);
|
|
4552
|
+
return client;
|
|
4553
|
+
}
|
|
4554
|
+
registerElicitationHandler(client) {
|
|
4555
|
+
const proxyServer = this.stdioProxyServer;
|
|
4556
|
+
if (!proxyServer) return;
|
|
4557
|
+
client.setRequestHandler(__modelcontextprotocol_sdk_types_js.ElicitRequestSchema, async (request) => {
|
|
4558
|
+
return await proxyServer.elicitInput(request.params);
|
|
4559
|
+
});
|
|
4560
|
+
}
|
|
4561
|
+
async reconnectWithBackoff() {
|
|
4562
|
+
for (let attempt = 0; attempt < this.MAX_RECONNECT_ATTEMPTS; attempt++) {
|
|
4563
|
+
const delay = Math.min(this.RECONNECT_BASE_MS * 2 ** attempt, this.RECONNECT_MAX_MS);
|
|
4564
|
+
await new Promise((resolve$2) => setTimeout(resolve$2, delay));
|
|
4565
|
+
try {
|
|
4566
|
+
await this.httpClient?.close().catch(() => void 0);
|
|
4567
|
+
const client = await this.createAndConnectClient();
|
|
4568
|
+
this.httpClient = client;
|
|
4569
|
+
this.registerElicitationHandler(client);
|
|
4570
|
+
this.logger.info(`Reconnected to HTTP backend at ${this.endpoint.toString()} (attempt ${attempt + 1})`);
|
|
4571
|
+
return;
|
|
4572
|
+
} catch {
|
|
4573
|
+
this.logger.info(`Reconnect attempt ${attempt + 1}/${this.MAX_RECONNECT_ATTEMPTS} to ${this.endpoint.toString()} failed`);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
throw new Error(`Failed to reconnect to HTTP backend at ${this.endpoint.toString()} after ${this.MAX_RECONNECT_ATTEMPTS} attempts`);
|
|
4577
|
+
}
|
|
4578
|
+
async withReconnect(fn) {
|
|
4579
|
+
if (!this.httpClient) throw new Error("HTTP client not connected");
|
|
4580
|
+
try {
|
|
4581
|
+
return await fn();
|
|
4582
|
+
} catch (error) {
|
|
4583
|
+
if (isConnectionError(error)) {
|
|
4584
|
+
await this.reconnectWithBackoff();
|
|
4585
|
+
return await fn();
|
|
4586
|
+
}
|
|
4587
|
+
throw error;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
createProxyServer() {
|
|
4505
4591
|
const proxyServer = new __modelcontextprotocol_sdk_server_index_js.Server({
|
|
4506
4592
|
name: "@agimon-ai/mcp-proxy-stdio-http-proxy",
|
|
4507
4593
|
version: "0.1.0"
|
|
@@ -4512,48 +4598,48 @@ var StdioHttpTransportHandler = class {
|
|
|
4512
4598
|
} });
|
|
4513
4599
|
proxyServer.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => {
|
|
4514
4600
|
try {
|
|
4515
|
-
return await
|
|
4601
|
+
return await this.withReconnect(() => this.httpClient.listTools());
|
|
4516
4602
|
} catch (error) {
|
|
4517
4603
|
throw new Error(`Failed forwarding tools/list to HTTP backend: ${error instanceof Error ? error.message : String(error)}`);
|
|
4518
4604
|
}
|
|
4519
4605
|
});
|
|
4520
4606
|
proxyServer.setRequestHandler(__modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (request) => {
|
|
4521
4607
|
try {
|
|
4522
|
-
return await
|
|
4608
|
+
return await this.withReconnect(() => this.httpClient.callTool({
|
|
4523
4609
|
name: request.params.name,
|
|
4524
4610
|
arguments: request.params.arguments
|
|
4525
|
-
});
|
|
4611
|
+
}));
|
|
4526
4612
|
} catch (error) {
|
|
4527
4613
|
throw new Error(`Failed forwarding tools/call (${request.params.name}) to HTTP backend: ${error instanceof Error ? error.message : String(error)}`);
|
|
4528
4614
|
}
|
|
4529
4615
|
});
|
|
4530
4616
|
proxyServer.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListResourcesRequestSchema, async () => {
|
|
4531
4617
|
try {
|
|
4532
|
-
return await
|
|
4618
|
+
return await this.withReconnect(() => this.httpClient.listResources());
|
|
4533
4619
|
} catch (error) {
|
|
4534
4620
|
throw new Error(`Failed forwarding resources/list to HTTP backend: ${error instanceof Error ? error.message : String(error)}`);
|
|
4535
4621
|
}
|
|
4536
4622
|
});
|
|
4537
4623
|
proxyServer.setRequestHandler(__modelcontextprotocol_sdk_types_js.ReadResourceRequestSchema, async (request) => {
|
|
4538
4624
|
try {
|
|
4539
|
-
return await
|
|
4625
|
+
return await this.withReconnect(() => this.httpClient.readResource({ uri: request.params.uri }));
|
|
4540
4626
|
} catch (error) {
|
|
4541
4627
|
throw new Error(`Failed forwarding resources/read (${request.params.uri}) to HTTP backend: ${error instanceof Error ? error.message : String(error)}`);
|
|
4542
4628
|
}
|
|
4543
4629
|
});
|
|
4544
4630
|
proxyServer.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListPromptsRequestSchema, async () => {
|
|
4545
4631
|
try {
|
|
4546
|
-
return await
|
|
4632
|
+
return await this.withReconnect(() => this.httpClient.listPrompts());
|
|
4547
4633
|
} catch (error) {
|
|
4548
4634
|
throw new Error(`Failed forwarding prompts/list to HTTP backend: ${error instanceof Error ? error.message : String(error)}`);
|
|
4549
4635
|
}
|
|
4550
4636
|
});
|
|
4551
4637
|
proxyServer.setRequestHandler(__modelcontextprotocol_sdk_types_js.GetPromptRequestSchema, async (request) => {
|
|
4552
4638
|
try {
|
|
4553
|
-
return await
|
|
4639
|
+
return await this.withReconnect(() => this.httpClient.getPrompt({
|
|
4554
4640
|
name: request.params.name,
|
|
4555
4641
|
arguments: request.params.arguments
|
|
4556
|
-
});
|
|
4642
|
+
}));
|
|
4557
4643
|
} catch (error) {
|
|
4558
4644
|
throw new Error(`Failed forwarding prompts/get (${request.params.name}) to HTTP backend: ${error instanceof Error ? error.message : String(error)}`);
|
|
4559
4645
|
}
|
|
@@ -4916,21 +5002,27 @@ async function createSessionServer(shared) {
|
|
|
4916
5002
|
})() : [await describeTools.getDefinition(), useToolWithCache.getDefinition()] }));
|
|
4917
5003
|
server.setRequestHandler(__modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (request) => {
|
|
4918
5004
|
const { name, arguments: args } = request.params;
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
5005
|
+
const executeWithCoercion = async (tool, toolName) => {
|
|
5006
|
+
const coerced = (0, __agimon_ai_foundation_validator.coerceArgs)(args ?? {}, tool.getInputSchema());
|
|
5007
|
+
try {
|
|
5008
|
+
return await tool.execute(coerced);
|
|
5009
|
+
} catch (error) {
|
|
5010
|
+
if (error instanceof zod.z.ZodError) return {
|
|
5011
|
+
content: [{
|
|
5012
|
+
type: "text",
|
|
5013
|
+
text: (0, __agimon_ai_foundation_validator.formatZodError)(error, {
|
|
5014
|
+
schemaName: toolName,
|
|
5015
|
+
schema: tool.getInputSchema()
|
|
5016
|
+
})
|
|
5017
|
+
}],
|
|
5018
|
+
isError: true
|
|
5019
|
+
};
|
|
5020
|
+
throw new Error(`Failed to execute ${toolName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
5021
|
+
}
|
|
5022
|
+
};
|
|
5023
|
+
if (name === DescribeToolsTool.TOOL_NAME) return await executeWithCoercion(describeTools, name);
|
|
5024
|
+
if (name === UseToolTool.TOOL_NAME) return await executeWithCoercion(useToolWithCache, name);
|
|
5025
|
+
if (name === SearchListToolsTool.TOOL_NAME && proxyMode === "search") return await executeWithCoercion(searchListTools, name);
|
|
4934
5026
|
if (proxyMode === "flat") return await useToolWithCache.execute({
|
|
4935
5027
|
toolName: name,
|
|
4936
5028
|
toolArgs: args || {}
|