@openeryc/pi-coding-agent 0.75.53 → 0.75.55

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +4 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +4 -2
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +30 -30
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/mcp/client.d.ts +16 -7
  10. package/dist/core/mcp/client.d.ts.map +1 -1
  11. package/dist/core/mcp/client.js +135 -107
  12. package/dist/core/mcp/client.js.map +1 -1
  13. package/dist/core/mcp/manager.d.ts +12 -1
  14. package/dist/core/mcp/manager.d.ts.map +1 -1
  15. package/dist/core/mcp/manager.js +88 -34
  16. package/dist/core/mcp/manager.js.map +1 -1
  17. package/dist/core/mcp/types.d.ts +0 -13
  18. package/dist/core/mcp/types.d.ts.map +1 -1
  19. package/dist/core/mcp/types.js.map +1 -1
  20. package/dist/core/package-manager.d.ts.map +1 -1
  21. package/dist/core/package-manager.js +3 -1
  22. package/dist/core/package-manager.js.map +1 -1
  23. package/dist/core/session-manager.d.ts.map +1 -1
  24. package/dist/core/session-manager.js +1 -1
  25. package/dist/core/session-manager.js.map +1 -1
  26. package/dist/modes/interactive/interactive-mode.d.ts +18 -0
  27. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  28. package/dist/modes/interactive/interactive-mode.js +360 -427
  29. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  30. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  31. package/dist/modes/rpc/rpc-client.js +5 -1
  32. package/dist/modes/rpc/rpc-client.js.map +1 -1
  33. package/dist/utils/sleep.d.ts.map +1 -1
  34. package/dist/utils/sleep.js +7 -3
  35. package/dist/utils/sleep.js.map +1 -1
  36. package/dist/utils/tools-manager.d.ts.map +1 -1
  37. package/dist/utils/tools-manager.js.map +1 -1
  38. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  39. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  40. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  41. package/examples/extensions/sandbox/package-lock.json +2 -2
  42. package/examples/extensions/sandbox/package.json +1 -1
  43. package/examples/extensions/with-deps/package-lock.json +2 -2
  44. package/examples/extensions/with-deps/package.json +1 -1
  45. package/npm-shrinkwrap.json +12 -12
  46. package/package.json +4 -4
@@ -21,15 +21,26 @@ export declare class MCPManager {
21
21
  }>;
22
22
  private setStatus;
23
23
  start(serverConfigs: Record<string, MCPServerConfig>): Promise<void>;
24
+ /** Start a single server (used by reloadMcp for targeted toggle). */
25
+ startServer(serverName: string, config: MCPServerConfig): Promise<void>;
26
+ /** Stop a single server without affecting others. */
27
+ stopServer(serverName: string): Promise<void>;
24
28
  private connectServer;
25
29
  private buildToolDefs;
26
30
  private scheduleReconnect;
27
31
  /** Manually trigger reconnection for a specific server */
28
32
  reconnect(serverName: string): Promise<void>;
29
33
  getToolDefinitions(): ToolDefinition[];
34
+ /** Get all MCP tool descriptions grouped by server (for system prompt). */
35
+ getMcpToolDescriptions(): Array<{
36
+ serverName: string;
37
+ tools: Array<{
38
+ name: string;
39
+ description: string;
40
+ }>;
41
+ }>;
30
42
  getServerNames(): string[];
31
43
  getResourceUris(): string[];
32
- readResource(uri: string): Promise<string>;
33
44
  stop(): Promise<void>;
34
45
  }
