@vfarcic/dot-ai 1.11.0 → 1.12.0

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 (44) hide show
  1. package/dist/core/ai-provider-factory.d.ts.map +1 -1
  2. package/dist/core/ai-provider-factory.js +1 -0
  3. package/dist/core/mcp-client-manager.d.ts +88 -0
  4. package/dist/core/mcp-client-manager.d.ts.map +1 -0
  5. package/dist/core/mcp-client-manager.js +362 -0
  6. package/dist/core/mcp-client-registry.d.ts +36 -0
  7. package/dist/core/mcp-client-registry.d.ts.map +1 -0
  8. package/dist/core/mcp-client-registry.js +52 -0
  9. package/dist/core/mcp-client-types.d.ts +84 -0
  10. package/dist/core/mcp-client-types.d.ts.map +1 -0
  11. package/dist/core/mcp-client-types.js +24 -0
  12. package/dist/core/model-config.d.ts +1 -0
  13. package/dist/core/model-config.d.ts.map +1 -1
  14. package/dist/core/model-config.js +1 -0
  15. package/dist/core/providers/vercel-provider.d.ts.map +1 -1
  16. package/dist/core/providers/vercel-provider.js +5 -0
  17. package/dist/interfaces/mcp.d.ts.map +1 -1
  18. package/dist/interfaces/mcp.js +13 -0
  19. package/dist/mcp/server.js +26 -1
  20. package/dist/tools/generate-manifests.d.ts.map +1 -1
  21. package/dist/tools/generate-manifests.js +2 -1
  22. package/dist/tools/impact-analysis.d.ts +51 -0
  23. package/dist/tools/impact-analysis.d.ts.map +1 -0
  24. package/dist/tools/impact-analysis.js +204 -0
  25. package/dist/tools/index.d.ts +1 -0
  26. package/dist/tools/index.d.ts.map +1 -1
  27. package/dist/tools/index.js +6 -1
  28. package/dist/tools/operate-analysis.d.ts +1 -1
  29. package/dist/tools/operate-analysis.d.ts.map +1 -1
  30. package/dist/tools/operate-analysis.js +17 -6
  31. package/dist/tools/operate.d.ts +1 -1
  32. package/dist/tools/operate.d.ts.map +1 -1
  33. package/dist/tools/operate.js +1 -1
  34. package/dist/tools/organizational-data.d.ts +1 -1
  35. package/dist/tools/query.d.ts +1 -1
  36. package/dist/tools/query.d.ts.map +1 -1
  37. package/dist/tools/query.js +18 -8
  38. package/dist/tools/remediate.d.ts.map +1 -1
  39. package/dist/tools/remediate.js +17 -7
  40. package/dist/tools/version.d.ts +12 -0
  41. package/dist/tools/version.d.ts.map +1 -1
  42. package/dist/tools/version.js +16 -1
  43. package/package.json +3 -1
  44. package/prompts/impact-analysis-system.md +32 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../src/core/ai-provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AA6BjC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IA2BnD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,aAAa,IAAI,UAAU;IAoFlC;;;;;OAKG;IACH,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWrD;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,IAAI,MAAM,EAAE;IAMxC;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
