@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.
@@ -1424,6 +1424,14 @@ var DefinitionsCacheService = class {
1424
1424
  /** Default connection timeout in milliseconds (30 seconds) */
1425
1425
  const DEFAULT_CONNECTION_TIMEOUT_MS = 3e4;
1426
1426
  /**
1427
+ * Checks if an error is a session-related error from an HTTP backend
1428
+ * (e.g., downstream server restarted and no longer recognizes the session ID).
1429
+ */
1430
+ function isSessionError(error) {
1431
+ const message = error instanceof Error ? error.message : String(error);
1432
+ return message.includes("unknown session") || message.includes("Session not found");
1433
+ }
1434
+ /**
1427
1435
  * MCP Client wrapper for managing individual server connections
1428
1436
  * This is an internal class used by McpClientManagerService
1429
1437
  */
@@ -1437,6 +1445,7 @@ var McpClient = class {
1437
1445
  client;
1438
1446
  childProcess;
1439
1447
  connected = false;
1448
+ reconnectFn;
1440
1449
  constructor(serverName, transport, client, config) {
1441
1450
  this.serverName = serverName;
1442
1451
  this.serverInstruction = config.instruction;
@@ -1452,34 +1461,76 @@ var McpClient = class {
1452
1461
  setConnected(connected) {
1453
1462
  this.connected = connected;
1454
1463
  }
1464
+ /**
1465
+ * Sets a reconnection function that creates a fresh Client and transport.
1466
+ * Called automatically by withSessionRetry when a session error is detected
1467
+ * (e.g., downstream HTTP server restarted and the old session ID is invalid).
1468
+ */
1469
+ setReconnectFn(fn) {
1470
+ this.reconnectFn = fn;
1471
+ }
1472
+ /**
1473
+ * Wraps an operation with automatic retry on session errors.
1474
+ * If the operation fails with a session error (e.g., downstream server restarted),
1475
+ * reconnects and retries once.
1476
+ */
1477
+ async withSessionRetry(operation) {
1478
+ try {
1479
+ return await operation();
1480
+ } catch (error) {
1481
+ if (!this.reconnectFn || !isSessionError(error)) throw error;
1482
+ console.error(`Session error for ${this.serverName}, reconnecting: ${error instanceof Error ? error.message : String(error)}`);
1483
+ try {
1484
+ await this.client.close();
1485
+ } catch (closeError) {
1486
+ console.error(`Failed to close stale client for ${this.serverName}:`, closeError);
1487
+ }
1488
+ const result = await this.reconnectFn();
1489
+ this.client = result.client;
1490
+ if (result.childProcess) this.childProcess = result.childProcess;
1491
+ return await operation();
1492
+ }
1493
+ }
1455
1494
  async listTools() {
1456
1495
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1457
- return (await this.client.listTools()).tools;
1496
+ return this.withSessionRetry(async () => {
1497
+ return (await this.client.listTools()).tools;
1498
+ });
1458
1499
  }
1459
1500
  async listResources() {
1460
1501
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1461
- return (await this.client.listResources()).resources;
1502
+ return this.withSessionRetry(async () => {
1503
+ return (await this.client.listResources()).resources;
1504
+ });
1462
1505
  }
1463
1506
  async listPrompts() {
1464
1507
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1465
- return (await this.client.listPrompts()).prompts;
1508
+ return this.withSessionRetry(async () => {
1509
+ return (await this.client.listPrompts()).prompts;
1510
+ });
1466
1511
  }
1467
1512
  async callTool(name, args) {
1468
1513
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1469
- return await this.client.callTool({
1470
- name,
1471
- arguments: args
1514
+ return this.withSessionRetry(async () => {
1515
+ return await this.client.callTool({
1516
+ name,
1517
+ arguments: args
1518
+ });
1472
1519
  });
1473
1520
  }
1474
1521
  async readResource(uri) {
1475
1522
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1476
- return await this.client.readResource({ uri });
1523
+ return this.withSessionRetry(async () => {
1524
+ return await this.client.readResource({ uri });
1525
+ });
1477
1526
  }
1478
1527
  async getPrompt(name, args) {
1479
1528
  if (!this.connected) throw new Error(`Client for ${this.serverName} is not connected`);
1480
- return await this.client.getPrompt({
1481
- name,
1482
- arguments: args
1529
+ return this.withSessionRetry(async () => {
1530
+ return await this.client.getPrompt({
1531
+ name,
1532
+ arguments: args
1533
+ });
1483
1534
  });
1484
1535
  }
1485
1536
  async close() {
@@ -1495,23 +1546,11 @@ var McpClientManagerService = class {
1495
1546
  clients = /* @__PURE__ */ new Map();
1496
1547
  serverConfigs = /* @__PURE__ */ new Map();
1497
1548
  connectionPromises = /* @__PURE__ */ new Map();
1498
- constructor() {
1499
- process.on("exit", () => {
1500
- this.cleanupOnExit();
1501
- });
1502
- process.on("SIGINT", () => {
1503
- this.cleanupOnExit();
1504
- process.exit(0);
1505
- });
1506
- process.on("SIGTERM", () => {
1507
- this.cleanupOnExit();
1508
- process.exit(0);
1509
- });
1510
- }
1511
1549
  /**
1512
- * Cleanup all resources on exit (child processes)
1550
+ * Synchronously kill all stdio MCP server child processes.
1551
+ * Must be called by the owner (e.g. transport/command layer) during shutdown.
1513
1552
  */
1514
- cleanupOnExit() {
1553
+ cleanupChildProcesses() {
1515
1554
  for (const [serverName, client] of this.clients) try {
1516
1555
  const childProcess = client["childProcess"];
1517
1556
  if (childProcess && !childProcess.killed) {
@@ -1572,6 +1611,20 @@ var McpClientManagerService = class {
1572
1611
  try {
1573
1612
  await Promise.race([this.performConnection(mcpClient, config), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timeout after ${timeoutMs}ms`)), timeoutMs))]);
1574
1613
  mcpClient.setConnected(true);
1614
+ if (config.transport === "http" || config.transport === "sse") mcpClient.setReconnectFn(async () => {
1615
+ try {
1616
+ const newClient = new Client({
1617
+ name: "@agiflowai/one-mcp-client",
1618
+ version: "0.1.0"
1619
+ }, { capabilities: {} });
1620
+ const newMcpClient = new McpClient(serverName, config.transport, newClient, {});
1621
+ await this.performConnection(newMcpClient, config);
1622
+ return { client: newClient };
1623
+ } catch (error) {
1624
+ console.error(`Failed to reconnect to ${serverName}:`, error);
1625
+ throw error;
1626
+ }
1627
+ });
1575
1628
  if (!mcpClient.serverInstruction) try {
1576
1629
  const serverInstruction = mcpClient["client"].getInstructions();
1577
1630
  if (serverInstruction) mcpClient.serverInstruction = serverInstruction;
@@ -2779,7 +2832,7 @@ IMPORTANT: Only use tools discovered from describe_tools with id="${this.serverI
2779
2832
 
2780
2833
  //#endregion
2781
2834
  //#region package.json
2782
- var version = "0.3.14";
2835
+ var version = "0.3.16";
2783
2836
 
2784
2837
  //#endregion
2785
2838
  //#region src/server/index.ts
@@ -2790,6 +2843,7 @@ var version = "0.3.14";
2790
2843
  * - Factory pattern for server creation
2791
2844
  * - Tool registration pattern
2792
2845
  * - Dependency injection for services
2846
+ * - Shared services pattern for multi-session HTTP transport
2793
2847
  *
2794
2848
  * CODING STANDARDS:
2795
2849
  * - Register all tools, resources, and prompts here
@@ -2893,7 +2947,13 @@ function buildProxyInstructions(serverDefinitions, mode, includeSkillsTool) {
2893
2947
  "Use describe_tools to inspect capabilities and use_tool to execute them."
2894
2948
  ].join("\n\n");
2895
2949
  }
2896
- async function createServer(options) {
2950
+ /**
2951
+ * Initialize shared services and tools once for use across multiple sessions.
2952
+ * Use with createSessionServer() for HTTP transport where multiple agents
2953
+ * connect concurrently. This avoids duplicating downstream connections,
2954
+ * file watchers, caches, and tool instances per session.
2955
+ */
2956
+ async function initializeSharedServices(options) {
2897
2957
  const clientManager = new McpClientManagerService();
2898
2958
  let configSkills;
2899
2959
  let configId;
@@ -2966,13 +3026,47 @@ async function createServer(options) {
2966
3026
  definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
2967
3027
  }
2968
3028
  else definitionsCacheService = new DefinitionsCacheService(clientManager, skillService);
3029
+ const proxyMode = options?.proxyMode || "meta";
2969
3030
  const describeTools = new DescribeToolsTool(clientManager, skillService, serverId, definitionsCacheService);
2970
- const useToolWithCache = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
3031
+ const useTool = new UseToolTool(clientManager, skillService, serverId, definitionsCacheService);
2971
3032
  const searchListTools = new SearchListToolsTool(clientManager, definitionsCacheService);
2972
3033
  toolsRef.describeTools = describeTools;
2973
- const serverDefinitions = await definitionsCacheService.getServerDefinitions();
2974
- const includeSkillsTool = await hasAnySkills(definitionsCacheService, skillService);
2975
- const proxyMode = options?.proxyMode || "meta";
3034
+ if (skillService) skillService.startWatching().catch((error) => {
3035
+ console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
3036
+ });
3037
+ if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
3038
+ configPath: options.configFilePath,
3039
+ configHash,
3040
+ oneMcpVersion: version,
3041
+ serverId
3042
+ }).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
3043
+ console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
3044
+ }).catch((error) => {
3045
+ console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
3046
+ });
3047
+ const dispose = async () => {
3048
+ await clientManager.disconnectAll();
3049
+ if (skillService) skillService.stopWatching();
3050
+ };
3051
+ return {
3052
+ clientManager,
3053
+ definitionsCacheService,
3054
+ skillService,
3055
+ describeTools,
3056
+ useTool,
3057
+ searchListTools,
3058
+ serverId,
3059
+ proxyMode,
3060
+ dispose
3061
+ };
3062
+ }
3063
+ /**
3064
+ * Create a lightweight per-session MCP Server instance that delegates
3065
+ * to shared services and tools. Use with initializeSharedServices()
3066
+ * for multi-session HTTP transport.
3067
+ */
3068
+ async function createSessionServer(shared) {
3069
+ const { clientManager, definitionsCacheService, skillService, describeTools, useTool: useToolWithCache, searchListTools, serverId, proxyMode } = shared;
2976
3070
  const server = new Server({
2977
3071
  name: "@agiflowai/one-mcp",
2978
3072
  version: "0.1.0"
@@ -2982,10 +3076,7 @@ async function createServer(options) {
2982
3076
  resources: {},
2983
3077
  prompts: {}
2984
3078
  },
2985
- instructions: buildProxyInstructions(serverDefinitions, proxyMode, includeSkillsTool)
2986
- });
2987
- if (skillService) skillService.startWatching().catch((error) => {
2988
- console.error(`[skill-watcher] File watcher failed (non-critical): ${error instanceof Error ? error.message : "Unknown error"}`);
3079
+ instructions: buildProxyInstructions(await definitionsCacheService.getServerDefinitions(), proxyMode, await hasAnySkills(definitionsCacheService, skillService))
2989
3080
  });
2990
3081
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: proxyMode === "flat" ? await (async () => {
2991
3082
  const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
@@ -3022,14 +3113,14 @@ async function createServer(options) {
3022
3113
  throw new Error(`Unknown tool: ${name}`);
3023
3114
  });
3024
3115
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
3025
- const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3116
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3026
3117
  const resourceToServers = /* @__PURE__ */ new Map();
3027
- for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3118
+ for (const serverDefinition of currentServerDefinitions) for (const resource of serverDefinition.resources) {
3028
3119
  if (!resourceToServers.has(resource.uri)) resourceToServers.set(resource.uri, []);
3029
3120
  resourceToServers.get(resource.uri)?.push(serverDefinition.serverName);
3030
3121
  }
3031
3122
  const resources = [];
3032
- for (const serverDefinition of serverDefinitions$1) for (const resource of serverDefinition.resources) {
3123
+ for (const serverDefinition of currentServerDefinitions) for (const resource of serverDefinition.resources) {
3033
3124
  const hasClash = (resourceToServers.get(resource.uri) || []).length > 1;
3034
3125
  resources.push({
3035
3126
  ...resource,
@@ -3048,10 +3139,10 @@ async function createServer(options) {
3048
3139
  return await (await clientManager.ensureConnected(matchingServers[0])).readResource(actualUri);
3049
3140
  });
3050
3141
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
3051
- const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3142
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3052
3143
  const promptToServers = /* @__PURE__ */ new Map();
3053
3144
  const serverPromptsMap = /* @__PURE__ */ new Map();
3054
- for (const serverDefinition of serverDefinitions$1) {
3145
+ for (const serverDefinition of currentServerDefinitions) {
3055
3146
  serverPromptsMap.set(serverDefinition.serverName, serverDefinition.prompts);
3056
3147
  for (const prompt of serverDefinition.prompts) {
3057
3148
  if (!promptToServers.has(prompt.name)) promptToServers.set(prompt.name, []);
@@ -3059,7 +3150,7 @@ async function createServer(options) {
3059
3150
  }
3060
3151
  }
3061
3152
  const aggregatedPrompts = [];
3062
- for (const serverDefinition of serverDefinitions$1) {
3153
+ for (const serverDefinition of currentServerDefinitions) {
3063
3154
  const prompts = serverPromptsMap.get(serverDefinition.serverName) || [];
3064
3155
  for (const prompt of prompts) {
3065
3156
  const hasClash = (promptToServers.get(prompt.name) || []).length > 1;
@@ -3074,29 +3165,26 @@ async function createServer(options) {
3074
3165
  });
3075
3166
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
3076
3167
  const { name, arguments: args } = request.params;
3077
- const serverDefinitions$1 = await definitionsCacheService.getServerDefinitions();
3168
+ const currentServerDefinitions = await definitionsCacheService.getServerDefinitions();
3078
3169
  const { serverName, actualToolName: actualPromptName } = parseToolName(name);
3079
3170
  if (serverName) return await (await clientManager.ensureConnected(serverName)).getPrompt(actualPromptName, args);
3080
3171
  const serversWithPrompt = [];
3081
- for (const serverDefinition of serverDefinitions$1) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
3172
+ for (const serverDefinition of currentServerDefinitions) if (serverDefinition.prompts.some((prompt) => prompt.name === name)) serversWithPrompt.push(serverDefinition.serverName);
3082
3173
  if (serversWithPrompt.length === 0) throw new Error(`Prompt not found: ${name}`);
3083
3174
  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.`);
3084
3175
  const client = clientManager.getClient(serversWithPrompt[0]);
3085
3176
  if (!client) return await (await clientManager.ensureConnected(serversWithPrompt[0])).getPrompt(name, args);
3086
3177
  return await client.getPrompt(name, args);
3087
3178
  });
3088
- if (!shouldStartFromCache && effectiveDefinitionsCachePath && options?.configFilePath) definitionsCacheService.collectForCache({
3089
- configPath: options.configFilePath,
3090
- configHash,
3091
- oneMcpVersion: version,
3092
- serverId
3093
- }).then((definitionsCache) => DefinitionsCacheService.writeToFile(effectiveDefinitionsCachePath, definitionsCache)).then(() => {
3094
- console.error(`[definitions-cache] Wrote ${effectiveDefinitionsCachePath}`);
3095
- }).catch((error) => {
3096
- console.error(`[definitions-cache] Failed to persist ${effectiveDefinitionsCachePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
3097
- });
3098
3179
  return server;
3099
3180
  }
3181
+ /**
3182
+ * Create a single MCP server instance (backward-compatible wrapper).
3183
+ * For multi-session HTTP transport, use initializeSharedServices() + createSessionServer() instead.
3184
+ */
3185
+ async function createServer(options) {
3186
+ return createSessionServer(await initializeSharedServices(options));
3187
+ }
3100
3188
 
3101
3189
  //#endregion
3102
3190
  //#region src/types/index.ts
@@ -4080,4 +4168,4 @@ var StopServerService = class {
4080
4168
  };
4081
4169
 
4082
4170
  //#endregion
4083
- export { findConfigFile as _, SseTransportHandler as a, createServer as c, SearchListToolsTool as d, DescribeToolsTool as f, generateServerId as g, DefinitionsCacheService as h, StdioTransportHandler as i, version as l, McpClientManagerService as m, RuntimeStateService as n, HttpTransportHandler as o, SkillService as p, StdioHttpTransportHandler as r, TRANSPORT_MODE as s, StopServerService as t, UseToolTool as u, ConfigFetcherService as v };
4171
+ export { DefinitionsCacheService as _, SseTransportHandler as a, ConfigFetcherService as b, createServer as c, version as d, UseToolTool as f, McpClientManagerService as g, SkillService as h, StdioTransportHandler as i, createSessionServer as l, DescribeToolsTool as m, RuntimeStateService as n, HttpTransportHandler as o, SearchListToolsTool as p, StdioHttpTransportHandler as r, TRANSPORT_MODE as s, StopServerService as t, initializeSharedServices as u, generateServerId as v, findConfigFile as y };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agiflowai/one-mcp",
3
3
  "description": "One MCP server package",
4
- "version": "0.3.15",
4
+ "version": "0.3.17",
5
5
  "license": "AGPL-3.0",
6
6
  "keywords": [
7
7
  "mcp",