@tyvm/knowhow 0.0.56 → 0.0.59

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 (190) hide show
  1. package/package.json +3 -3
  2. package/src/agents/base/base.ts +87 -43
  3. package/src/agents/tools/execCommand.ts +17 -14
  4. package/src/agents/tools/googleSearch.ts +1 -0
  5. package/src/agents/tools/index.ts +1 -0
  6. package/src/agents/tools/lazy/definitions.ts +63 -0
  7. package/src/agents/tools/lazy/disableTools.ts +16 -0
  8. package/src/agents/tools/lazy/enableTools.ts +16 -0
  9. package/src/agents/tools/lazy/index.ts +3 -0
  10. package/src/agents/tools/lazy/listAvailableTools.ts +14 -0
  11. package/src/agents/tools/list.ts +2 -0
  12. package/src/agents/tools/mcp/connectMcpServer.ts +40 -0
  13. package/src/agents/tools/mcp/definitions.ts +67 -0
  14. package/src/agents/tools/mcp/disconnectMcpServer.ts +40 -0
  15. package/src/agents/tools/mcp/index.ts +3 -0
  16. package/src/agents/tools/mcp/listAvailableMcpServers.ts +28 -0
  17. package/src/agents/tools/writeFile.ts +4 -1
  18. package/src/chat/CliChatService.ts +8 -3
  19. package/src/chat/modules/AgentModule.ts +74 -296
  20. package/src/cli.ts +33 -10
  21. package/src/plugins/GitPlugin.ts +30 -24
  22. package/src/plugins/language.ts +95 -18
  23. package/src/processors/ToolResponseCache.ts +98 -79
  24. package/src/processors/tools/grepToolResponse.ts +99 -0
  25. package/src/processors/tools/index.ts +21 -0
  26. package/src/processors/tools/jqToolResponse.ts +124 -0
  27. package/src/processors/tools/listStoredToolResponses.ts +83 -0
  28. package/src/processors/tools/tailToolResponse.ts +75 -0
  29. package/src/services/AgentService.ts +1 -1
  30. package/src/services/AgentSynchronization.ts +291 -0
  31. package/src/services/DockerService.ts +37 -1
  32. package/src/services/EventService.ts +8 -2
  33. package/src/services/KnowhowClient.ts +141 -1
  34. package/src/services/LazyToolsService.ts +146 -0
  35. package/src/services/Mcp.ts +171 -4
  36. package/src/services/SessionManager.ts +287 -0
  37. package/src/services/TaskRegistry.ts +108 -0
  38. package/src/services/Tools.ts +2 -0
  39. package/src/services/index.ts +7 -0
  40. package/src/services/script-execution/ScriptExecutor.ts +7 -5
  41. package/src/types.ts +1 -0
  42. package/src/utils/InputQueueManager.ts +91 -57
  43. package/src/utils/errors.ts +0 -0
  44. package/src/utils/index.ts +11 -0
  45. package/src/worker.ts +12 -0
  46. package/tests/compressor/bigstring.test.ts +100 -0
  47. package/tests/compressor/bigstring.txt +1 -0
  48. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +13 -5
  49. package/tests/plugins/language/languagePlugin-integration.test.ts +22 -7
  50. package/tests/plugins/language/languagePlugin.test.ts +11 -4
  51. package/tests/processors/ToolResponseCache.test.ts +128 -0
  52. package/tests/unit/InputQueueManager.test.ts +174 -0
  53. package/ts_build/package.json +3 -3
  54. package/ts_build/src/agents/base/base.d.ts +10 -0
  55. package/ts_build/src/agents/base/base.js +66 -34
  56. package/ts_build/src/agents/base/base.js.map +1 -1
  57. package/ts_build/src/agents/tools/execCommand.js +1 -9
  58. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  59. package/ts_build/src/agents/tools/github/index.d.ts +1 -1
  60. package/ts_build/src/agents/tools/googleSearch.d.ts +1 -0
  61. package/ts_build/src/agents/tools/googleSearch.js +1 -0
  62. package/ts_build/src/agents/tools/googleSearch.js.map +1 -1
  63. package/ts_build/src/agents/tools/index.d.ts +1 -0
  64. package/ts_build/src/agents/tools/index.js +1 -0
  65. package/ts_build/src/agents/tools/index.js.map +1 -1
  66. package/ts_build/src/agents/tools/lazy/definitions.d.ts +5 -0
  67. package/ts_build/src/agents/tools/lazy/definitions.js +58 -0
  68. package/ts_build/src/agents/tools/lazy/definitions.js.map +1 -0
  69. package/ts_build/src/agents/tools/lazy/disableTools.d.ts +9 -0
  70. package/ts_build/src/agents/tools/lazy/disableTools.js +15 -0
  71. package/ts_build/src/agents/tools/lazy/disableTools.js.map +1 -0
  72. package/ts_build/src/agents/tools/lazy/enableTools.d.ts +9 -0
  73. package/ts_build/src/agents/tools/lazy/enableTools.js +15 -0
  74. package/ts_build/src/agents/tools/lazy/enableTools.js.map +1 -0
  75. package/ts_build/src/agents/tools/lazy/index.d.ts +3 -0
  76. package/ts_build/src/agents/tools/lazy/index.js +20 -0
  77. package/ts_build/src/agents/tools/lazy/index.js.map +1 -0
  78. package/ts_build/src/agents/tools/lazy/listAvailableTools.d.ts +11 -0
  79. package/ts_build/src/agents/tools/lazy/listAvailableTools.js +15 -0
  80. package/ts_build/src/agents/tools/lazy/listAvailableTools.js.map +1 -0
  81. package/ts_build/src/agents/tools/list.js +2 -0
  82. package/ts_build/src/agents/tools/list.js.map +1 -1
  83. package/ts_build/src/agents/tools/mcp/connectMcpServer.d.ts +5 -0
  84. package/ts_build/src/agents/tools/mcp/connectMcpServer.js +31 -0
  85. package/ts_build/src/agents/tools/mcp/connectMcpServer.js.map +1 -0
  86. package/ts_build/src/agents/tools/mcp/definitions.d.ts +2 -0
  87. package/ts_build/src/agents/tools/mcp/definitions.js +62 -0
  88. package/ts_build/src/agents/tools/mcp/definitions.js.map +1 -0
  89. package/ts_build/src/agents/tools/mcp/disconnectMcpServer.d.ts +5 -0
  90. package/ts_build/src/agents/tools/mcp/disconnectMcpServer.js +31 -0
  91. package/ts_build/src/agents/tools/mcp/disconnectMcpServer.js.map +1 -0
  92. package/ts_build/src/agents/tools/mcp/index.d.ts +3 -0
  93. package/ts_build/src/agents/tools/mcp/index.js +10 -0
  94. package/ts_build/src/agents/tools/mcp/index.js.map +1 -0
  95. package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.d.ts +14 -0
  96. package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.js +23 -0
  97. package/ts_build/src/agents/tools/mcp/listAvailableMcpServers.js.map +1 -0
  98. package/ts_build/src/agents/tools/writeFile.js +4 -1
  99. package/ts_build/src/agents/tools/writeFile.js.map +1 -1
  100. package/ts_build/src/chat/CliChatService.js +3 -1
  101. package/ts_build/src/chat/CliChatService.js.map +1 -1
  102. package/ts_build/src/chat/modules/AgentModule.d.ts +4 -3
  103. package/ts_build/src/chat/modules/AgentModule.js +71 -265
  104. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  105. package/ts_build/src/cli.d.ts +1 -1
  106. package/ts_build/src/cli.js +17 -4
  107. package/ts_build/src/cli.js.map +1 -1
  108. package/ts_build/src/plugins/GitPlugin.d.ts +1 -0
  109. package/ts_build/src/plugins/GitPlugin.js +26 -19
  110. package/ts_build/src/plugins/GitPlugin.js.map +1 -1
  111. package/ts_build/src/plugins/language.d.ts +3 -0
  112. package/ts_build/src/plugins/language.js +55 -13
  113. package/ts_build/src/plugins/language.js.map +1 -1
  114. package/ts_build/src/processors/ToolResponseCache.d.ts +7 -4
  115. package/ts_build/src/processors/ToolResponseCache.js +47 -88
  116. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  117. package/ts_build/src/processors/tools/grepToolResponse.d.ts +10 -0
  118. package/ts_build/src/processors/tools/grepToolResponse.js +71 -0
  119. package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -0
  120. package/ts_build/src/processors/tools/index.d.ts +4 -0
  121. package/ts_build/src/processors/tools/index.js +16 -0
  122. package/ts_build/src/processors/tools/index.js.map +1 -0
  123. package/ts_build/src/processors/tools/jqToolResponse.d.ts +3 -0
  124. package/ts_build/src/processors/tools/jqToolResponse.js +115 -0
  125. package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -0
  126. package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +21 -0
  127. package/ts_build/src/processors/tools/listStoredToolResponses.js +51 -0
  128. package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -0
  129. package/ts_build/src/processors/tools/tailToolResponse.d.ts +6 -0
  130. package/ts_build/src/processors/tools/tailToolResponse.js +55 -0
  131. package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -0
  132. package/ts_build/src/services/AgentService.d.ts +1 -1
  133. package/ts_build/src/services/AgentSynchronization.d.ts +27 -0
  134. package/ts_build/src/services/AgentSynchronization.js +168 -0
  135. package/ts_build/src/services/AgentSynchronization.js.map +1 -0
  136. package/ts_build/src/services/DockerService.d.ts +2 -0
  137. package/ts_build/src/services/DockerService.js +21 -1
  138. package/ts_build/src/services/DockerService.js.map +1 -1
  139. package/ts_build/src/services/EventService.d.ts +5 -0
  140. package/ts_build/src/services/EventService.js +7 -2
  141. package/ts_build/src/services/EventService.js.map +1 -1
  142. package/ts_build/src/services/KnowhowClient.d.ts +41 -1
  143. package/ts_build/src/services/KnowhowClient.js +42 -0
  144. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  145. package/ts_build/src/services/LazyToolsService.d.ts +29 -0
  146. package/ts_build/src/services/LazyToolsService.js +96 -0
  147. package/ts_build/src/services/LazyToolsService.js.map +1 -0
  148. package/ts_build/src/services/Mcp.d.ts +18 -1
  149. package/ts_build/src/services/Mcp.js +119 -4
  150. package/ts_build/src/services/Mcp.js.map +1 -1
  151. package/ts_build/src/services/SessionManager.d.ts +15 -0
  152. package/ts_build/src/services/SessionManager.js +220 -0
  153. package/ts_build/src/services/SessionManager.js.map +1 -0
  154. package/ts_build/src/services/TaskRegistry.d.ts +15 -0
  155. package/ts_build/src/services/TaskRegistry.js +58 -0
  156. package/ts_build/src/services/TaskRegistry.js.map +1 -0
  157. package/ts_build/src/services/Tools.d.ts +2 -0
  158. package/ts_build/src/services/Tools.js.map +1 -1
  159. package/ts_build/src/services/index.d.ts +4 -0
  160. package/ts_build/src/services/index.js +4 -0
  161. package/ts_build/src/services/index.js.map +1 -1
  162. package/ts_build/src/services/script-execution/ScriptExecutor.js +7 -5
  163. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -1
  164. package/ts_build/src/types.d.ts +1 -0
  165. package/ts_build/src/types.js.map +1 -1
  166. package/ts_build/src/utils/InputQueueManager.d.ts +9 -2
  167. package/ts_build/src/utils/InputQueueManager.js +54 -40
  168. package/ts_build/src/utils/InputQueueManager.js.map +1 -1
  169. package/ts_build/src/utils/errors.d.ts +0 -0
  170. package/ts_build/src/utils/errors.js +1 -0
  171. package/ts_build/src/utils/errors.js.map +1 -0
  172. package/ts_build/src/utils/index.d.ts +1 -0
  173. package/ts_build/src/utils/index.js +5 -1
  174. package/ts_build/src/utils/index.js.map +1 -1
  175. package/ts_build/src/worker.js +8 -0
  176. package/ts_build/src/worker.js.map +1 -1
  177. package/ts_build/tests/compressor/bigstring.test.d.ts +1 -0
  178. package/ts_build/tests/compressor/bigstring.test.js +66 -0
  179. package/ts_build/tests/compressor/bigstring.test.js.map +1 -0
  180. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +6 -5
  181. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
  182. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +9 -7
  183. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
  184. package/ts_build/tests/plugins/language/languagePlugin.test.js +7 -4
  185. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
  186. package/ts_build/tests/processors/ToolResponseCache.test.js +107 -0
  187. package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
  188. package/ts_build/tests/unit/InputQueueManager.test.d.ts +1 -0
  189. package/ts_build/tests/unit/InputQueueManager.test.js +104 -0
  190. package/ts_build/tests/unit/InputQueueManager.test.js.map +1 -0