35
46
  //# sourceMappingURL=manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAqB,MAAM,YAAY,CAAC;AAuCzE,MAAM,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAE/F,qBAAa,UAAU;IACtB,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,eAAe,CAAoD;IAC3E,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,cAAc,CAA0C;IAEhE,yDAAyD;IACzD,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAErD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,CAEvD;IAED,cAAc,IAAI,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAM9F;IAED,OAAO,CAAC,SAAS;IAWX,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzE;YAEa,aAAa;IAsC3B,OAAO,CAAC,aAAa;IAkFrB,OAAO,CAAC,iBAAiB;IA6CzB,0DAA0D;IACpD,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBjD;IAGD,kBAAkB,IAAI,cAAc,EAAE,CAErC;IAED,cAAc,IAAI,MAAM,EAAE,CAEzB;IAED,eAAe,IAAI,MAAM,EAAE,CAE1B;IAEK,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ/C;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsB1B;CACD","sourcesContent":["import type { AgentToolResult } from \"@openeryc/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { MCPClient, type MCPServerConfig } from \"./client.ts\";\nimport type { MCPConnectionStatus, MCPToolDefinition } from \"./types.ts\";\n\nconst DEFAULT_RECONNECT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RECONNECT_INTERVAL_MS = 5000;\n\nfunction toTypeBox(schema: MCPToolDefinition[\"inputSchema\"]): ReturnType<typeof Type.Object> {\n\tif (!schema.properties || Object.keys(schema.properties).length === 0) {\n\t\treturn Type.Object({});\n\t}\n\tconst required = new Set(schema.required ?? []);\n\tconst fields: Record<string, unknown> = {};\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst p = prop as { type?: string; description?: string; enum?: string[] };\n\t\tconst t = p.type ?? \"string\";\n\t\tconst desc = p.description ? { description: p.description } : {};\n\t\tlet f: unknown;\n\t\tswitch (t) {\n\t\t\tcase \"string\":\n\t\t\t\tf = p.enum ? Type.Unsafe<string>({ type: \"string\", enum: p.enum, ...desc }) : Type.String(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"number\":\n\t\t\tcase \"integer\":\n\t\t\t\tf = Type.Number(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tf = Type.Boolean(desc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tf = Type.Any(desc);\n\t\t}\n\t\tfields[key] = required.has(key) ? f : Type.Optional(f as never);\n\t}\n\treturn Type.Object(fields as Record<string, ReturnType<typeof Type.String>>);\n}\n\nfunction sanitizeMcpName(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport type MCPStatusChangeHandler = (serverName: string, status: MCPConnectionStatus) => void;\n\nexport class MCPManager {\n\tprivate clients = new Map<string, MCPClient>();\n\tprivate serverConfigs = new Map<string, MCPServerConfig>();\n\tprivate tools = new Map<string, ToolDefinition[]>();\n\tprivate resourceUris: string[] = [];\n\tprivate reconnectTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate reconnectAttempts = new Map<string, number>();\n\tprivate _statuses = new Map<string, MCPConnectionStatus>();\n\tprivate toolTimeoutMap = new Map<string, Map<string, number>>();\n\n\t/** Called when any server's connection status changes */\n\tonStatusChange: MCPStatusChangeHandler | null = null;\n\n\tgetServerStatus(serverName: string): MCPConnectionStatus {\n\t\treturn this._statuses.get(serverName) ?? \"disconnected\";\n\t}\n\n\tgetAllStatuses(): Array<{ serverName: string; status: MCPConnectionStatus; toolCount: number }> {\n\t\treturn [...this._statuses.entries()].map(([serverName, status]) => ({\n\t\t\tserverName,\n\t\t\tstatus,\n\t\t\ttoolCount: this.tools.get(serverName)?.length ?? 0,\n\t\t}));\n\t}\n\n\tprivate setStatus(serverName: string, status: MCPConnectionStatus): void {\n\t\tconst prev = this._statuses.get(serverName);\n\t\tif (prev === status) return;\n\t\tthis._statuses.set(serverName, status);\n\t\ttry {\n\t\t\tthis.onStatusChange?.(serverName, status);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync start(serverConfigs: Record<string, MCPServerConfig>): Promise<void> {\n\t\tfor (const [serverName, config] of Object.entries(serverConfigs)) {\n\t\t\tif (config.enabled === false) {\n\t\t\t\tthis.setStatus(serverName, \"disabled\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.serverConfigs.set(serverName, config);\n\t\t\tif (config.toolTimeouts) {\n\t\t\t\tthis.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));\n\t\t\t}\n\t\t\tawait this.connectServer(serverName, config);\n\t\t}\n\t}\n\n\tprivate async connectServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\t\ttry {\n\t\t\tconst client = new MCPClient(config.timeoutMs);\n\t\t\tclient.onDisconnect = (_reason) => {\n\t\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\t\tthis.tools.delete(serverName);\n\t\t\t\tthis.scheduleReconnect(serverName);\n\t\t\t};\n\n\t\t\tawait client.connect(config);\n\t\t\tconst { tools: mcpTools } = await client.listTools();\n\t\t\tthis.clients.set(serverName, client);\n\n\t\t\tthis.buildToolDefs(serverName, mcpTools);\n\n\t\t\t// Discover resources\n\t\t\ttry {\n\t\t\t\tconst { resources } = await client.listResources();\n\t\t\t\tfor (const r of resources) {\n\t\t\t\t\tthis.resourceUris.push(`mcp://${serverName}${r.uri}`);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Server may not support resources\n\t\t\t}\n\n\t\t\tthis.setStatus(serverName, \"connected\");\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t} catch (error) {\n\t\t\tthis.clients.delete(serverName);\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to connect to MCP server \"${serverName}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\tthis.scheduleReconnect(serverName);\n\t\t}\n\t}\n\n\tprivate buildToolDefs(serverName: string, mcpTools: MCPToolDefinition[]): void {\n\t\tconst safeServerName = sanitizeMcpName(serverName);\n\t\tconst client = this.clients.get(serverName)!;\n\t\tconst perToolTimeout = this.toolTimeoutMap.get(serverName);\n\n\t\tconst defs: ToolDefinition[] = mcpTools.map((tool) => {\n\t\t\t// Resolve effective timeout: per-tool > server default > client default (120s)\n\t\t\tconst toolSpecificTimeoutMs = perToolTimeout?.get(tool.name);\n\n\t\t\t// Add optional _timeoutMs parameter so the AI can override timeout per-call\n\t\t\tconst baseParams = toTypeBox(tool.inputSchema);\n\t\t\t// Merge _timeoutMs into the existing schema (preserving required fields)\n\t\t\tconst existingProps = (baseParams as any).properties ?? {};\n\t\t\tconst existingRequired = (baseParams as any).required ?? [];\n\t\t\tconst parameters = Type.Object(\n\t\t\t\t{\n\t\t\t\t\t...existingProps,\n\t\t\t\t\t_timeoutMs: Type.Optional(\n\t\t\t\t\t\tType.Number({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Optional timeout override in milliseconds for this specific call (e.g. 300000 for 5 min, 5000 for 5s). Overrides the server default timeout.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t{ required: existingRequired },\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tname: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,\n\t\t\t\tlabel: `mcp.${serverName}.${tool.name}`,\n\t\t\t\tdescription: tool.description\n\t\t\t\t\t? `${tool.description} (from MCP server \"${serverName}\")`\n\t\t\t\t\t: `Tool from MCP server \"${serverName}\"`,\n\t\t\t\tpromptSnippet: tool.description\n\t\t\t\t\t? `[${serverName}] ${tool.description.split(\"\\n\")[0]}`\n\t\t\t\t\t: `[${serverName}] ${tool.name}`,\n\t\t\t\tparameters,\n\t\t\t\trenderShell: \"default\" as const,\n\t\t\t\texecute: async (_id, params, signal) => {\n\t\t\t\t\tif (!client.isConnected) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\t\t\ttext: `MCP server \"${serverName}\" is disconnected. Use /mcp to check status.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { error: \"disconnected\" },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Extract _timeoutMs from params (AI-specified timeout override)\n\t\t\t\t\t\tconst rawParams = params as Record<string, unknown>;\n\t\t\t\t\t\tconst callTimeoutMs =\n\t\t\t\t\t\t\ttypeof rawParams._timeoutMs === \"number\" ? rawParams._timeoutMs : toolSpecificTimeoutMs;\n\t\t\t\t\t\tconst { _timeoutMs: _ignored, ...mcpParams } = rawParams;\n\n\t\t\t\t\t\tconst effectiveSignal =\n\t\t\t\t\t\t\tcallTimeoutMs !== undefined\n\t\t\t\t\t\t\t\t? AbortSignal.any([AbortSignal.timeout(callTimeoutMs), ...(signal ? [signal] : [])])\n\t\t\t\t\t\t\t\t: signal;\n\t\t\t\t\t\tconst result = await client.callTool(tool.name, mcpParams, effectiveSignal);\n\t\t\t\t\t\tconst text = result.content\n\t\t\t\t\t\t\t.map((item) => (item.type === \"text\" && item.text ? item.text : `[Image: ${item.mimeType}]`))\n\t\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\t\t\t\tdetails: result,\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `MCP tool error: ${msg}` }],\n\t\t\t\t\t\t\tdetails: { error: msg },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t\tthis.tools.set(serverName, defs);\n\t}\n\n\tprivate scheduleReconnect(serverName: string): void {\n\t\t// Cancel any existing reconnect timer for this server\n\t\tconst existing = this.reconnectTimers.get(serverName);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) return;\n\n\t\tconst reconnectCfg = config.reconnect ?? {};\n\t\tif (reconnectCfg.enabled === false) return;\n\n\t\tconst maxAttempts = reconnectCfg.maxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS;\n\t\tconst intervalMs = reconnectCfg.intervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;\n\t\tconst attempt = (this.reconnectAttempts.get(serverName) ?? 0) + 1;\n\n\t\tif (attempt > maxAttempts) {\n\t\t\tconsole.warn(`MCP server \"${serverName}\": max reconnection attempts (${maxAttempts}) reached, giving up.`);\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.set(serverName, attempt);\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\n\t\tconst timer = setTimeout(async () => {\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t\tconst cfg = this.serverConfigs.get(serverName);\n\t\t\tif (!cfg) return;\n\n\t\t\t// Remove old client if any\n\t\t\tconst oldClient = this.clients.get(serverName);\n\t\t\tif (oldClient) {\n\t\t\t\toldClient.onDisconnect = null;\n\t\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t}\n\n\t\t\tawait this.connectServer(serverName, cfg);\n\t\t}, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x\n\n\t\tthis.reconnectTimers.set(serverName, timer);\n\t}\n\n\t/** Manually trigger reconnection for a specific server */\n\tasync reconnect(serverName: string): Promise<void> {\n\t\tconst existingTimer = this.reconnectTimers.get(serverName);\n\t\tif (existingTimer) {\n\t\t\tclearTimeout(existingTimer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\n\t\tconst oldClient = this.clients.get(serverName);\n\t\tif (oldClient) {\n\t\t\toldClient.onDisconnect = null;\n\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\tthis.clients.delete(serverName);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) {\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\t// Rebuild tool definitions for a server (used after reconnect to pick up any changes)\n\tgetToolDefinitions(): ToolDefinition[] {\n\t\treturn Array.from(this.tools.values()).flat();\n\t}\n\n\tgetServerNames(): string[] {\n\t\treturn [...this.clients.keys()];\n\t}\n\n\tgetResourceUris(): string[] {\n\t\treturn this.resourceUris;\n\t}\n\n\tasync readResource(uri: string): Promise<string> {\n\t\tconst serverName = this.clients.keys().next().value;\n\t\tif (!serverName) throw new Error(\"No MCP servers connected\");\n\t\tconst mcpUri = uri.startsWith(`mcp://${serverName}`) ? uri.slice(`mcp://${serverName}`.length) : uri;\n\t\tconst client = this.clients.get(serverName);\n\t\tif (!client) throw new Error(`MCP server \"${serverName}\" not connected`);\n\t\tconst result = await client.readResource(mcpUri);\n\t\treturn result.contents.map((c) => c.text ?? \"\").join(\"\\n\");\n\t}\n\n\tasync stop(): Promise<void> {\n\t\t// Cancel all reconnect timers\n\t\tfor (const [, timer] of this.reconnectTimers) {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t\tthis.reconnectTimers.clear();\n\t\tthis.reconnectAttempts.clear();\n\n\t\tconst pending = [...this.clients.entries()].map(async ([name, client]) => {\n\t\t\ttry {\n\t\t\t\tawait client.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Error disconnecting MCP server \"${name}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tthis.clients.clear();\n\t\tthis.tools.clear();\n\t\tthis.resourceUris = [];\n\t\tthis._statuses.clear();\n\t\tawait Promise.all(pending);\n\t}\n}\n"]}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAqB,MAAM,YAAY,CAAC;AAuCzE,MAAM,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAE/F,qBAAa,UAAU;IACtB,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,eAAe,CAAoD;IAC3E,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,cAAc,CAA0C;IAEhE,yDAAyD;IACzD,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAErD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,CAEvD;IAED,cAAc,IAAI,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAM9F;IAED,OAAO,CAAC,SAAS;IAWX,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAezE;IAED,qEAAqE;IAC/D,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5E;IAED,qDAAqD;IAC/C,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBlD;YAEa,aAAa;IA0D3B,OAAO,CAAC,aAAa;IAgFrB,OAAO,CAAC,iBAAiB;IA+CzB,0DAA0D;IACpD,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBjD;IAED,kBAAkB,IAAI,cAAc,EAAE,CAErC;IAED,2EAA2E;IAC3E,sBAAsB,IAAI,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAU3G;IAED,cAAc,IAAI,MAAM,EAAE,CAEzB;IAED,eAAe,IAAI,MAAM,EAAE,CAE1B;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsB1B;CACD","sourcesContent":["import type { AgentToolResult } from \"@openeryc/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { MCPClient, type MCPServerConfig } from \"./client.ts\";\nimport type { MCPConnectionStatus, MCPToolDefinition } from \"./types.ts\";\n\nconst DEFAULT_RECONNECT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RECONNECT_INTERVAL_MS = 5000;\n\nfunction toTypeBox(schema: MCPToolDefinition[\"inputSchema\"]): ReturnType<typeof Type.Object> {\n\tif (!schema.properties || Object.keys(schema.properties).length === 0) {\n\t\treturn Type.Object({});\n\t}\n\tconst required = new Set(schema.required ?? []);\n\tconst fields: Record<string, unknown> = {};\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst p = prop as { type?: string; description?: string; enum?: string[] };\n\t\tconst t = p.type ?? \"string\";\n\t\tconst desc = p.description ? { description: p.description } : {};\n\t\tlet f: unknown;\n\t\tswitch (t) {\n\t\t\tcase \"string\":\n\t\t\t\tf = p.enum ? Type.Unsafe<string>({ type: \"string\", enum: p.enum, ...desc }) : Type.String(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"number\":\n\t\t\tcase \"integer\":\n\t\t\t\tf = Type.Number(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tf = Type.Boolean(desc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tf = Type.Any(desc);\n\t\t}\n\t\tfields[key] = required.has(key) ? f : Type.Optional(f as never);\n\t}\n\treturn Type.Object(fields as Record<string, ReturnType<typeof Type.String>>);\n}\n\nfunction sanitizeMcpName(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport type MCPStatusChangeHandler = (serverName: string, status: MCPConnectionStatus) => void;\n\nexport class MCPManager {\n\tprivate clients = new Map<string, MCPClient>();\n\tprivate serverConfigs = new Map<string, MCPServerConfig>();\n\tprivate tools = new Map<string, ToolDefinition[]>();\n\tprivate resourceUris: string[] = [];\n\tprivate reconnectTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate reconnectAttempts = new Map<string, number>();\n\tprivate _statuses = new Map<string, MCPConnectionStatus>();\n\tprivate toolTimeoutMap = new Map<string, Map<string, number>>();\n\n\t/** Called when any server's connection status changes */\n\tonStatusChange: MCPStatusChangeHandler | null = null;\n\n\tgetServerStatus(serverName: string): MCPConnectionStatus {\n\t\treturn this._statuses.get(serverName) ?? \"disconnected\";\n\t}\n\n\tgetAllStatuses(): Array<{ serverName: string; status: MCPConnectionStatus; toolCount: number }> {\n\t\treturn [...this._statuses.entries()].map(([serverName, status]) => ({\n\t\t\tserverName,\n\t\t\tstatus,\n\t\t\ttoolCount: this.tools.get(serverName)?.length ?? 0,\n\t\t}));\n\t}\n\n\tprivate setStatus(serverName: string, status: MCPConnectionStatus): void {\n\t\tconst prev = this._statuses.get(serverName);\n\t\tif (prev === status) return;\n\t\tthis._statuses.set(serverName, status);\n\t\ttry {\n\t\t\tthis.onStatusChange?.(serverName, status);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync start(serverConfigs: Record<string, MCPServerConfig>): Promise<void> {\n\t\tconst pending: Array<Promise<void>> = [];\n\t\tfor (const [serverName, config] of Object.entries(serverConfigs)) {\n\t\t\tif (config.enabled === false) {\n\t\t\t\tthis.setStatus(serverName, \"disabled\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.serverConfigs.set(serverName, config);\n\t\t\tif (config.toolTimeouts) {\n\t\t\t\tthis.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));\n\t\t\t}\n\t\t\tpending.push(this.connectServer(serverName, config));\n\t\t}\n\t\t// Connect all servers in parallel for faster startup\n\t\tawait Promise.allSettled(pending);\n\t}\n\n\t/** Start a single server (used by reloadMcp for targeted toggle). */\n\tasync startServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.serverConfigs.set(serverName, config);\n\t\tif (config.toolTimeouts) {\n\t\t\tthis.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));\n\t\t}\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\t/** Stop a single server without affecting others. */\n\tasync stopServer(serverName: string): Promise<void> {\n\t\t// Cancel reconnect timer for this server\n\t\tconst timer = this.reconnectTimers.get(serverName);\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tthis.serverConfigs.delete(serverName);\n\t\tthis.toolTimeoutMap.delete(serverName);\n\t\tthis.tools.delete(serverName);\n\t\t// Also clean up resource URIs for this server\n\t\tthis.resourceUris = this.resourceUris.filter((u) => !u.startsWith(`mcp://${serverName}`));\n\n\t\tconst client = this.clients.get(serverName);\n\t\tif (client) {\n\t\t\tclient.onDisconnect = null;\n\t\t\tthis.clients.delete(serverName);\n\t\t\tawait client.disconnect().catch(() => {});\n\t\t}\n\t\tthis.setStatus(serverName, \"disabled\");\n\t}\n\n\tprivate async connectServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\t\tlet disconnected = false;\n\t\tconst onDisconnect = () => {\n\t\t\tif (disconnected) return;\n\t\t\tdisconnected = true;\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\tthis.tools.delete(serverName);\n\t\t\tthis.scheduleReconnect(serverName);\n\t\t};\n\n\t\ttry {\n\t\t\tconst client = new MCPClient(config.timeoutMs);\n\t\t\tclient.onDisconnect = () => onDisconnect();\n\n\t\t\tawait client.connect(config);\n\t\t\t// If disconnect happened during connect(), bail out\n\t\t\tif (disconnected) {\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { tools: mcpTools } = await client.listTools();\n\t\t\tif (disconnected) {\n\t\t\t\tclient.disconnect().catch(() => {});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.clients.set(serverName, client);\n\n\t\t\tthis.buildToolDefs(serverName, mcpTools);\n\n\t\t\ttry {\n\t\t\t\tconst { resources } = await client.listResources();\n\t\t\t\tfor (const r of resources) {\n\t\t\t\t\tthis.resourceUris.push(`mcp://${serverName}${r.uri}`);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Server may not support resources\n\t\t\t}\n\n\t\t\tif (disconnected) {\n\t\t\t\t// Was disconnected during tool/resource discovery\n\t\t\t\tclient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t\tthis.tools.delete(serverName);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.setStatus(serverName, \"connected\");\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t} catch (error) {\n\t\t\tonDisconnect();\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to connect to MCP server \"${serverName}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate buildToolDefs(serverName: string, mcpTools: MCPToolDefinition[]): void {\n\t\tconst safeServerName = sanitizeMcpName(serverName);\n\t\tconst client = this.clients.get(serverName)!;\n\t\tconst perToolTimeout = this.toolTimeoutMap.get(serverName);\n\n\t\tconst defs: ToolDefinition[] = mcpTools.map((tool) => {\n\t\t\t// Resolve effective timeout: per-tool > server default > client default (120s)\n\t\t\tconst toolSpecificTimeoutMs = perToolTimeout?.get(tool.name);\n\n\t\t\t// Add optional _timeoutMs parameter so the AI can override timeout per-call.\n\t\t\t// We must produce a flat properties schema (not allOf) because LLM providers\n\t\t\t// access tool.parameters.properties directly.\n\t\t\tconst baseParams = toTypeBox(tool.inputSchema);\n\t\t\tconst baseSchema = baseParams as unknown as Record<string, unknown>;\n\t\t\tconst mergedProps = {\n\t\t\t\t...((baseSchema.properties ?? {}) as Record<string, unknown>),\n\t\t\t\t_timeoutMs: {\n\t\t\t\t\ttype: \"number\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Optional timeout override in milliseconds for this specific call (e.g. 300000 for 5 min, 5000 for 5s). Overrides the server default timeout.\",\n\t\t\t\t},\n\t\t\t};\n\t\t\tconst mergedRequired = [...((baseSchema.required ?? []) as string[])];\n\t\t\tconst parameters = Type.Object(mergedProps, { required: mergedRequired });\n\n\t\t\treturn {\n\t\t\t\tname: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,\n\t\t\t\tlabel: `mcp.${serverName}.${tool.name}`,\n\t\t\t\tdescription: tool.description\n\t\t\t\t\t? `${tool.description} (from MCP server \"${serverName}\")`\n\t\t\t\t\t: `Tool from MCP server \"${serverName}\"`,\n\t\t\t\tpromptSnippet: tool.description\n\t\t\t\t\t? `[${serverName}] ${tool.description.split(\"\\n\")[0]}`\n\t\t\t\t\t: `[${serverName}] ${tool.name}`,\n\t\t\t\tparameters,\n\t\t\t\trenderShell: \"default\" as const,\n\t\t\t\texecute: async (_id, params, signal) => {\n\t\t\t\t\tif (!client.isConnected) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\t\t\ttext: `MCP server \"${serverName}\" is disconnected. Use /mcp to check status.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { error: \"disconnected\" },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Extract _timeoutMs from params (AI-specified timeout override)\n\t\t\t\t\t\tconst rawParams = params as Record<string, unknown>;\n\t\t\t\t\t\tconst callTimeoutMs =\n\t\t\t\t\t\t\ttypeof rawParams._timeoutMs === \"number\" ? rawParams._timeoutMs : toolSpecificTimeoutMs;\n\t\t\t\t\t\tconst { _timeoutMs: _ignored, ...mcpParams } = rawParams;\n\n\t\t\t\t\t\tconst effectiveSignal =\n\t\t\t\t\t\t\tcallTimeoutMs !== undefined\n\t\t\t\t\t\t\t\t? AbortSignal.any([AbortSignal.timeout(callTimeoutMs), ...(signal ? [signal] : [])])\n\t\t\t\t\t\t\t\t: signal;\n\t\t\t\t\t\tconst result = await client.callTool(tool.name, mcpParams, effectiveSignal);\n\t\t\t\t\t\tconst text = result.content\n\t\t\t\t\t\t\t.map((item) => (item.type === \"text\" && item.text ? item.text : `[Image: ${item.mimeType}]`))\n\t\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\t\t\t\tdetails: result,\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `MCP tool error: ${msg}` }],\n\t\t\t\t\t\t\tdetails: { error: msg },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t\tthis.tools.set(serverName, defs);\n\t}\n\n\tprivate scheduleReconnect(serverName: string): void {\n\t\t// Cancel any existing reconnect timer for this server\n\t\tconst existing = this.reconnectTimers.get(serverName);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) return;\n\n\t\tconst reconnectCfg = config.reconnect ?? {};\n\t\tif (reconnectCfg.enabled === false) return;\n\n\t\tconst maxAttempts = reconnectCfg.maxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS;\n\t\tconst intervalMs = reconnectCfg.intervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;\n\t\tconst attempt = (this.reconnectAttempts.get(serverName) ?? 0) + 1;\n\n\t\tif (attempt > maxAttempts) {\n\t\t\tconsole.warn(`MCP server \"${serverName}\": max reconnection attempts (${maxAttempts}) reached, giving up.`);\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.set(serverName, attempt);\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t\tconst cfg = this.serverConfigs.get(serverName);\n\t\t\tif (!cfg) return;\n\n\t\t\t// Remove old client if any\n\t\t\tconst oldClient = this.clients.get(serverName);\n\t\t\tif (oldClient) {\n\t\t\t\toldClient.onDisconnect = null;\n\t\t\t\toldClient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t}\n\n\t\t\tthis.connectServer(serverName, cfg).catch((err) =>\n\t\t\t\tconsole.warn(`MCP reconnect failed for \"${serverName}\":`, err),\n\t\t\t);\n\t\t}, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x\n\n\t\tthis.reconnectTimers.set(serverName, timer);\n\t}\n\n\t/** Manually trigger reconnection for a specific server */\n\tasync reconnect(serverName: string): Promise<void> {\n\t\tconst existingTimer = this.reconnectTimers.get(serverName);\n\t\tif (existingTimer) {\n\t\t\tclearTimeout(existingTimer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\n\t\tconst oldClient = this.clients.get(serverName);\n\t\tif (oldClient) {\n\t\t\toldClient.onDisconnect = null;\n\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\tthis.clients.delete(serverName);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) {\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\tgetToolDefinitions(): ToolDefinition[] {\n\t\treturn Array.from(this.tools.values()).flat();\n\t}\n\n\t/** Get all MCP tool descriptions grouped by server (for system prompt). */\n\tgetMcpToolDescriptions(): Array<{ serverName: string; tools: Array<{ name: string; description: string }> }> {\n\t\tconst result: Array<{ serverName: string; tools: Array<{ name: string; description: string }> }> = [];\n\t\tfor (const [serverName, defs] of this.tools) {\n\t\t\tconst tools = defs.map((d) => ({\n\t\t\t\tname: d.name,\n\t\t\t\tdescription: d.description ?? `Tool from MCP server \"${serverName}\"`,\n\t\t\t}));\n\t\t\tresult.push({ serverName, tools });\n\t\t}\n\t\treturn result;\n\t}\n\n\tgetServerNames(): string[] {\n\t\treturn [...this.clients.keys()];\n\t}\n\n\tgetResourceUris(): string[] {\n\t\treturn this.resourceUris;\n\t}\n\n\tasync stop(): Promise<void> {\n\t\t// Cancel all reconnect timers\n\t\tfor (const [, timer] of this.reconnectTimers) {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t\tthis.reconnectTimers.clear();\n\t\tthis.reconnectAttempts.clear();\n\n\t\tconst pending = [...this.clients.entries()].map(async ([name, client]) => {\n\t\t\ttry {\n\t\t\t\tawait client.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Error disconnecting MCP server \"${name}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tthis.clients.clear();\n\t\tthis.tools.clear();\n\t\tthis.resourceUris = [];\n\t\tthis._statuses.clear();\n\t\tawait Promise.all(pending);\n\t}\n}\n"]}
