@agiflowai/one-mcp 0.3.15 → 0.3.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.
@@ -1453,6 +1453,14 @@ var DefinitionsCacheService = class {
1453
1453
  /** Default connection timeout in milliseconds (30 seconds) */
1454
1454
  const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
1455
1455
  /**
1456
+ * Checks if an error is a session-related error from an HTTP backend
1457
+ * (e.g., downstream server restarted and no longer recognizes the session ID).
1458
+ */
1459
+ function isSessionError(error) {
1460
+ const message = error instanceof Error ? error.message : String(error);
1461
+ return message.includes("unknown session") || message.includes("Session not found");
1462
+ }
1463
+ /**
1456
1464
  * MCP Client wrapper for managing individual server connections
1457
1465
  * This is an internal class used by McpClientManagerService
1458
1466
  */
@@ -1466,6 +1474,7 @@ var McpClient = class {
1466
1474
  client;
1467
1475
  childProcess;
1468
1476
  connected = false;
1477
+ reconnectFn;
1469
1478
  constructor(serverName, transport, client, config) {
1470
1479
  this.serverName = serverName;
1471
1480
  this.serverInstruction = config.instruction;
@@ -1481,34 +1490,76 @@ var McpClient = class {
1481
1490
  setConnected(connected) {
1482
1491
  this.connected = connected;
1483
1492
  }
1493
+ /**
1494
+ * Sets a reconnection function that creates a fresh Client and transport.
1495
+ * Called automatically by withSessionRetry when a session error is detected
1496
+ * (e.g., downstream HTTP server restarted and the old session ID is invalid).
1497
+ */
1498
+ setReconnectFn(fn) {
1499
+ this.reconnectFn = fn;
1500
+ }
1501
+ /**
1502
+ * Wraps an operation with automatic retry on session errors.
1503
+ * If the operation fails with a session error (e.g., downstream server restarted),
1504
+ * reconnects and retries once.
1505
+ */
1506
+ async withSessionRetry(operation) {
1507
+ try {
1508
+ return await operation();
1509
+ } catch (error) {
1510
+ if (!this.reconnectFn || !isSessionError(error)) throw error;
1511
+ console.error(`Session error for ${this.serverName}, reconnecting: ${error instanceof Error ? error.message : String(error)}`);
1512
+ try {
1513
+ await this.client.close();
1514
+ } catch (closeError) {
1515
+ console.error(`Failed to close stale client for ${this.serverName}:`, closeError);
1516
+ }
1517
+ const result = await this.reconnectFn();
1518
+ this.client = result.client;
1519
+ if (result.childProcess) this.childProcess = result.childProcess;
1520
+ return await operation();
1521
+ }
1522
+ }
1484
1523
  async listTools() {
1485
1524
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1486
- return (await this.client.listTools()).tools;
1525
+ return this.withSessionRetry(async () => {
1526
+ return (await this.client.listTools()).tools;
1527
+ });
1487
1528
  }
1488
1529
  async listResources() {
1489
1530
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1490
- return (await this.client.listResources()).resources;
1531
+ return this.withSessionRetry(async () => {
1532
+ return (await this.client.listResources()).resources;
1533
+ });
1491
1534
  }
1492
1535
  async listPrompts() {
1493
1536
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1494
- return (await this.client.listPrompts()).prompts;
1537
+ return this.withSessionRetry(async () => {
1538
+ return (await this.client.listPrompts()).prompts;
1539
+ });
1495
1540
  }
1496
1541
  async callTool(name, args) {
1497
1542
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1498
- return await this.client.callTool({
1499
- name,
1500
- arguments: args
1543
+ return this.withSessionRetry(async () => {
1544
+ return await this.client.callTool({
1545
+ name,
1546
+ arguments: args
1547
+ });
1501
1548
  });
1502
1549
  }
1503
1550
  async readResource(uri) {
1504
1551
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1505
- return await this.client.readResource({ uri });
1552
+ return this.withSessionRetry(async () => {
1553
+ return await this.client.readResource({ uri });
1554
+ });
1506
1555
  }
1507
1556
  async getPrompt(name, args) {
1508
1557
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1509
- return await this.client.getPrompt({
1510
- name,
1511
- arguments: args
1558
+ return this.withSessionRetry(async () => {
1559
+ return await this.client.getPrompt({
1560
+ name,
1561
+ arguments: args
1562
+ });
1512
1563
  });
1513
1564
  }
1514
1565
  async close() {
@@ -1524,23 +1575,11 @@ var McpClientManagerService = class {
1524
1575
  clients = /* @__PURE__ */ new Map();
1525
1576
  serverConfigs = /* @__PURE__ */ new Map();
1526
1577
  connectionPromises = /* @__PURE__ */ new Map();
1527
- constructor() {
1528
- process.on("exit", () => {
1529
- this.cleanupOnExit();
1530
- });
1531
- process.on("SIGINT", () => {
1532
- this.cleanupOnExit();
1533
- process.exit(0);
1534
- });
1535
- process.on("SIGTERM", () => {
1536
- this.cleanupOnExit();
1537
- process.exit(0);
1538
- });
1539
- }
1540
1578
  /**
1541
- * Cleanup all resources on exit (child processes)
1579
+ * Synchronously kill all stdio MCP server child processes.
1580
+ * Must be called by the owner (e.g. transport/command layer) during shutdown.
1542
1581
  */
1543
- cleanupOnExit() {
1582
+ cleanupChildProcesses() {
1544
1583
  for (const [serverName, client] of this.clients) try {
1545
1584
  const childProcess = client["childProcess"];
1546
1585
  if (childProcess && !childProcess.killed) {
@@ -1601,6 +1640,20 @@ var McpClientManagerService = class {
1601
1640
  try {
1602
1641
  await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
1603
1642
  mcpClient.setConnected(true);
1643
+ if (config.transport === "http" || config.transport === "sse") mcpClient.setReconnectFn(async () => {
1644
+ try {
1645
+ const newClient = new __modelcontextprotocol_sdk_client_index_js.Client({
1646
+ name: "@agiflowai/one-mcp-client",
1647
+ version: "0.1.0"
1648
+ }, { capabilities: {} });
1649
+ const newMcpClient = new McpClient(serverName, config.transport, newClient, {});
1650
+ await this.performConnection(newMcpClient, config);
1651
+ return { client: newClient };
1652
+ } catch (error) {
1653
+ console.error(`Failed to reconnect to ${serverName}:`, error);
1654
+ throw error;
1655
+ }
1656
+ });
1604
1657
  if (!mcpClient.serverInstruction) try {
1605
1658
  const serverInstruction = mcpClient["client"].getInstructions();
1606
1659
  if (serverInstruction) mcpClient.serverInstruction = serverInstruction;
@@ -2808,7 +2861,7 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2808
2861
 
2809
2862
  //#endregion
2810
2863
  //#region package.json
2811
- var version = "0.3.14";
2864
+ var version = "0.3.16";
2812
2865
 
2813
2866
  //#endregion
2814
2867
  //#region src/server/index.ts
@@ -2819,6 +2872,7 @@ var version = "0.3.14";
2819
2872
  * - Factory pattern for server creation
2820
2873
  * - Tool registration pattern
2821
2874
  * - Dependency injection for services
2875
+ * - Shared services pattern for multi-session HTTP transport
2822
2876
  *
2823
2877
  * CODING STANDARDS:
2824
2878
  * - Register all tools, resources, and prompts here
@@ -2922,7 +2976,13 @@ function buildProxyInstructions(serverDefinitions, mode, includeSkillsTool) {
2922
2976
  "Use describe_tools to inspect capabilities and use_tool to execute them."
2923
2977
  ].join("\n\n");
2924
2978
  }
2925
- async function createServer(options) {
2979
+ /**
2980
+ * Initialize shared services and tools once for use across multiple sessions.
2981
+ * Use with createSessionServer() for HTTP transport where multiple agents
2982
+ * connect concurrently. This avoids duplicating downstream connections,
2983
+ * file watchers, caches, and tool instances per session.
2984
+ */
2985
+ async function initializeSharedServices(options) {
2926
2986
  const clientManager = new McpClientManagerService();
2927
2987
  let configSkills;
2928
2988
  let configId;
@@ -2995,13 +3055,47 @@ async function createServer(options) {
2995
3055
  definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2996
3056
  }
2997
3057
  else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
3058
+ const proxyMode = options?.proxyMode || "meta";
2998
3059
  const describeTools = new DescribeToolsTool(clientManager, skillService, serverId, definitionsCacheService);
2999
- const useToolWithCache = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
3060
+ const useTool = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
3000
3061
  const searchListTools = new SearchListToolsTool(clientManager, definitionsCacheService);
3001
3062
  toolsRef.describeTools = describeTools;
3002
- const serverDefinitions = await definitionsCacheService.getServerDefinitions();
3003
- const includeSkillsTool = await hasAnySkills(definitionsCacheService, skillService);
3004
- const proxyMode = options?.proxyMode || "meta";
3063
+ if (skillService) skillService.startWatching().catch((error) => {
3064
+ console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
3065
+ });
3066
+ if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
3067
+ configPath: options.configFilePath,
3068
+ configHash,
3069
+ oneMcpVersion: version,
3070
+ serverId
3071
+ }).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
3072
+ console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
3073
+ }).catch((error) => {
3074
+ console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
3075
+ });
3076
+ const dispose = async () => {
3077
+ await clientManager.disconnectAll();
3078
+ if (skillService) skillService.stopWatching();
3079
+ };
3080
+ return {
3081
+ clientManager,
3082
+ definitionsCacheService,
3083
+ skillService,
3084
+ describeTools,
3085
+ useTool,
3086
+ searchListTools,
3087
+ serverId,
3088
+ proxyMode,
3089
+ dispose
3090
+ };
3091
+ }
3092
+ /**
3093
+ * Create a lightweight per-session MCP Server instance that delegates
3094
+ * to shared services and tools. Use with initializeSharedServices()
3095
+ * for multi-session HTTP transport.
3096
+ */
3097
+ async function createSessionServer(shared) {
3098
+ const { clientManager, definitionsCacheService, skillService, describeTools, useTool: useToolWithCache, searchListTools, serverId, proxyMode } = shared;
3005
3099
  const server = new __modelcontextprotocol_sdk_server_index_js.Server({
3006
3100
  name: "@agiflowai/one-mcp",
3007
3101
  version: "0.1.0"
@@ -3011,10 +3105,7 @@ async function createServer(options) {
3011
3105
  resources: {},
3012
3106
  prompts: {}
3013
3107
  },
3014
- instructions: buildProxyInstructions(serverDefinitions, proxyMode, includeSkillsTool)
3015
- });
3016
- if (skillService) skillService.startWatching().catch((error) => {
3017
- console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
3108
+ instructions: buildProxyInstructions(await definitionsCacheService.getServerDefinitions(), proxyMode, await hasAnySkills(definitionsCacheService, skillService))
3018
3109
  });
3019
3110
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => ({ tools: proxyMode === "flat" ? await (async () => {
3020
3111
  const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
@@ -3051,14 +3142,14 @@ async function createServer(options) {
3051
3142
  throw new Error(`Unknown tool: ${name}`);
3052
3143
  });
3053
3144
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListResourcesRequestSchema, async () => {
3054
- const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3145
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3055
3146
  const resourceToServers = /* @__PURE__ */ new Map();
3056
- for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3147
+ for (const serverDefinition of currentServerDefinitions) for (const resource of serverDefinition.resources) {
3057
3148
  if (!resourceToServers.has(resource.uri)) resourceToServers.set(resource.uri, []);
3058
3149
  resourceToServers.get(resource.uri)?.push(serverDefinition.serverName);
3059
3150
  }
3060
3151
  const resources = [];
3061
- for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3152
+ for (const serverDefinition of currentServerDefinitions) for (const resource of serverDefinition.resources) {
3062
3153
  const hasClash = (resourceToServers.get(resource.uri) || []).length > 1;
3063
3154
  resources.push({
3064
3155
  ...resource,
@@ -3077,10 +3168,10 @@ async function createServer(options) {
3077
3168
  return await (await clientManager.ensureConnected(matchingServers[0])).readResource(actualUri);
3078
3169
  });
3079
3170
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListPromptsRequestSchema, async () => {
3080
- const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3171
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3081
3172
  const promptToServers = /* @__PURE__ */ new Map();
3082
3173
  const serverPromptsMap = /* @__PURE__ */ new Map();
3083
- for (const serverDefinition of serverDefinitions$1) {
3174
+ for (const serverDefinition of currentServerDefinitions) {
3084
3175
  serverPromptsMap.set(serverDefinition.serverName, serverDefinition.prompts);
3085
3176
  for (const prompt of serverDefinition.prompts) {
3086
3177
  if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
@@ -3088,7 +3179,7 @@ async function createServer(options) {
3088
3179
  }
3089
3180
  }
3090
3181
  const aggregatedPrompts = [];
3091
- for (const serverDefinition of serverDefinitions$1) {
3182
+ for (const serverDefinition of currentServerDefinitions) {
3092
3183
  const prompts = serverPromptsMap.get(serverDefinition.serverName) || [];
3093
3184
  for (const prompt of prompts) {
3094
3185
  const hasClash = (promptToServers.get(prompt.name) || []).length > 1;
@@ -3103,29 +3194,26 @@ async function createServer(options) {
3103
3194
  });
3104
3195
  server.setRequestHandler(__modelcontextprotocol_sdk_types_js.GetPromptRequestSchema, async (request) => {
3105
3196
  const { name, arguments: args } = request.params;
3106
- const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3197
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3107
3198
  const { serverName, actualToolName: actualPromptName } = parseToolName(name);
3108
3199
  if (serverName) return await (await clientManager.ensureConnected(serverName)).getPrompt(actualPromptName, args);
3109
3200
  const serversWithPrompt = [];
3110
- for (const serverDefinition of serverDefinitions$1) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
3201
+ for (const serverDefinition of currentServerDefinitions) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
3111
3202
  if (serversWithPrompt.length === 0) throw new Error(`Prompt not found: ${name}`);
3112
3203
  if (serversWithPrompt.length > 1) throw new Error(`Prompt "${name}" exists on multiple servers: ${serversWithPrompt.join(", ")}. Use the prefixed format (e.g., "${serversWithPrompt[0]}__${name}") to specify which server to use.`);
3113
3204
  const client = clientManager.getClient(serversWithPrompt[0]);
3114
3205
  if (!client) return await (await clientManager.ensureConnected(serversWithPrompt[0])).getPrompt(name, args);
3115
3206
  return await client.getPrompt(name, args);
3116
3207
  });
3117
- if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
3118
- configPath: options.configFilePath,
3119
- configHash,
3120
- oneMcpVersion: version,
3121
- serverId
3122
- }).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
3123
- console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
3124
- }).catch((error) => {
3125
- console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
3126
- });
3127
3208
  return server;
3128
3209
  }
3210
+ /**
3211
+ * Create a single MCP server instance (backward-compatible wrapper).
3212
+ * For multi-session HTTP transport, use initializeSharedServices() + createSessionServer() instead.
3213
+ */
3214
+ async function createServer(options) {
3215
+ return createSessionServer(await initializeSharedServices(options));
3216
+ }
3129
3217
 
3130
3218
  //#endregion
3131
3219
  //#region src/types/index.ts
@@ -4205,6 +4293,12 @@ Object.defineProperty(exports, 'createServer', {
4205
4293
  return createServer;
4206
4294
  }
4207
4295
  });
4296
+ Object.defineProperty(exports, 'createSessionServer', {
4297
+ enumerable: true,
4298
+ get: function () {
4299
+ return createSessionServer;
4300
+ }
4301
+ });
4208
4302
  Object.defineProperty(exports, 'findConfigFile', {
4209
4303
  enumerable: true,
4210
4304
  get: function () {
@@ -4217,6 +4311,12 @@ Object.defineProperty(exports, 'generateServerId', {
4217
4311
  return generateServerId;
4218
4312
  }
4219
4313
  });
4314
+ Object.defineProperty(exports, 'initializeSharedServices', {
4315
+ enumerable: true,
4316
+ get: function () {
4317
+ return initializeSharedServices;
4318
+ }
4319
+ });
4220
4320
  Object.defineProperty(exports, 'version', {
4221
4321
  enumerable: true,
4222
4322
  get: function () {