@@ -3,6 +3,7 @@ import * as path from "path";
3
3
  import * as os from "os";
4
4
  import { Config } from "../types";
5
5
  import { updateConfig } from "../config";
6
+ import * as crypto from "crypto";
6
7
 
7
8
  export interface DockerWorkerOptions {
8
9
  workspaceDir: string;
@@ -234,15 +235,50 @@ export class DockerService {
234
235
  return { valid, errors };
235
236
  }
236
237
 
238
+ /**
239
+ * Generate a short hash from the workspace directory path
240
+ * This ensures one container per workspace directory
241
+ */
242
+ private getWorkspaceHash(workspaceDir: string): string {
243
+ const hash = crypto.createHash("md5").update(workspaceDir).digest("hex");
244
+ // Return first 8 characters for a short, readable hash
245
+ return hash.substring(0, 8);
246
+ }
247
+
248
+ /**
249
+ * Stop and remove a container by name if it exists
250
+ */
251
+ private async removeContainerByName(containerName: string): Promise<void> {
252
+ try {
253
+ // Check if container exists
254
+ const { stdout } = await execAsync(
255
+ `docker ps -a --filter "name=^/${containerName}$" --format "{{.ID}}"`
256
+ );
257
+ const containerId = stdout.trim();
258
+
259
+ if (containerId) {
260
+ console.log(`🗑️ Removing existing container: ${containerName}`);
261
+ await execAsync(`docker stop ${containerId}`);
262
+ await execAsync(`docker rm ${containerId}`);
263
+ }
264
+ } catch (error) {
265
+ // Ignore errors if container doesn't exist
266
+ }
267
+ }
268
+
237
269
  /**
238
270
  * Run the knowhow worker in a Docker container
239
271
  */
240
272
  async runWorkerContainer(options: DockerWorkerOptions): Promise<string> {
241
- const containerName = `${DockerService.CONTAINER_PREFIX}-${Date.now()}`;
273
+ const workspaceHash = this.getWorkspaceHash(options.workspaceDir);
274
+ const containerName = `${DockerService.CONTAINER_PREFIX}-${workspaceHash}`;
242
275
  const homedir = os.homedir();
243
276
  const relativeWorkspace = options.workspaceDir.replace(homedir, "~");
244
277
  const knowhowDir = path.join(homedir, ".knowhow");
245
278
  const relativeKnowhowDir = "~/.knowhow";
279
+ // Remove any existing container for this workspace
280
+ await this.removeContainerByName(containerName);
281
+
246
282
 
247
283
  let config = options.config;
248
284
 
@@ -8,6 +8,12 @@ export interface EventHandler {
8
8
  export class EventService extends EventEmitter {
9
9
  private blockingHandlers: Map<string, EventHandler[]> = new Map();
10
10
 
11
+ eventTypes = {
12
+ agentMsg: "agent:msg",
13
+ agentsRegister: "agents:register",
14
+ agentsCall: "agents:call",
15
+ };
16
+
11
17
  constructor() {
12
18
  super();
13
19
  }
@@ -87,12 +93,12 @@ export class EventService extends EventEmitter {
87
93
  }
88
94
 
89
95
  registerAgent(agent: IAgent): void {
90
- this.emit("agents:register", { name: agent.name, agent });
96
+ this.emit(this.eventTypes.agentsRegister, { name: agent.name, agent });
91
97
  }
92
98
 
93
99
  callAgent(name: string, query: string): Promise<string> {
94
100
  return new Promise((resolve, reject) => {
95
- this.emit("agents:call", { name, query, resolve, reject });
101
+ this.emit(this.eventTypes.agentsCall, { name, query, resolve, reject });
96
102
  });
97
103
  }
98
104
  }
@@ -7,7 +7,7 @@ import {
7
7
  CompletionResponse,
8
8
  EmbeddingOptions,
9
9
  EmbeddingResponse,
10
- } from "src/clients";
10
+ } from "../clients";
11
11
  import { Config } from "../types";
12
12
 
13
13
  // Chat Task interfaces
@@ -33,6 +33,46 @@ export interface UpdateOrgTaskResponse {
33
33
  inProgress: boolean;
34
34
  }
35
35
 
36
+ // Agent task synchronization interfaces
37
+ export interface TaskDetailsResponse {
38
+ taskId: string;
39
+ inProgress: boolean;
40
+ status: "running" | "paused" | "killed" | "completed";
41
+ totalUsdCost: number;
42
+ threads: any;
43
+ createdAt: string;
44
+ updatedAt: string;
45
+ messageId?: string;
46
+ hasPendingMessages: boolean;
47
+ pendingMessagesCount: number;
48
+ }
49
+
50
+ export interface PendingMessage {
51
+ id: string;
52
+ message: string;
53
+ role: string;
54
+ createdAt: Date;
55
+ processedAt: Date | null;
56
+ }
57
+
58
+ export interface SendMessageRequest {
59
+ message: string;
60
+ role?: "user" | "system";
61
+ }
62
+
63
+ export interface SendMessageResponse {
64
+ id: string;
65
+ message: string;
66
+ }
67
+
68
+ export interface StatusResponse {
69
+ status: string;
70
+ }
71
+
72
+ export interface MarkProcessedResponse {
73
+ processedCount: number;
74
+ }
75
+
36
76
  export function loadKnowhowJwt(): string {
37
77
  const jwtFile = path.join(process.cwd(), ".knowhow", ".jwt");
38
78
  if (!fs.existsSync(jwtFile)) {
@@ -183,4 +223,104 @@ export class KnowhowSimpleClient {
183
223
  }
184
224
  );
185
225
  }
226
+
227
+ // ============================================
228
+ // Agent Task Synchronization Methods
229
+ // ============================================
230
+
231
+ /**
232
+ * Get task details including status, threads, and pending message info
233
+ */
234
+ async getTaskDetails(taskId: string) {
235
+ await this.checkJwt();
236
+ return axios.get<TaskDetailsResponse>(
237
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}`,
238
+ {
239
+ headers: this.headers,
240
+ }
241
+ );
242
+ }
243
+
244
+ /**
245
+ * Get pending messages for an agent task
246
+ */
247
+ async getPendingMessages(taskId: string) {
248
+ await this.checkJwt();
249
+ return axios.get<PendingMessage[]>(
250
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}/pending-messages`,
251
+ {
252
+ headers: this.headers,
253
+ }
254
+ );
255
+ }
256
+
257
+ /**
258
+ * Mark pending messages as processed
259
+ */
260
+ async markMessagesAsProcessed(taskId: string, messageIds: string[]) {
261
+ await this.checkJwt();
262
+ return axios.post<MarkProcessedResponse>(
263
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}/pending-messages/mark-processed`,
264
+ { messageIds },
265
+ {
266
+ headers: this.headers,
267
+ }
268
+ );
269
+ }
270
+
271
+ /**
272
+ * Send a message to a running agent task
273
+ */
274
+ async sendMessageToAgent(taskId: string, message: string, role: "user" | "system" = "user") {
275
+ await this.checkJwt();
276
+ return axios.post<SendMessageResponse>(
277
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}/messages`,
278
+ { message, role },
279
+ {
280
+ headers: this.headers,
281
+ }
282
+ );
283
+ }
284
+
285
+ /**
286
+ * Pause a running agent task
287
+ */
288
+ async pauseAgent(taskId: string) {
289
+ await this.checkJwt();
290
+ return axios.post<StatusResponse>(
291
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}/pause`,
292
+ {},
293
+ {
294
+ headers: this.headers,
295
+ }
296
+ );
297
+ }
298
+
299
+ /**
300
+ * Resume a paused agent task
301
+ */
302
+ async resumeAgent(taskId: string) {
303
+ await this.checkJwt();
304
+ return axios.post<StatusResponse>(
305
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}/resume`,
306
+ {},
307
+ {
308
+ headers: this.headers,
309
+ }
310
+ );
311
+ }
312
+
313
+ /**
314
+ * Kill/cancel a running or paused agent task
315
+ */
316
+ async killAgent(taskId: string) {
317
+ await this.checkJwt();
318
+ return axios.post<StatusResponse>(
319
+ `${this.baseUrl}/api/org-agent-tasks/${taskId}/kill`,
320
+ {},
321
+ {
322
+ headers: this.headers,
323
+ }
324
+ );
325
+ }
186
326
  }