@@ -68,6 +68,7 @@ export class MCPManager {
68
68
  }
69
69
  }
70
70
  async start(serverConfigs) {
71
+ const pending = [];
71
72
  for (const [serverName, config] of Object.entries(serverConfigs)) {
72
73
  if (config.enabled === false) {
73
74
  this.setStatus(serverName, "disabled");
@@ -77,23 +78,68 @@ export class MCPManager {
77
78
  if (config.toolTimeouts) {
78
79
  this.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));
79
80
  }
80
- await this.connectServer(serverName, config);
81
+ pending.push(this.connectServer(serverName, config));
81
82
  }
83
+ // Connect all servers in parallel for faster startup
84
+ await Promise.allSettled(pending);
85
+ }
86
+ /** Start a single server (used by reloadMcp for targeted toggle). */
87
+ async startServer(serverName, config) {
88
+ this.serverConfigs.set(serverName, config);
89
+ if (config.toolTimeouts) {
90
+ this.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));
91
+ }
92
+ await this.connectServer(serverName, config);
93
+ }
94
+ /** Stop a single server without affecting others. */
95
+ async stopServer(serverName) {
96
+ // Cancel reconnect timer for this server
97
+ const timer = this.reconnectTimers.get(serverName);
98
+ if (timer) {
99
+ clearTimeout(timer);
100
+ this.reconnectTimers.delete(serverName);
101
+ }
102
+ this.reconnectAttempts.delete(serverName);
103
+ this.serverConfigs.delete(serverName);
104
+ this.toolTimeoutMap.delete(serverName);
105
+ this.tools.delete(serverName);
106
+ // Also clean up resource URIs for this server
107
+ this.resourceUris = this.resourceUris.filter((u) => !u.startsWith(`mcp://${serverName}`));
108
+ const client = this.clients.get(serverName);
109
+ if (client) {
110
+ client.onDisconnect = null;
111
+ this.clients.delete(serverName);
112
+ await client.disconnect().catch(() => { });
113
+ }
114
+ this.setStatus(serverName, "disabled");
82
115
  }