1
+ {"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../src/core/ai-provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AA8BjC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IA2BnD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,aAAa,IAAI,UAAU;IAoFlC;;;;;OAKG;IACH,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWrD;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,IAAI,MAAM,EAAE;IAMxC;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
@@ -28,6 +28,7 @@ const PROVIDER_ENV_KEYS = {
28
28
  google: 'GOOGLE_GENERATIVE_AI_API_KEY', // Standard Vercel AI SDK env var (also checks GOOGLE_API_KEY as fallback)
29
29
  google_flash: 'GOOGLE_GENERATIVE_AI_API_KEY', // PRD #294: Uses same API key as regular Google
30
30
  kimi: 'MOONSHOT_API_KEY', // PRD #353: Moonshot AI Kimi K2.5
31
+ alibaba: 'ALIBABA_API_KEY', // PRD #382: Alibaba Qwen 3.5 Plus
31
32
  xai: 'XAI_API_KEY',
32
33
  };
33
34
  const IMPLEMENTED_PROVIDERS = Object.keys(model_config_1.CURRENT_MODELS);
@@ -0,0 +1,88 @@
1
+ /**
2
+ * MCP Client Manager for dot-ai MCP Server Integration
3
+ *
4
+ * Connects to external MCP servers running in the cluster, discovers their tools,
5
+ * and makes them available to dot-ai operations (remediate, operate, query) via
6
+ * the attachTo routing mechanism.
7
+ *
8
+ * PRD #358: MCP Server Integration
9
+ */
10
+ import { McpServerConfig, DiscoveredMcpServer, McpServerStats, McpAttachableOperation } from './mcp-client-types';
11
+ import { Logger } from './error-handling';
12
+ import { AITool, ToolExecutor } from './ai-provider.interface';
13
+ /**
14
+ * Manages MCP server connections, tool discovery, and tool routing.
15
+ *
16
+ * Follows the same structural patterns as PluginManager but uses
17
+ * the MCP SDK (Client + StreamableHTTPClientTransport) instead of HTTP REST.
18
+ */
19
+ export declare class McpClientManager {
20
+ private readonly logger;
21
+ /** MCP SDK Client instances keyed by server name */
22
+ private readonly clients;
23
+ /** Transport instances keyed by server name (needed for cleanup) */
24
+ private readonly transports;
25
+ /** Discovered server metadata keyed by server name */
26
+ private readonly discoveredServers;
27
+ /** Maps namespaced tool name → server name for routing */
28
+ private readonly toolToServer;
29
+ constructor(logger: Logger);
30
+ /**
31
+ * Parse MCP server configuration from file.
32
+ *
33
+ * Reads from /etc/dot-ai-mcp/mcp-servers.json (mounted from ConfigMap in K8s).
34
+ * Returns empty array if file doesn't exist (MCP servers only work in-cluster).
35
+ * Throws on invalid JSON or malformed configuration.
36
+ */
37
+ static parseMcpServerConfig(): McpServerConfig[];
38
+ /**
39
+ * Discover all configured MCP servers.
40
+ *
41
+ * Connects to each server, performs MCP handshake, and discovers available tools.
42
+ * All servers must connect successfully — any failure throws McpDiscoveryError.
43
+ */
44
+ discoverMcpServers(configs: McpServerConfig[]): Promise<void>;
45
+ /**
46
+ * Connect to a single MCP server and discover its tools.
47
+ */
48
+ private connectAndDiscover;
49
+ /**
50
+ * Get tools available for a specific dot-ai operation, filtered by attachTo.
51
+ *
52
+ * Returns tools as AITool[] with namespaced names ({serverName}__{toolName}).
53
+ */
54
+ getToolsForOperation(operation: McpAttachableOperation): AITool[];
55
+ /**
56
+ * Get all discovered tools across all servers.
57
+ */
58
+ getAllDiscoveredTools(): AITool[];
59
+ /**
60
+ * Check if a tool name belongs to an MCP server (is namespaced).
61
+ */
62
+ isMcpTool(toolName: string): boolean;
63
+ /**
64
+ * Create a ToolExecutor that routes MCP tools to their servers.
65
+ *
66
+ * Returns a function compatible with toolLoop's toolExecutor parameter.
67
+ * MCP tools (namespaced) are routed to their MCP servers; non-MCP tools
68
+ * are routed to the optional fallback executor.
69
+ */
70
+ createToolExecutor(fallbackExecutor?: ToolExecutor): ToolExecutor;
71
+ /**
72
+ * Get statistics about MCP server connections.
73
+ */
74
+ getStats(): McpServerStats;
75
+ /**
76
+ * Get discovered server metadata.
77
+ */
78
+ getDiscoveredServers(): DiscoveredMcpServer[];
79
+ /**
80
+ * Close all MCP server connections.
81
+ */
82
+ close(): Promise<void>;
83
+ /**
84
+ * Convert an MCP tool definition to AITool format with namespaced name.
85
+ */
86
+ private convertToAITool;
87
+ }
88
+ //# sourceMappingURL=mcp-client-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-manager.d.ts","sourceRoot":"","sources":["../../src/core/mcp-client-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EACL,eAAe,EAEf,mBAAmB,EACnB,cAAc,EAEd,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAW/D;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyD;IAEpF,sDAAsD;IACtD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA+C;IAEjF,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;gBAEnD,MAAM,EAAE,MAAM;IAI1B;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,IAAI,eAAe,EAAE;IA+DhD;;;;;OAKG;IACG,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAwCnE;;OAEG;YACW,kBAAkB;IAmGhC;;;;OAIG;IACH,oBAAoB,CAAC,SAAS,EAAE,sBAAsB,GAAG,MAAM,EAAE;IAoBjE;;OAEG;IACH,qBAAqB,IAAI,MAAM,EAAE;IAejC;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIpC;;;;;;OAMG;IACH,kBAAkB,CAAC,gBAAgB,CAAC,EAAE,YAAY,GAAG,YAAY;IA6DjE;;OAEG;IACH,QAAQ,IAAI,cAAc;IAQ1B;;OAEG;IACH,oBAAoB,IAAI,mBAAmB,EAAE;IAI7C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;OAEG;IACH,OAAO,CAAC,eAAe;CAYxB"}
@@ -0,0 +1,362 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Client Manager for dot-ai MCP Server Integration
4
+ *
5
+ * Connects to external MCP servers running in the cluster, discovers their tools,
6
+ * and makes them available to dot-ai operations (remediate, operate, query) via
7
+ * the attachTo routing mechanism.
8
+ *
9
+ * PRD #358: MCP Server Integration
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.McpClientManager = void 0;
13
+ const node_fs_1 = require("node:fs");
14
+ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
15
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
16
+ const mcp_client_types_1 = require("./mcp-client-types");
17
+ /** Path for MCP servers config file (mounted from ConfigMap in K8s) */
18
+ const MCP_SERVERS_CONFIG_PATH = '/etc/dot-ai-mcp/mcp-servers.json';
19
+ /** Separator used to namespace MCP tools: {serverName}__{toolName} */
20
+ const TOOL_NAME_SEPARATOR = '__';
21
+ /** Default timeout for MCP requests in milliseconds */
22
+ const DEFAULT_TIMEOUT_MS = 30_000;
23
+ /**
24
+ * Manages MCP server connections, tool discovery, and tool routing.
25
+ *
26
+ * Follows the same structural patterns as PluginManager but uses
27
+ * the MCP SDK (Client + StreamableHTTPClientTransport) instead of HTTP REST.
28
+ */
29
+ class McpClientManager {
30
+ logger;
31
+ /** MCP SDK Client instances keyed by server name */
32
+ clients = new Map();
33
+ /** Transport instances keyed by server name (needed for cleanup) */
34
+ transports = new Map();
35
+ /** Discovered server metadata keyed by server name */
36
+ discoveredServers = new Map();
37
+ /** Maps namespaced tool name → server name for routing */
38
+ toolToServer = new Map();
39
+ constructor(logger) {
40
+ this.logger = logger;
41
+ }
42
+ /**
43
+ * Parse MCP server configuration from file.
44
+ *
45
+ * Reads from /etc/dot-ai-mcp/mcp-servers.json (mounted from ConfigMap in K8s).
46
+ * Returns empty array if file doesn't exist (MCP servers only work in-cluster).
47
+ * Throws on invalid JSON or malformed configuration.
48
+ */
49
+ static parseMcpServerConfig() {
50
+ if (!(0, node_fs_1.existsSync)(MCP_SERVERS_CONFIG_PATH)) {
51
+ return [];
52
+ }
53
+ let content;
54
+ try {
55
+ content = (0, node_fs_1.readFileSync)(MCP_SERVERS_CONFIG_PATH, 'utf-8');
56
+ }
57
+ catch (err) {
58
+ throw new Error(`Failed to read MCP server config at ${MCP_SERVERS_CONFIG_PATH}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
59
+ }
60
+ let parsed;
61
+ try {
62
+ parsed = JSON.parse(content);
63
+ }
64
+ catch (err) {
65
+ throw new Error(`Invalid JSON in MCP server config at ${MCP_SERVERS_CONFIG_PATH}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
66
+ }
67
+ if (!Array.isArray(parsed)) {
68
+ throw new Error(`MCP server config at ${MCP_SERVERS_CONFIG_PATH} must be an array, got ${typeof parsed}`);
69
+ }
70
+ const validOperations = ['remediate', 'operate', 'query'];
71
+ return parsed.map((s, index) => {
72
+ if (!s || typeof s !== 'object') {
73
+ throw new Error(`MCP server at index ${index} must be an object`);
74
+ }
75
+ if (!s.endpoint || typeof s.endpoint !== 'string') {
76
+ throw new Error(`MCP server at index ${index} (${s.name || 'unnamed'}) is missing required 'endpoint' field`);
77
+ }
78
+ if (!Array.isArray(s.attachTo) || s.attachTo.length === 0) {
79
+ throw new Error(`MCP server at index ${index} (${s.name || 'unnamed'}) must have a non-empty 'attachTo' array`);
80
+ }
81
+ for (const op of s.attachTo) {
82
+ if (!validOperations.includes(op)) {
83
+ throw new Error(`MCP server at index ${index} (${s.name || 'unnamed'}) has invalid attachTo value '${op}'. Must be one of: ${validOperations.join(', ')}`);
84
+ }
85
+ }
86
+ return {
87
+ name: s.name || `mcp-server-${index}`,
88
+ endpoint: s.endpoint,
89
+ attachTo: s.attachTo,
90
+ timeout: s.timeout,
91
+ };
92
+ });
93
+ }
94
+ /**
95
+ * Discover all configured MCP servers.
96
+ *
97
+ * Connects to each server, performs MCP handshake, and discovers available tools.
98
+ * All servers must connect successfully — any failure throws McpDiscoveryError.
99
+ */
100
+ async discoverMcpServers(configs) {
101
+ if (configs.length === 0) {
102
+ this.logger.debug('No MCP servers configured for discovery');
103
+ return;
104
+ }
105
+ this.logger.info('Starting MCP server discovery', {
106
+ serverCount: configs.length,
107
+ servers: configs.map(c => c.name),
108
+ });
109
+ const results = await Promise.allSettled(configs.map(config => this.connectAndDiscover(config)));
110
+ const failed = [];
111
+ results.forEach((result, index) => {
112
+ const config = configs[index];
113
+ if (result.status === 'rejected') {
114
+ const error = result.reason instanceof Error
115
+ ? result.reason.message
116
+ : String(result.reason);
117
+ failed.push({ name: config.name, error });
118
+ }
119
+ });
120
+ if (failed.length > 0) {
121
+ throw new mcp_client_types_1.McpDiscoveryError(`MCP server discovery failed: ${failed.map(f => `${f.name} (${f.error})`).join(', ')}`, failed);
122
+ }
123
+ this.logger.info('MCP server discovery complete', {
124
+ discovered: this.discoveredServers.size,
125
+ totalTools: this.toolToServer.size,
126
+ });
127
+ }
128
+ /**
129
+ * Connect to a single MCP server and discover its tools.
130
+ */
131
+ async connectAndDiscover(config) {
132
+ const timeout = config.timeout || DEFAULT_TIMEOUT_MS;
133
+ this.logger.debug('Connecting to MCP server', {
134
+ name: config.name,
135
+ endpoint: config.endpoint,
136
+ attachTo: config.attachTo,
137
+ });
138
+ const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(config.endpoint), {
139
+ reconnectionOptions: {
140
+ maxReconnectionDelay: 30_000,
141
+ initialReconnectionDelay: 1_000,
142
+ reconnectionDelayGrowFactor: 1.5,
143
+ maxRetries: 2,
144
+ },
145
+ });
146
+ const client = new index_js_1.Client({ name: 'dot-ai', version: '1.0.0' }, { capabilities: {} });
147
+ // Connect with timeout
148
+ const connectPromise = client.connect(transport);
149
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Connection timed out after ${timeout}ms`)), timeout));
150
+ try {
151
+ await Promise.race([connectPromise, timeoutPromise]);
152
+ }
153
+ catch (err) {
154
+ // Clean up transport on failure
155
+ try {
156
+ await transport.close();
157
+ }
158
+ catch { /* ignore cleanup errors */ }
159
+ throw new Error(`Failed to connect to MCP server '${config.name}' at ${config.endpoint}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
160
+ }
161
+ // Discover tools
162
+ let tools;
163
+ try {
164
+ const result = await client.listTools();
165
+ tools = result.tools.map(t => ({
166
+ name: t.name,
167
+ description: t.description,
168
+ inputSchema: {
169
+ type: t.inputSchema.type,
170
+ properties: t.inputSchema.properties,
171
+ required: t.inputSchema.required,
172
+ },
173
+ }));
174
+ }
175
+ catch (err) {
176
+ try {
177
+ await transport.close();
178
+ }
179
+ catch { /* ignore cleanup errors */ }
180
+ throw new Error(`Failed to list tools from MCP server '${config.name}': ${err instanceof Error ? err.message : String(err)}`, { cause: err });
181
+ }
182
+ // Store client and transport
183
+ this.clients.set(config.name, client);
184
+ this.transports.set(config.name, transport);
185
+ // Store discovered server metadata
186
+ this.discoveredServers.set(config.name, {
187
+ name: config.name,
188
+ endpoint: config.endpoint,
189
+ attachTo: config.attachTo,
190
+ version: client.getServerVersion()?.version,
191
+ tools,
192
+ discoveredAt: new Date(),
193
+ });
194
+ // Map namespaced tools to server
195
+ for (const tool of tools) {
196
+ const namespacedName = `${config.name}${TOOL_NAME_SEPARATOR}${tool.name}`;
197
+ if (this.toolToServer.has(namespacedName)) {
198
+ this.logger.warn('Namespaced tool name conflict - overwriting', {
199
+ tool: namespacedName,
200
+ existingServer: this.toolToServer.get(namespacedName),
201
+ newServer: config.name,
202
+ });
203
+ }
204
+ this.toolToServer.set(namespacedName, config.name);
205
+ }
206
+ this.logger.info('MCP server discovered', {
207
+ name: config.name,
208
+ version: client.getServerVersion()?.version,
209
+ tools: tools.map(t => t.name),
210
+ namespacedTools: tools.map(t => `${config.name}${TOOL_NAME_SEPARATOR}${t.name}`),
211
+ });
212
+ }
213
+ /**
214
+ * Get tools available for a specific dot-ai operation, filtered by attachTo.
215
+ *
216
+ * Returns tools as AITool[] with namespaced names ({serverName}__{toolName}).
217
+ */
218
+ getToolsForOperation(operation) {
219
+ const tools = [];
220
+ for (const server of this.discoveredServers.values()) {
221
+ if (!server.attachTo.includes(operation)) {
222
+ continue;
223
+ }
224
+ for (const tool of server.tools) {
225
+ const namespacedName = `${server.name}${TOOL_NAME_SEPARATOR}${tool.name}`;
226
+ // Only include if this server owns the routing
227
+ if (this.toolToServer.get(namespacedName) === server.name) {
228
+ tools.push(this.convertToAITool(server.name, tool));
229
+ }
230
+ }
231
+ }
232
+ return tools;
233
+ }
234
+ /**
235
+ * Get all discovered tools across all servers.
236
+ */
237
+ getAllDiscoveredTools() {
238
+ const tools = [];
239
+ for (const server of this.discoveredServers.values()) {
240
+ for (const tool of server.tools) {
241
+ const namespacedName = `${server.name}${TOOL_NAME_SEPARATOR}${tool.name}`;
242
+ if (this.toolToServer.get(namespacedName) === server.name) {
243
+ tools.push(this.convertToAITool(server.name, tool));
244
+ }
245
+ }
246
+ }
247
+ return tools;
248
+ }
249
+ /**
250
+ * Check if a tool name belongs to an MCP server (is namespaced).
251
+ */
252
+ isMcpTool(toolName) {
253
+ return this.toolToServer.has(toolName);
254
+ }
255
+ /**
256
+ * Create a ToolExecutor that routes MCP tools to their servers.
257
+ *
258
+ * Returns a function compatible with toolLoop's toolExecutor parameter.
259
+ * MCP tools (namespaced) are routed to their MCP servers; non-MCP tools
260
+ * are routed to the optional fallback executor.
261
+ */
262
+ createToolExecutor(fallbackExecutor) {
263
+ return async (toolName, input) => {
264
+ if (this.isMcpTool(toolName)) {
265
+ this.logger.debug('Routing tool to MCP server', {
266
+ tool: toolName,
267
+ server: this.toolToServer.get(toolName),
268
+ });
269
+ try {
270
+ const serverName = this.toolToServer.get(toolName);
271
+ const originalToolName = toolName.substring(serverName.length + TOOL_NAME_SEPARATOR.length);
272
+ const client = this.clients.get(serverName);
273
+ if (!client) {
274
+ return `Error: MCP server '${serverName}' is not connected`;
275
+ }
276
+ const result = await client.callTool({
277
+ name: originalToolName,
278
+ arguments: input,
279
+ });
280
+ // Extract text content from MCP response
281
+ const contentArray = result.content;
282
+ if (result.isError) {
283
+ const errorText = contentArray
284
+ ?.filter(c => c.type === 'text')
285
+ .map(c => c.text)
286
+ .join('\n') || 'Unknown MCP tool error';
287
+ return `Error: ${errorText}`;
288
+ }
289
+ // Return text content for AI consumption
290
+ const textContent = contentArray
291
+ ?.filter(c => c.type === 'text')
292
+ .map(c => c.text)
293
+ .join('\n');
294
+ return textContent || '';
295
+ }
296
+ catch (err) {
297
+ const message = err instanceof Error ? err.message : String(err);
298
+ this.logger.error('MCP tool invocation failed', new Error(message), { tool: toolName, server: this.toolToServer.get(toolName) });
299
+ return `Error: ${message}`;
300
+ }
301
+ }
302
+ // Fall back to provided executor for non-MCP tools
303
+ if (fallbackExecutor) {
304
+ return fallbackExecutor(toolName, input);
305
+ }
306
+ return `Error: Tool '${toolName}' not found in MCP servers or fallback executor`;
307
+ };
308
+ }
309
+ /**
310
+ * Get statistics about MCP server connections.
311
+ */
312
+ getStats() {
313
+ return {
314
+ serverCount: this.discoveredServers.size,
315
+ toolCount: this.toolToServer.size,
316
+ servers: Array.from(this.discoveredServers.keys()),
317
+ };
318
+ }
319
+ /**
320
+ * Get discovered server metadata.
321
+ */
322
+ getDiscoveredServers() {
323
+ return Array.from(this.discoveredServers.values());
324
+ }
325
+ /**
326
+ * Close all MCP server connections.
327
+ */
328
+ async close() {
329
+ for (const [name, transport] of this.transports.entries()) {
330
+ try {
331
+ await transport.close();
332
+ this.logger.debug('Closed MCP server connection', { name });
333
+ }
334
+ catch (err) {
335
+ this.logger.warn('Error closing MCP server connection', {
336
+ name,
337
+ error: err instanceof Error ? err.message : String(err),
338
+ });
339
+ }
340
+ }
341
+ this.clients.clear();
342
+ this.transports.clear();
343
+ this.discoveredServers.clear();
344
+ this.toolToServer.clear();
345
+ }
346
+ /**
347
+ * Convert an MCP tool definition to AITool format with namespaced name.
348
+ */
349
+ convertToAITool(serverName, tool) {
350
+ const namespacedName = `${serverName}${TOOL_NAME_SEPARATOR}${tool.name}`;
351
+ return {
352
+ name: namespacedName,
353
+ description: `[${serverName}] ${tool.description || tool.name}`,
354
+ inputSchema: {
355
+ type: 'object',
356
+ properties: tool.inputSchema.properties || {},
357
+ required: tool.inputSchema.required,
358
+ },
359
+ };
360
+ }
361
+ }
362
+ exports.McpClientManager = McpClientManager;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * MCP Client Registry - Unified MCP Server Tool Access
3
+ *
4
+ * PRD #358: Provides a single, consistent way to access MCP server tools
5
+ * from anywhere in the codebase (remediate, operate, query).
6
+ *
7
+ * Usage:
8
+ * // At startup (src/mcp/server.ts):
9
+ * initializeMcpClientRegistry(mcpClientManager);
10
+ *
11
+ * // Anywhere in the codebase:
12
+ * if (isMcpClientInitialized()) {
13
+ * const tools = getMcpClientManager()!.getToolsForOperation('remediate');
14
+ * }
15
+ */
16
+ import type { McpClientManager } from './mcp-client-manager';
17
+ /**
18
+ * Initialize the MCP client registry with a McpClientManager instance.
19
+ * Must be called once at startup after MCP servers are discovered.
20
+ */
21
+ export declare function initializeMcpClientRegistry(manager: McpClientManager): void;
22
+ /**
23
+ * Get the McpClientManager instance.
24
+ * Returns null if not initialized.
25
+ */
26
+ export declare function getMcpClientManager(): McpClientManager | null;
27
+ /**
28
+ * Check if the MCP client registry is initialized.
29
+ */
30
+ export declare function isMcpClientInitialized(): boolean;
31
+ /**
32
+ * Reset the MCP client registry (for testing only).
33
+ * @internal
34
+ */
35
+ export declare function resetMcpClientRegistry(): void;
36
+ //# sourceMappingURL=mcp-client-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-registry.d.ts","sourceRoot":"","sources":["../../src/core/mcp-client-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAO7D;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Client Registry - Unified MCP Server Tool Access
4
+ *
5
+ * PRD #358: Provides a single, consistent way to access MCP server tools
6
+ * from anywhere in the codebase (remediate, operate, query).
7
+ *
8
+ * Usage:
9
+ * // At startup (src/mcp/server.ts):
10
+ * initializeMcpClientRegistry(mcpClientManager);
11
+ *
12
+ * // Anywhere in the codebase:
13
+ * if (isMcpClientInitialized()) {
14
+ * const tools = getMcpClientManager()!.getToolsForOperation('remediate');
15
+ * }
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.initializeMcpClientRegistry = initializeMcpClientRegistry;
19
+ exports.getMcpClientManager = getMcpClientManager;
20
+ exports.isMcpClientInitialized = isMcpClientInitialized;
21
+ exports.resetMcpClientRegistry = resetMcpClientRegistry;
22
+ /**
23
+ * Global MCP client manager instance (set once at startup)
24
+ */
25
+ let mcpClientManager = null;
26
+ /**
27
+ * Initialize the MCP client registry with a McpClientManager instance.
28
+ * Must be called once at startup after MCP servers are discovered.
29
+ */
30
+ function initializeMcpClientRegistry(manager) {
31
+ mcpClientManager = manager;
32
+ }
33
+ /**
34
+ * Get the McpClientManager instance.
35
+ * Returns null if not initialized.
36
+ */
37
+ function getMcpClientManager() {
38
+ return mcpClientManager;
39
+ }
40
+ /**
41
+ * Check if the MCP client registry is initialized.
42
+ */
43
+ function isMcpClientInitialized() {
44
+ return mcpClientManager !== null;
45
+ }
46
+ /**
47
+ * Reset the MCP client registry (for testing only).
48
+ * @internal
49
+ */
50
+ function resetMcpClientRegistry() {
51
+ mcpClientManager = null;
52
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * MCP Client Types for dot-ai MCP Server Integration
3
+ *
4
+ * Defines the interface contract for connecting to external MCP servers.
5
+ * MCP servers provide additional tools (e.g., Prometheus metrics, tracing)
6
+ * that augment dot-ai's built-in kubectl/helm tools.
7
+ *
8
+ * PRD #358: MCP Server Integration
9
+ */
10
+ /**
11
+ * Valid dot-ai operations that MCP servers can attach to
12
+ */
13
+ export type McpAttachableOperation = 'remediate' | 'operate' | 'query';
14
+ /**
15
+ * Configuration for an MCP server connection.
16
+ * Loaded from /etc/dot-ai-mcp/mcp-servers.json (mounted via Helm ConfigMap).
17
+ */
18
+ export interface McpServerConfig {
19
+ /** Unique server name (e.g., "prometheus", "jaeger") */
20
+ name: string;
21
+ /** MCP server HTTP endpoint URL (e.g., "http://prometheus-mcp.monitoring.svc:3000") */
22
+ endpoint: string;
23
+ /** Which dot-ai operations this server's tools should be available to */
24
+ attachTo: McpAttachableOperation[];
25
+ /** Optional timeout in milliseconds for MCP requests (default: 30000) */
26
+ timeout?: number;
27
+ }
28
+ /**
29
+ * Tool definition discovered from an MCP server via client.listTools()
30
+ */
31
+ export interface McpToolDefinition {
32
+ /** Original tool name from MCP server */
33
+ name: string;
34
+ /** Tool description */
35
+ description?: string;
36
+ /** JSON Schema for tool input */
37
+ inputSchema: {
38
+ type: string;
39
+ properties?: Record<string, unknown>;
40
+ required?: string[];
41
+ };
42
+ }
43
+ /**
44
+ * MCP server metadata after successful discovery
45
+ */
46
+ export interface DiscoveredMcpServer {
47
+ /** Server name from config */
48
+ name: string;
49
+ /** Server endpoint URL */
50
+ endpoint: string;
51
+ /** Which operations this server is attached to */
52
+ attachTo: McpAttachableOperation[];
53
+ /** Server version from MCP protocol (if available) */
54
+ version?: string;
55
+ /** Tools provided by this server */
56
+ tools: McpToolDefinition[];
57
+ /** Discovery timestamp */
58
+ discoveredAt: Date;
59
+ }
60
+ /**
61
+ * Stats about MCP server connections
62
+ */
63
+ export interface McpServerStats {
64
+ /** Number of connected servers */
65
+ serverCount: number;
66
+ /** Total number of discovered tools across all servers */
67
+ toolCount: number;
68
+ /** Names of connected servers */
69
+ servers: string[];
70
+ }
71
+ /**
72
+ * Error thrown when MCP server discovery fails
73
+ */
74
+ export declare class McpDiscoveryError extends Error {
75
+ readonly failedServers: Array<{
76
+ name: string;
77
+ error: string;
78
+ }>;
79
+ constructor(message: string, failedServers: Array<{
80
+ name: string;
81
+ error: string;
82
+ }>);
83
+ }
84
+ //# sourceMappingURL=mcp-client-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-types.d.ts","sourceRoot":"","sources":["../../src/core/mcp-client-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvE;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,0BAA0B;IAC1B,YAAY,EAAE,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAGxB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;gBADrE,OAAO,EAAE,MAAM,EACC,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAKxE"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Client Types for dot-ai MCP Server Integration
4
+ *
5
+ * Defines the interface contract for connecting to external MCP servers.
6
+ * MCP servers provide additional tools (e.g., Prometheus metrics, tracing)
7
+ * that augment dot-ai's built-in kubectl/helm tools.
8
+ *
9
+ * PRD #358: MCP Server Integration
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.McpDiscoveryError = void 0;
13
+ /**
14
+ * Error thrown when MCP server discovery fails
15
+ */
16
+ class McpDiscoveryError extends Error {
17
+ failedServers;
18
+ constructor(message, failedServers) {
19
+ super(message);
20
+ this.failedServers = failedServers;
21
+ this.name = 'McpDiscoveryError';
22
+ }
23
+ }
24
+ exports.McpDiscoveryError = McpDiscoveryError;