@@ -0,0 +1,146 @@
1
+ import { minimatch } from "minimatch";
2
+ import { ToolsService, ToolContext } from "./Tools";
3
+ import { Tool } from "../clients/types";
4
+ import {
5
+ listAvailableTools,
6
+ enableTools,
7
+ disableTools,
8
+ } from "../agents/tools/lazy";
9
+ import { definitions } from "../agents/tools/lazy/definitions";
10
+
11
+ export class LazyToolsService extends ToolsService {
12
+ private allTools: Tool[] = [];
13
+ // Start with lazy tools enabled by default
14
+ private enabledPatterns: string[] = [
15
+ "listAvailableTools",
16
+ "enableTools",
17
+ "disableTools",
18
+ "finalAnswer",
19
+ ];
20
+ private disabledPatterns: string[] = [];
21
+
22
+ constructor(context?: ToolContext) {
23
+ super(context);
24
+ this.registerLazyTools();
25
+ }
26
+
27
+ /**
28
+ * Registers lazy tool management tools
29
+ * These tools are only available when using LazyToolsService
30
+ */
31
+ private registerLazyTools(): void {
32
+ // Add to allTools so they're always available
33
+ this.allTools.push(...definitions);
34
+
35
+ // Add to visible tools so they're immediately accessible
36
+ super.addTools(definitions);
37
+
38
+ // Register the function implementations
39
+ this.addFunctions({
40
+ listAvailableTools: listAvailableTools.bind(this),
41
+ enableTools: enableTools.bind(this),
42
+ disableTools: disableTools.bind(this),
43
+ });
44
+ }
45
+
46
+ // Override addTools to store all tools but not expose them yet
47
+ addTools(tools: Tool[]) {
48
+ // Store tools in allTools instead of this.tools
49
+ const existingNames = this.allTools.map((t) => t.function.name);
50
+ const newTools = tools.filter(
51
+ (t) => !existingNames.includes(t.function.name)
52
+ );
53
+ this.allTools.push(...newTools);
54
+
55
+ // Update visible tools based on patterns
56
+ this.updateVisibleTools();
57
+ }
58
+
59
+ // Override getTools to return only enabled tools
60
+ getTools() {
61
+ return this.tools; // Returns filtered subset
62
+ }
63
+
64
+ // Enable tools matching glob patterns
65
+ enableTools(patterns: string | string[]) {
66
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
67
+
68
+ for (const pattern of patternArray) {
69
+ if (!this.enabledPatterns.includes(pattern)) {
70
+ this.enabledPatterns.push(pattern);
71
+ }
72
+ }
73
+
74
+ this.updateVisibleTools();
75
+
76
+ return {
77
+ enabled: this.tools.length,
78
+ total: this.allTools.length,
79
+ patterns: this.enabledPatterns,
80
+ };
81
+ }
82
+
83
+ // Disable tools matching glob patterns
84
+ disableTools(patterns: string | string[]) {
85
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
86
+
87
+ for (const pattern of patternArray) {
88
+ if (!this.disabledPatterns.includes(pattern)) {
89
+ this.disabledPatterns.push(pattern);
90
+ }
91
+ }
92
+
93
+ this.updateVisibleTools();
94
+
95
+ return {
96
+ enabled: this.tools.length,
97
+ total: this.allTools.length,
98
+ patterns: this.disabledPatterns,
99
+ };
100
+ }
101
+
102
+ // List all available tools (not just enabled ones)
103
+ listAvailableTools() {
104
+ return {
105
+ enabled: this.tools.map((t) => t.function.name),
106
+ disabled: this.allTools
107
+ .filter(
108
+ (t) => !this.tools.find((et) => et.function.name === t.function.name)
109
+ )
110
+ .map((t) => t.function.name),
111
+ total: this.allTools.length,
112
+ enabledCount: this.tools.length,
113
+ disabledCount: this.allTools.length - this.tools.length,
114
+ };
115
+ }
116
+
117
+ // Internal: Update visible tools based on patterns
118
+ private updateVisibleTools() {
119
+ const enabledTools: Tool[] = [];
120
+
121
+ for (const tool of this.allTools) {
122
+ const name = tool.function.name;
123
+
124
+ // Check if disabled by any pattern
125
+ const isDisabled = this.disabledPatterns.some((pattern) =>
126
+ minimatch(name, pattern)
127
+ );
128
+
129
+ if (isDisabled) {
130
+ continue;
131
+ }
132
+
133
+ // Check if enabled by any pattern (requires explicit enabling)
134
+ const isEnabled =
135
+ this.enabledPatterns.length > 0 &&
136
+ this.enabledPatterns.some((pattern) => minimatch(name, pattern));
137
+
138
+ if (isEnabled) {
139
+ enabledTools.push(tool);
140
+ }
141
+ }
142
+
143
+ // Update the visible tools array
144
+ this.tools = enabledTools;
145
+ }
146
+ }
@@ -122,13 +122,175 @@ export class McpService {
122
122
 
123
123
  async connectTo(mcpServers: McpConfig[] = [], tools?: ToolsService) {
124
124
  const clients = await this.createStdioClients(mcpServers);
125
- await this.connectAll();
125
+ await this.connectAutoServers();
126
126
 
127
127
  if (tools) {
128
128
  await this.addTools(tools);
129
129
  }
130
130
  }
131
131
 
132
+ // Connect only servers with autoConnect !== false
133
+ async connectAutoServers() {
134
+ await Promise.all(
135
+ this.clients.map(async (client, index) => {
136
+ const config = this.config[index];
137
+ const shouldAutoConnect = config.autoConnect !== false;
138
+
139
+ if (shouldAutoConnect && !this.connected[index]) {
140
+ console.log(`Connecting to MCP server: ${config.name}`);
141
+ await client.connect(this.transports[index]);
142
+ this.connected[index] = true;
143
+ } else if (!shouldAutoConnect) {
144
+ console.log(
145
+ `Skipping auto-connect for MCP server: ${config.name} (autoConnect: false)`
146
+ );
147
+ }
148
+ })
149
+ );
150
+ }
151
+
152
+ // Connect to a specific MCP server by name
153
+ async connectSingle(
154
+ serverName: string,
155
+ timeout: number = 30000
156
+ ): Promise<{
157
+ success: boolean;
158
+ toolsAdded: string[];
159
+ error?: string;
160
+ }> {
161
+ const index = this.getClientIndex(serverName);
162
+
163
+ if (index < 0) {
164
+ return {
165
+ success: false,
166
+ toolsAdded: [],
167
+ error: `MCP server '${serverName}' not found in configuration`,
168
+ };
169
+ }
170
+
171
+ if (this.connected[index]) {
172
+ return {
173
+ success: true,
174
+ toolsAdded: [],
175
+ error: `MCP server '${serverName}' already connected`,
176
+ };
177
+ }
178
+
179
+ try {
180
+ const client = this.clients[index];
181
+ const transport = this.transports[index];
182
+
183
+ // Connect with timeout
184
+ await Promise.race([
185
+ client.connect(transport),
186
+ new Promise((_, reject) =>
187
+ setTimeout(() => reject(new Error("Connection timeout")), timeout)
188
+ ),
189
+ ]);
190
+
191
+ this.connected[index] = true;
192
+
193
+ // Get tools from this server
194
+ const clientTools = await client.listTools();
195
+ const toolNames: string[] = [];
196
+
197
+ for (const tool of clientTools.tools) {
198
+ const transformed = this.toOpenAiTool(index, tool as any);
199
+ if (transformed.function.name !== tool.name) {
200
+ this.toolAliases[transformed.function.name] = tool.name;
201
+ }
202
+ toolNames.push(transformed.function.name);
203
+
204
+ // Add to cache if not already present
205
+ if (
206
+ !this.tools.find((t) => t.function.name === transformed.function.name)
207
+ ) {
208
+ this.tools.push(transformed);
209
+ }
210
+ }
211
+
212
+ return {
213
+ success: true,
214
+ toolsAdded: toolNames,
215
+ };
216
+ } catch (error) {
217
+ return {
218
+ success: false,
219
+ toolsAdded: [],
220
+ error: error.message,
221
+ };
222
+ }
223
+ }
224
+
225
+ // Disconnect a specific MCP server
226
+ async disconnectSingle(serverName: string): Promise<{
227
+ success: boolean;
228
+ toolsRemoved: string[];
229
+ error?: string;
230
+ }> {
231
+ const index = this.getClientIndex(serverName);
232
+
233
+ if (index < 0) {
234
+ return {
235
+ success: false,
236
+ toolsRemoved: [],
237
+ error: `MCP server '${serverName}' not found`,
238
+ };
239
+ }
240
+
241
+ if (!this.connected[index]) {
242
+ return {
243
+ success: true,
244
+ toolsRemoved: [],
245
+ error: `MCP server '${serverName}' not connected`,
246
+ };
247
+ }
248
+
249
+ try {
250
+ const toolsToRemove = this.tools
251
+ .filter((t) => this.getToolClientIndex(t.function.name) === index)
252
+ .map((t) => t.function.name);
253
+
254
+ // Close connection
255
+ await this.transports[index]?.close();
256
+ this.connected[index] = false;
257
+
258
+ // Remove tools from cache
259
+ this.tools = this.tools.filter(
260
+ (t) => this.getToolClientIndex(t.function.name) !== index
261
+ );
262
+
263
+ return {
264
+ success: true,
265
+ toolsRemoved: toolsToRemove,
266
+ };
267
+ } catch (error) {
268
+ return {
269
+ success: false,
270
+ toolsRemoved: [],
271
+ error: error.message,
272
+ };
273
+ }
274
+ }
275
+
276
+ // Get available servers (connected and disconnected)
277
+ getAvailableServers() {
278
+ return this.config.map((config, index) => {
279
+ const toolCount = this.connected[index]
280
+ ? this.tools.filter(
281
+ (t) => this.getToolClientIndex(t.function.name) === index
282
+ ).length
283
+ : 0;
284
+
285
+ return {
286
+ name: config.name,
287
+ connected: this.connected[index] || false,
288
+ autoConnect: config.autoConnect !== false,
289
+ toolCount,
290
+ };
291
+ });
292
+ }
293
+
132
294
  async addTools(tools: ToolsService) {
133
295
  tools.addTools(await this.getTools());
134
296
  tools.addFunctions(await this.getToolMap());
@@ -203,7 +365,7 @@ export class McpService {
203
365
  return this.clients[index];
204
366
  }
205
367
 
206
- getFunction(toolName: string) {
368
+ getFunction(toolName: string, timeout?: number) {
207
369
  const client = this.getToolClient(toolName);
208
370
 
209
371
  // Handle unwrapped tool names if we have 1 client
@@ -227,8 +389,8 @@ export class McpService {
227
389
  },
228
390
  CallToolResultSchema,
229
391
  {
230
- timeout: 10 * 60 * 1000,
231
- maxTotalTimeout: 10 * 60 * 1000,
392
+ timeout: timeout || 10 * 60 * 1000,
393
+ maxTotalTimeout: timeout || 10 * 60 * 1000,
232
394
  }
233
395
  );
234
396
  return tool;
@@ -314,6 +476,11 @@ export class McpService {
314
476
  for (let i = 0; i < this.config.length; i++) {
315
477
  const config = this.config[i];
316
478
  const client = this.clients[i];
479
+
480
+ if (!this.connected[i]) {
481
+ // skip adding tools for unconnected clients
482
+ continue;
483
+ }
317
484
  const clientTools = await client.listTools();
318
485
 
319
486
  for (const tool of clientTools.tools) {