83
116
  async connectServer(serverName, config) {
84
117
  this.setStatus(serverName, "reconnecting");
118
+ let disconnected = false;
119
+ const onDisconnect = () => {
120
+ if (disconnected)
121
+ return;
122
+ disconnected = true;
123
+ this.setStatus(serverName, "disconnected");
124
+ this.tools.delete(serverName);
125
+ this.scheduleReconnect(serverName);
126
+ };
85
127
  try {
86
128
  const client = new MCPClient(config.timeoutMs);
87
- client.onDisconnect = (_reason) => {
88
- this.setStatus(serverName, "disconnected");
89
- this.tools.delete(serverName);
90
- this.scheduleReconnect(serverName);
91
- };
129
+ client.onDisconnect = () => onDisconnect();
92
130
  await client.connect(config);
131
+ // If disconnect happened during connect(), bail out
132
+ if (disconnected) {
133
+ this.clients.delete(serverName);
134
+ return;
135
+ }
93
136
  const { tools: mcpTools } = await client.listTools();
137
+ if (disconnected) {
138
+ client.disconnect().catch(() => { });
139
+ return;
140
+ }
94
141
  this.clients.set(serverName, client);
95
142
  this.buildToolDefs(serverName, mcpTools);
96
- // Discover resources
97
143
  try {
98
144
  const { resources } = await client.listResources();
99
145
  for (const r of resources) {
@@ -103,14 +149,19 @@ export class MCPManager {
103
149
  catch {
104
150
  // Server may not support resources
105
151
  }
152
+ if (disconnected) {
153
+ // Was disconnected during tool/resource discovery
154
+ client.disconnect().catch(() => { });
155
+ this.clients.delete(serverName);
156
+ this.tools.delete(serverName);
157
+ return;
158
+ }
106
159
  this.setStatus(serverName, "connected");
107
160
  this.reconnectAttempts.delete(serverName);
108
161
  }
109
162
  catch (error) {
110
- this.clients.delete(serverName);
163
+ onDisconnect();
111
164
  console.warn(`Failed to connect to MCP server "${serverName}": ${error instanceof Error ? error.message : String(error)}`);
112
- this.setStatus(serverName, "disconnected");
113
- this.scheduleReconnect(serverName);
114
165
  }
115
166
  }
116
167
  buildToolDefs(serverName, mcpTools) {
@@ -120,17 +171,20 @@ export class MCPManager {
120
171
  const defs = mcpTools.map((tool) => {
121
172
  // Resolve effective timeout: per-tool > server default > client default (120s)
122
173
  const toolSpecificTimeoutMs = perToolTimeout?.get(tool.name);
123
- // Add optional _timeoutMs parameter so the AI can override timeout per-call
174
+ // Add optional _timeoutMs parameter so the AI can override timeout per-call.
175
+ // We must produce a flat properties schema (not allOf) because LLM providers
176
+ // access tool.parameters.properties directly.
124
177
  const baseParams = toTypeBox(tool.inputSchema);
125
- // Merge _timeoutMs into the existing schema (preserving required fields)
126
- const existingProps = baseParams.properties ?? {};
127
- const existingRequired = baseParams.required ?? [];
128
- const parameters = Type.Object({
129
- ...existingProps,
130
- _timeoutMs: Type.Optional(Type.Number({
178
+ const baseSchema = baseParams;
179
+ const mergedProps = {
180
+ ...(baseSchema.properties ?? {}),
181
+ _timeoutMs: {
182
+ type: "number",
131
183
  description: "Optional timeout override in milliseconds for this specific call (e.g. 300000 for 5 min, 5000 for 5s). Overrides the server default timeout.",
132
- })),
133
- }, { required: existingRequired });
184
+ },
185
+ };
186
+ const mergedRequired = [...(baseSchema.required ?? [])];
187
+ const parameters = Type.Object(mergedProps, { required: mergedRequired });
134
188
  return {
135
189
  name: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,
136
190
  label: `mcp.${serverName}.${tool.name}`,
@@ -205,7 +259,7 @@ export class MCPManager {
205
259
  }
206
260
  this.reconnectAttempts.set(serverName, attempt);
207
261
  this.setStatus(serverName, "reconnecting");
208
- const timer = setTimeout(async () => {
262
+ const timer = setTimeout(() => {
209
263
  this.reconnectTimers.delete(serverName);
210
264
  const cfg = this.serverConfigs.get(serverName);
211
265
  if (!cfg)
@@ -214,10 +268,10 @@ export class MCPManager {
214
268
  const oldClient = this.clients.get(serverName);
215
269
  if (oldClient) {
216
270
  oldClient.onDisconnect = null;
217
- await oldClient.disconnect().catch(() => { });
271
+ oldClient.disconnect().catch(() => { });
218
272
  this.clients.delete(serverName);
219
273
  }
220
- await this.connectServer(serverName, cfg);
274
+ this.connectServer(serverName, cfg).catch((err) => console.warn(`MCP reconnect failed for "${serverName}":`, err));
221
275
  }, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x
222
276
  this.reconnectTimers.set(serverName, timer);
223
277
  }
@@ -242,27 +296,27 @@ export class MCPManager {
242
296
  this.reconnectAttempts.delete(serverName);
243
297
  await this.connectServer(serverName, config);
244
298
  }
245
- // Rebuild tool definitions for a server (used after reconnect to pick up any changes)
246
299
  getToolDefinitions() {
247
300
  return Array.from(this.tools.values()).flat();
248
301
  }
302
+ /** Get all MCP tool descriptions grouped by server (for system prompt). */
303
+ getMcpToolDescriptions() {
304
+ const result = [];
305
+ for (const [serverName, defs] of this.tools) {
306
+ const tools = defs.map((d) => ({
307
+ name: d.name,
308
+ description: d.description ?? `Tool from MCP server "${serverName}"`,
309
+ }));
310
+ result.push({ serverName, tools });
311
+ }
312
+ return result;
313
+ }
249
314
  getServerNames() {
250
315
  return [...this.clients.keys()];
251
316
  }
252
317
  getResourceUris() {
253
318
  return this.resourceUris;
254
319
  }
255
- async readResource(uri) {
256
- const serverName = this.clients.keys().next().value;
257
- if (!serverName)
258
- throw new Error("No MCP servers connected");
259
- const mcpUri = uri.startsWith(`mcp://${serverName}`) ? uri.slice(`mcp://${serverName}`.length) : uri;
260
- const client = this.clients.get(serverName);
261
- if (!client)
262
- throw new Error(`MCP server "${serverName}" not connected`);
263
- const result = await client.readResource(mcpUri);
264
- return result.contents.map((c) => c.text ?? "").join("\n");
265
- }
266
320
  async stop() {
267
321
  // Cancel all reconnect timers
268
322
  for (const [, timer] of this.reconnectTimers) {
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentToolResult } from \"@openeryc/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { MCPClient, type MCPServerConfig } from \"./client.ts\";\nimport type { MCPConnectionStatus, MCPToolDefinition } from \"./types.ts\";\n\nconst DEFAULT_RECONNECT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RECONNECT_INTERVAL_MS = 5000;\n\nfunction toTypeBox(schema: MCPToolDefinition[\"inputSchema\"]): ReturnType<typeof Type.Object> {\n\tif (!schema.properties || Object.keys(schema.properties).length === 0) {\n\t\treturn Type.Object({});\n\t}\n\tconst required = new Set(schema.required ?? []);\n\tconst fields: Record<string, unknown> = {};\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst p = prop as { type?: string; description?: string; enum?: string[] };\n\t\tconst t = p.type ?? \"string\";\n\t\tconst desc = p.description ? { description: p.description } : {};\n\t\tlet f: unknown;\n\t\tswitch (t) {\n\t\t\tcase \"string\":\n\t\t\t\tf = p.enum ? Type.Unsafe<string>({ type: \"string\", enum: p.enum, ...desc }) : Type.String(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"number\":\n\t\t\tcase \"integer\":\n\t\t\t\tf = Type.Number(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tf = Type.Boolean(desc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tf = Type.Any(desc);\n\t\t}\n\t\tfields[key] = required.has(key) ? f : Type.Optional(f as never);\n\t}\n\treturn Type.Object(fields as Record<string, ReturnType<typeof Type.String>>);\n}\n\nfunction sanitizeMcpName(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport type MCPStatusChangeHandler = (serverName: string, status: MCPConnectionStatus) => void;\n\nexport class MCPManager {\n\tprivate clients = new Map<string, MCPClient>();\n\tprivate serverConfigs = new Map<string, MCPServerConfig>();\n\tprivate tools = new Map<string, ToolDefinition[]>();\n\tprivate resourceUris: string[] = [];\n\tprivate reconnectTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate reconnectAttempts = new Map<string, number>();\n\tprivate _statuses = new Map<string, MCPConnectionStatus>();\n\tprivate toolTimeoutMap = new Map<string, Map<string, number>>();\n\n\t/** Called when any server's connection status changes */\n\tonStatusChange: MCPStatusChangeHandler | null = null;\n\n\tgetServerStatus(serverName: string): MCPConnectionStatus {\n\t\treturn this._statuses.get(serverName) ?? \"disconnected\";\n\t}\n\n\tgetAllStatuses(): Array<{ serverName: string; status: MCPConnectionStatus; toolCount: number }> {\n\t\treturn [...this._statuses.entries()].map(([serverName, status]) => ({\n\t\t\tserverName,\n\t\t\tstatus,\n\t\t\ttoolCount: this.tools.get(serverName)?.length ?? 0,\n\t\t}));\n\t}\n\n\tprivate setStatus(serverName: string, status: MCPConnectionStatus): void {\n\t\tconst prev = this._statuses.get(serverName);\n\t\tif (prev === status) return;\n\t\tthis._statuses.set(serverName, status);\n\t\ttry {\n\t\t\tthis.onStatusChange?.(serverName, status);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync start(serverConfigs: Record<string, MCPServerConfig>): Promise<void> {\n\t\tfor (const [serverName, config] of Object.entries(serverConfigs)) {\n\t\t\tif (config.enabled === false) {\n\t\t\t\tthis.setStatus(serverName, \"disabled\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.serverConfigs.set(serverName, config);\n\t\t\tif (config.toolTimeouts) {\n\t\t\t\tthis.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));\n\t\t\t}\n\t\t\tawait this.connectServer(serverName, config);\n\t\t}\n\t}\n\n\tprivate async connectServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\t\ttry {\n\t\t\tconst client = new MCPClient(config.timeoutMs);\n\t\t\tclient.onDisconnect = (_reason) => {\n\t\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\t\tthis.tools.delete(serverName);\n\t\t\t\tthis.scheduleReconnect(serverName);\n\t\t\t};\n\n\t\t\tawait client.connect(config);\n\t\t\tconst { tools: mcpTools } = await client.listTools();\n\t\t\tthis.clients.set(serverName, client);\n\n\t\t\tthis.buildToolDefs(serverName, mcpTools);\n\n\t\t\t// Discover resources\n\t\t\ttry {\n\t\t\t\tconst { resources } = await client.listResources();\n\t\t\t\tfor (const r of resources) {\n\t\t\t\t\tthis.resourceUris.push(`mcp://${serverName}${r.uri}`);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Server may not support resources\n\t\t\t}\n\n\t\t\tthis.setStatus(serverName, \"connected\");\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t} catch (error) {\n\t\t\tthis.clients.delete(serverName);\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to connect to MCP server \"${serverName}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\tthis.scheduleReconnect(serverName);\n\t\t}\n\t}\n\n\tprivate buildToolDefs(serverName: string, mcpTools: MCPToolDefinition[]): void {\n\t\tconst safeServerName = sanitizeMcpName(serverName);\n\t\tconst client = this.clients.get(serverName)!;\n\t\tconst perToolTimeout = this.toolTimeoutMap.get(serverName);\n\n\t\tconst defs: ToolDefinition[] = mcpTools.map((tool) => {\n\t\t\t// Resolve effective timeout: per-tool > server default > client default (120s)\n\t\t\tconst toolSpecificTimeoutMs = perToolTimeout?.get(tool.name);\n\n\t\t\t// Add optional _timeoutMs parameter so the AI can override timeout per-call\n\t\t\tconst baseParams = toTypeBox(tool.inputSchema);\n\t\t\t// Merge _timeoutMs into the existing schema (preserving required fields)\n\t\t\tconst existingProps = (baseParams as any).properties ?? {};\n\t\t\tconst existingRequired = (baseParams as any).required ?? [];\n\t\t\tconst parameters = Type.Object(\n\t\t\t\t{\n\t\t\t\t\t...existingProps,\n\t\t\t\t\t_timeoutMs: Type.Optional(\n\t\t\t\t\t\tType.Number({\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Optional timeout override in milliseconds for this specific call (e.g. 300000 for 5 min, 5000 for 5s). Overrides the server default timeout.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t{ required: existingRequired },\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tname: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,\n\t\t\t\tlabel: `mcp.${serverName}.${tool.name}`,\n\t\t\t\tdescription: tool.description\n\t\t\t\t\t? `${tool.description} (from MCP server \"${serverName}\")`\n\t\t\t\t\t: `Tool from MCP server \"${serverName}\"`,\n\t\t\t\tpromptSnippet: tool.description\n\t\t\t\t\t? `[${serverName}] ${tool.description.split(\"\\n\")[0]}`\n\t\t\t\t\t: `[${serverName}] ${tool.name}`,\n\t\t\t\tparameters,\n\t\t\t\trenderShell: \"default\" as const,\n\t\t\t\texecute: async (_id, params, signal) => {\n\t\t\t\t\tif (!client.isConnected) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\t\t\ttext: `MCP server \"${serverName}\" is disconnected. Use /mcp to check status.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { error: \"disconnected\" },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Extract _timeoutMs from params (AI-specified timeout override)\n\t\t\t\t\t\tconst rawParams = params as Record<string, unknown>;\n\t\t\t\t\t\tconst callTimeoutMs =\n\t\t\t\t\t\t\ttypeof rawParams._timeoutMs === \"number\" ? rawParams._timeoutMs : toolSpecificTimeoutMs;\n\t\t\t\t\t\tconst { _timeoutMs: _ignored, ...mcpParams } = rawParams;\n\n\t\t\t\t\t\tconst effectiveSignal =\n\t\t\t\t\t\t\tcallTimeoutMs !== undefined\n\t\t\t\t\t\t\t\t? AbortSignal.any([AbortSignal.timeout(callTimeoutMs), ...(signal ? [signal] : [])])\n\t\t\t\t\t\t\t\t: signal;\n\t\t\t\t\t\tconst result = await client.callTool(tool.name, mcpParams, effectiveSignal);\n\t\t\t\t\t\tconst text = result.content\n\t\t\t\t\t\t\t.map((item) => (item.type === \"text\" && item.text ? item.text : `[Image: ${item.mimeType}]`))\n\t\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\t\t\t\tdetails: result,\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `MCP tool error: ${msg}` }],\n\t\t\t\t\t\t\tdetails: { error: msg },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t\tthis.tools.set(serverName, defs);\n\t}\n\n\tprivate scheduleReconnect(serverName: string): void {\n\t\t// Cancel any existing reconnect timer for this server\n\t\tconst existing = this.reconnectTimers.get(serverName);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) return;\n\n\t\tconst reconnectCfg = config.reconnect ?? {};\n\t\tif (reconnectCfg.enabled === false) return;\n\n\t\tconst maxAttempts = reconnectCfg.maxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS;\n\t\tconst intervalMs = reconnectCfg.intervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;\n\t\tconst attempt = (this.reconnectAttempts.get(serverName) ?? 0) + 1;\n\n\t\tif (attempt > maxAttempts) {\n\t\t\tconsole.warn(`MCP server \"${serverName}\": max reconnection attempts (${maxAttempts}) reached, giving up.`);\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.set(serverName, attempt);\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\n\t\tconst timer = setTimeout(async () => {\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t\tconst cfg = this.serverConfigs.get(serverName);\n\t\t\tif (!cfg) return;\n\n\t\t\t// Remove old client if any\n\t\t\tconst oldClient = this.clients.get(serverName);\n\t\t\tif (oldClient) {\n\t\t\t\toldClient.onDisconnect = null;\n\t\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t}\n\n\t\t\tawait this.connectServer(serverName, cfg);\n\t\t}, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x\n\n\t\tthis.reconnectTimers.set(serverName, timer);\n\t}\n\n\t/** Manually trigger reconnection for a specific server */\n\tasync reconnect(serverName: string): Promise<void> {\n\t\tconst existingTimer = this.reconnectTimers.get(serverName);\n\t\tif (existingTimer) {\n\t\t\tclearTimeout(existingTimer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\n\t\tconst oldClient = this.clients.get(serverName);\n\t\tif (oldClient) {\n\t\t\toldClient.onDisconnect = null;\n\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\tthis.clients.delete(serverName);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) {\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\t// Rebuild tool definitions for a server (used after reconnect to pick up any changes)\n\tgetToolDefinitions(): ToolDefinition[] {\n\t\treturn Array.from(this.tools.values()).flat();\n\t}\n\n\tgetServerNames(): string[] {\n\t\treturn [...this.clients.keys()];\n\t}\n\n\tgetResourceUris(): string[] {\n\t\treturn this.resourceUris;\n\t}\n\n\tasync readResource(uri: string): Promise<string> {\n\t\tconst serverName = this.clients.keys().next().value;\n\t\tif (!serverName) throw new Error(\"No MCP servers connected\");\n\t\tconst mcpUri = uri.startsWith(`mcp://${serverName}`) ? uri.slice(`mcp://${serverName}`.length) : uri;\n\t\tconst client = this.clients.get(serverName);\n\t\tif (!client) throw new Error(`MCP server \"${serverName}\" not connected`);\n\t\tconst result = await client.readResource(mcpUri);\n\t\treturn result.contents.map((c) => c.text ?? \"\").join(\"\\n\");\n\t}\n\n\tasync stop(): Promise<void> {\n\t\t// Cancel all reconnect timers\n\t\tfor (const [, timer] of this.reconnectTimers) {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t\tthis.reconnectTimers.clear();\n\t\tthis.reconnectAttempts.clear();\n\n\t\tconst pending = [...this.clients.entries()].map(async ([name, client]) => {\n\t\t\ttry {\n\t\t\t\tawait client.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Error disconnecting MCP server \"${name}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tthis.clients.clear();\n\t\tthis.tools.clear();\n\t\tthis.resourceUris = [];\n\t\tthis._statuses.clear();\n\t\tawait Promise.all(pending);\n\t}\n}\n"]}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentToolResult } from \"@openeryc/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { MCPClient, type MCPServerConfig } from \"./client.ts\";\nimport type { MCPConnectionStatus, MCPToolDefinition } from \"./types.ts\";\n\nconst DEFAULT_RECONNECT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RECONNECT_INTERVAL_MS = 5000;\n\nfunction toTypeBox(schema: MCPToolDefinition[\"inputSchema\"]): ReturnType<typeof Type.Object> {\n\tif (!schema.properties || Object.keys(schema.properties).length === 0) {\n\t\treturn Type.Object({});\n\t}\n\tconst required = new Set(schema.required ?? []);\n\tconst fields: Record<string, unknown> = {};\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst p = prop as { type?: string; description?: string; enum?: string[] };\n\t\tconst t = p.type ?? \"string\";\n\t\tconst desc = p.description ? { description: p.description } : {};\n\t\tlet f: unknown;\n\t\tswitch (t) {\n\t\t\tcase \"string\":\n\t\t\t\tf = p.enum ? Type.Unsafe<string>({ type: \"string\", enum: p.enum, ...desc }) : Type.String(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"number\":\n\t\t\tcase \"integer\":\n\t\t\t\tf = Type.Number(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tf = Type.Boolean(desc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tf = Type.Any(desc);\n\t\t}\n\t\tfields[key] = required.has(key) ? f : Type.Optional(f as never);\n\t}\n\treturn Type.Object(fields as Record<string, ReturnType<typeof Type.String>>);\n}\n\nfunction sanitizeMcpName(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport type MCPStatusChangeHandler = (serverName: string, status: MCPConnectionStatus) => void;\n\nexport class MCPManager {\n\tprivate clients = new Map<string, MCPClient>();\n\tprivate serverConfigs = new Map<string, MCPServerConfig>();\n\tprivate tools = new Map<string, ToolDefinition[]>();\n\tprivate resourceUris: string[] = [];\n\tprivate reconnectTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate reconnectAttempts = new Map<string, number>();\n\tprivate _statuses = new Map<string, MCPConnectionStatus>();\n\tprivate toolTimeoutMap = new Map<string, Map<string, number>>();\n\n\t/** Called when any server's connection status changes */\n\tonStatusChange: MCPStatusChangeHandler | null = null;\n\n\tgetServerStatus(serverName: string): MCPConnectionStatus {\n\t\treturn this._statuses.get(serverName) ?? \"disconnected\";\n\t}\n\n\tgetAllStatuses(): Array<{ serverName: string; status: MCPConnectionStatus; toolCount: number }> {\n\t\treturn [...this._statuses.entries()].map(([serverName, status]) => ({\n\t\t\tserverName,\n\t\t\tstatus,\n\t\t\ttoolCount: this.tools.get(serverName)?.length ?? 0,\n\t\t}));\n\t}\n\n\tprivate setStatus(serverName: string, status: MCPConnectionStatus): void {\n\t\tconst prev = this._statuses.get(serverName);\n\t\tif (prev === status) return;\n\t\tthis._statuses.set(serverName, status);\n\t\ttry {\n\t\t\tthis.onStatusChange?.(serverName, status);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync start(serverConfigs: Record<string, MCPServerConfig>): Promise<void> {\n\t\tconst pending: Array<Promise<void>> = [];\n\t\tfor (const [serverName, config] of Object.entries(serverConfigs)) {\n\t\t\tif (config.enabled === false) {\n\t\t\t\tthis.setStatus(serverName, \"disabled\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.serverConfigs.set(serverName, config);\n\t\t\tif (config.toolTimeouts) {\n\t\t\t\tthis.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));\n\t\t\t}\n\t\t\tpending.push(this.connectServer(serverName, config));\n\t\t}\n\t\t// Connect all servers in parallel for faster startup\n\t\tawait Promise.allSettled(pending);\n\t}\n\n\t/** Start a single server (used by reloadMcp for targeted toggle). */\n\tasync startServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.serverConfigs.set(serverName, config);\n\t\tif (config.toolTimeouts) {\n\t\t\tthis.toolTimeoutMap.set(serverName, new Map(Object.entries(config.toolTimeouts)));\n\t\t}\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\t/** Stop a single server without affecting others. */\n\tasync stopServer(serverName: string): Promise<void> {\n\t\t// Cancel reconnect timer for this server\n\t\tconst timer = this.reconnectTimers.get(serverName);\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tthis.serverConfigs.delete(serverName);\n\t\tthis.toolTimeoutMap.delete(serverName);\n\t\tthis.tools.delete(serverName);\n\t\t// Also clean up resource URIs for this server\n\t\tthis.resourceUris = this.resourceUris.filter((u) => !u.startsWith(`mcp://${serverName}`));\n\n\t\tconst client = this.clients.get(serverName);\n\t\tif (client) {\n\t\t\tclient.onDisconnect = null;\n\t\t\tthis.clients.delete(serverName);\n\t\t\tawait client.disconnect().catch(() => {});\n\t\t}\n\t\tthis.setStatus(serverName, \"disabled\");\n\t}\n\n\tprivate async connectServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\t\tlet disconnected = false;\n\t\tconst onDisconnect = () => {\n\t\t\tif (disconnected) return;\n\t\t\tdisconnected = true;\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\tthis.tools.delete(serverName);\n\t\t\tthis.scheduleReconnect(serverName);\n\t\t};\n\n\t\ttry {\n\t\t\tconst client = new MCPClient(config.timeoutMs);\n\t\t\tclient.onDisconnect = () => onDisconnect();\n\n\t\t\tawait client.connect(config);\n\t\t\t// If disconnect happened during connect(), bail out\n\t\t\tif (disconnected) {\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { tools: mcpTools } = await client.listTools();\n\t\t\tif (disconnected) {\n\t\t\t\tclient.disconnect().catch(() => {});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.clients.set(serverName, client);\n\n\t\t\tthis.buildToolDefs(serverName, mcpTools);\n\n\t\t\ttry {\n\t\t\t\tconst { resources } = await client.listResources();\n\t\t\t\tfor (const r of resources) {\n\t\t\t\t\tthis.resourceUris.push(`mcp://${serverName}${r.uri}`);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Server may not support resources\n\t\t\t}\n\n\t\t\tif (disconnected) {\n\t\t\t\t// Was disconnected during tool/resource discovery\n\t\t\t\tclient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t\tthis.tools.delete(serverName);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.setStatus(serverName, \"connected\");\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t} catch (error) {\n\t\t\tonDisconnect();\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to connect to MCP server \"${serverName}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate buildToolDefs(serverName: string, mcpTools: MCPToolDefinition[]): void {\n\t\tconst safeServerName = sanitizeMcpName(serverName);\n\t\tconst client = this.clients.get(serverName)!;\n\t\tconst perToolTimeout = this.toolTimeoutMap.get(serverName);\n\n\t\tconst defs: ToolDefinition[] = mcpTools.map((tool) => {\n\t\t\t// Resolve effective timeout: per-tool > server default > client default (120s)\n\t\t\tconst toolSpecificTimeoutMs = perToolTimeout?.get(tool.name);\n\n\t\t\t// Add optional _timeoutMs parameter so the AI can override timeout per-call.\n\t\t\t// We must produce a flat properties schema (not allOf) because LLM providers\n\t\t\t// access tool.parameters.properties directly.\n\t\t\tconst baseParams = toTypeBox(tool.inputSchema);\n\t\t\tconst baseSchema = baseParams as unknown as Record<string, unknown>;\n\t\t\tconst mergedProps = {\n\t\t\t\t...((baseSchema.properties ?? {}) as Record<string, unknown>),\n\t\t\t\t_timeoutMs: {\n\t\t\t\t\ttype: \"number\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Optional timeout override in milliseconds for this specific call (e.g. 300000 for 5 min, 5000 for 5s). Overrides the server default timeout.\",\n\t\t\t\t},\n\t\t\t};\n\t\t\tconst mergedRequired = [...((baseSchema.required ?? []) as string[])];\n\t\t\tconst parameters = Type.Object(mergedProps, { required: mergedRequired });\n\n\t\t\treturn {\n\t\t\t\tname: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,\n\t\t\t\tlabel: `mcp.${serverName}.${tool.name}`,\n\t\t\t\tdescription: tool.description\n\t\t\t\t\t? `${tool.description} (from MCP server \"${serverName}\")`\n\t\t\t\t\t: `Tool from MCP server \"${serverName}\"`,\n\t\t\t\tpromptSnippet: tool.description\n\t\t\t\t\t? `[${serverName}] ${tool.description.split(\"\\n\")[0]}`\n\t\t\t\t\t: `[${serverName}] ${tool.name}`,\n\t\t\t\tparameters,\n\t\t\t\trenderShell: \"default\" as const,\n\t\t\t\texecute: async (_id, params, signal) => {\n\t\t\t\t\tif (!client.isConnected) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\t\t\ttext: `MCP server \"${serverName}\" is disconnected. Use /mcp to check status.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { error: \"disconnected\" },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Extract _timeoutMs from params (AI-specified timeout override)\n\t\t\t\t\t\tconst rawParams = params as Record<string, unknown>;\n\t\t\t\t\t\tconst callTimeoutMs =\n\t\t\t\t\t\t\ttypeof rawParams._timeoutMs === \"number\" ? rawParams._timeoutMs : toolSpecificTimeoutMs;\n\t\t\t\t\t\tconst { _timeoutMs: _ignored, ...mcpParams } = rawParams;\n\n\t\t\t\t\t\tconst effectiveSignal =\n\t\t\t\t\t\t\tcallTimeoutMs !== undefined\n\t\t\t\t\t\t\t\t? AbortSignal.any([AbortSignal.timeout(callTimeoutMs), ...(signal ? [signal] : [])])\n\t\t\t\t\t\t\t\t: signal;\n\t\t\t\t\t\tconst result = await client.callTool(tool.name, mcpParams, effectiveSignal);\n\t\t\t\t\t\tconst text = result.content\n\t\t\t\t\t\t\t.map((item) => (item.type === \"text\" && item.text ? item.text : `[Image: ${item.mimeType}]`))\n\t\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\t\t\t\tdetails: result,\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `MCP tool error: ${msg}` }],\n\t\t\t\t\t\t\tdetails: { error: msg },\n\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t\tthis.tools.set(serverName, defs);\n\t}\n\n\tprivate scheduleReconnect(serverName: string): void {\n\t\t// Cancel any existing reconnect timer for this server\n\t\tconst existing = this.reconnectTimers.get(serverName);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) return;\n\n\t\tconst reconnectCfg = config.reconnect ?? {};\n\t\tif (reconnectCfg.enabled === false) return;\n\n\t\tconst maxAttempts = reconnectCfg.maxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS;\n\t\tconst intervalMs = reconnectCfg.intervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;\n\t\tconst attempt = (this.reconnectAttempts.get(serverName) ?? 0) + 1;\n\n\t\tif (attempt > maxAttempts) {\n\t\t\tconsole.warn(`MCP server \"${serverName}\": max reconnection attempts (${maxAttempts}) reached, giving up.`);\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.set(serverName, attempt);\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t\tconst cfg = this.serverConfigs.get(serverName);\n\t\t\tif (!cfg) return;\n\n\t\t\t// Remove old client if any\n\t\t\tconst oldClient = this.clients.get(serverName);\n\t\t\tif (oldClient) {\n\t\t\t\toldClient.onDisconnect = null;\n\t\t\t\toldClient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t}\n\n\t\t\tthis.connectServer(serverName, cfg).catch((err) =>\n\t\t\t\tconsole.warn(`MCP reconnect failed for \"${serverName}\":`, err),\n\t\t\t);\n\t\t}, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x\n\n\t\tthis.reconnectTimers.set(serverName, timer);\n\t}\n\n\t/** Manually trigger reconnection for a specific server */\n\tasync reconnect(serverName: string): Promise<void> {\n\t\tconst existingTimer = this.reconnectTimers.get(serverName);\n\t\tif (existingTimer) {\n\t\t\tclearTimeout(existingTimer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\n\t\tconst oldClient = this.clients.get(serverName);\n\t\tif (oldClient) {\n\t\t\toldClient.onDisconnect = null;\n\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\tthis.clients.delete(serverName);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) {\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\tgetToolDefinitions(): ToolDefinition[] {\n\t\treturn Array.from(this.tools.values()).flat();\n\t}\n\n\t/** Get all MCP tool descriptions grouped by server (for system prompt). */\n\tgetMcpToolDescriptions(): Array<{ serverName: string; tools: Array<{ name: string; description: string }> }> {\n\t\tconst result: Array<{ serverName: string; tools: Array<{ name: string; description: string }> }> = [];\n\t\tfor (const [serverName, defs] of this.tools) {\n\t\t\tconst tools = defs.map((d) => ({\n\t\t\t\tname: d.name,\n\t\t\t\tdescription: d.description ?? `Tool from MCP server \"${serverName}\"`,\n\t\t\t}));\n\t\t\tresult.push({ serverName, tools });\n\t\t}\n\t\treturn result;\n\t}\n\n\tgetServerNames(): string[] {\n\t\treturn [...this.clients.keys()];\n\t}\n\n\tgetResourceUris(): string[] {\n\t\treturn this.resourceUris;\n\t}\n\n\tasync stop(): Promise<void> {\n\t\t// Cancel all reconnect timers\n\t\tfor (const [, timer] of this.reconnectTimers) {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t\tthis.reconnectTimers.clear();\n\t\tthis.reconnectAttempts.clear();\n\n\t\tconst pending = [...this.clients.entries()].map(async ([name, client]) => {\n\t\t\ttry {\n\t\t\t\tawait client.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Error disconnecting MCP server \"${name}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tthis.clients.clear();\n\t\tthis.tools.clear();\n\t\tthis.resourceUris = [];\n\t\tthis._statuses.clear();\n\t\tawait Promise.all(pending);\n\t}\n}\n"]}
@@ -32,19 +32,6 @@ export interface MCPResource {
32
32
  description?: string;
33
33
  mimeType?: string;
34
34
  }
35
- export interface MCPResourceTemplate {
36
- uriTemplate: string;
37
- name: string;
38
- description?: string;
39
- }
40
- export interface MCPReadResourceResult {
41
- contents: Array<{
42
- uri: string;
43
- mimeType?: string;
44
- text?: string;
45
- blob?: string;
46
- }>;
47
- }
48
35
  export interface MCPToolDefinition {
49
36
  name: string;
50
37
  description?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IAClC,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0GAA0G;IAC1G,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,mCAAmC;IACnC,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,0CAA0C;AAC1C,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,UAAU,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\n\nexport interface MCPReconnectConfig {\n\t/** Whether auto-reconnect is enabled (default: true) */\n\tenabled?: boolean;\n\t/** Maximum reconnection attempts before giving up (default: 5) */\n\tmaxAttempts?: number;\n\t/** Delay between reconnection attempts in milliseconds (default: 5000) */\n\tintervalMs?: number;\n}\n\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n\t/** Per-tool timeout overrides (key: tool name, value: timeout in ms). Takes precedence over timeoutMs. */\n\ttoolTimeouts?: Record<string, number>;\n\t/** Auto-reconnect configuration */\n\treconnect?: MCPReconnectConfig;\n}\n\n/** Connection status for an MCP server */\nexport type MCPConnectionStatus = \"connected\" | \"disconnected\" | \"reconnecting\" | \"disabled\";\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPResourceTemplate {\n\turiTemplate: string;\n\tname: string;\n\tdescription?: string;\n}\n\nexport interface MCPReadResourceResult {\n\tcontents: Array<{\n\t\turi: string;\n\t\tmimeType?: string;\n\t\ttext?: string;\n\t\tblob?: string;\n\t}>;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IAClC,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0GAA0G;IAC1G,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,mCAAmC;IACnC,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,0CAA0C;AAC1C,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,UAAU,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\n\nexport interface MCPReconnectConfig {\n\t/** Whether auto-reconnect is enabled (default: true) */\n\tenabled?: boolean;\n\t/** Maximum reconnection attempts before giving up (default: 5) */\n\tmaxAttempts?: number;\n\t/** Delay between reconnection attempts in milliseconds (default: 5000) */\n\tintervalMs?: number;\n}\n\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n\t/** Per-tool timeout overrides (key: tool name, value: timeout in ms). Takes precedence over timeoutMs. */\n\ttoolTimeouts?: Record<string, number>;\n\t/** Auto-reconnect configuration */\n\treconnect?: MCPReconnectConfig;\n}\n\n/** Connection status for an MCP server */\nexport type MCPConnectionStatus = \"connected\" | \"disconnected\" | \"reconnecting\" | \"disabled\";\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\n\nexport interface MCPReconnectConfig {\n\t/** Whether auto-reconnect is enabled (default: true) */\n\tenabled?: boolean;\n\t/** Maximum reconnection attempts before giving up (default: 5) */\n\tmaxAttempts?: number;\n\t/** Delay between reconnection attempts in milliseconds (default: 5000) */\n\tintervalMs?: number;\n}\n\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n\t/** Per-tool timeout overrides (key: tool name, value: timeout in ms). Takes precedence over timeoutMs. */\n\ttoolTimeouts?: Record<string, number>;\n\t/** Auto-reconnect configuration */\n\treconnect?: MCPReconnectConfig;\n}\n\n/** Connection status for an MCP server */\nexport type MCPConnectionStatus = \"connected\" | \"disconnected\" | \"reconnecting\" | \"disabled\";\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPResourceTemplate {\n\turiTemplate: string;\n\tname: string;\n\tdescription?: string;\n}\n\nexport interface MCPReadResourceResult {\n\tcontents: Array<{\n\t\turi: string;\n\t\tmimeType?: string;\n\t\ttext?: string;\n\t\tblob?: string;\n\t}>;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\n\nexport interface MCPReconnectConfig {\n\t/** Whether auto-reconnect is enabled (default: true) */\n\tenabled?: boolean;\n\t/** Maximum reconnection attempts before giving up (default: 5) */\n\tmaxAttempts?: number;\n\t/** Delay between reconnection attempts in milliseconds (default: 5000) */\n\tintervalMs?: number;\n}\n\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n\t/** Per-tool timeout overrides (key: tool name, value: timeout in ms). Takes precedence over timeoutMs. */\n\ttoolTimeouts?: Record<string, number>;\n\t/** Auto-reconnect configuration */\n\treconnect?: MCPReconnectConfig;\n}\n\n/** Connection status for an MCP server */\nexport type MCPConnectionStatus = \"connected\" | \"disconnected\" | \"reconnecting\" | \"disabled\";\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}