@aws/lsp-codewhisperer 0.0.66 → 0.0.68

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 (82) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +5 -0
  3. package/out/language-server/agenticChat/agenticChatController.d.ts +2 -2
  4. package/out/language-server/agenticChat/agenticChatController.js +109 -34
  5. package/out/language-server/agenticChat/agenticChatController.js.map +1 -1
  6. package/out/language-server/agenticChat/constants/constants.d.ts +5 -0
  7. package/out/language-server/agenticChat/constants/constants.js +7 -1
  8. package/out/language-server/agenticChat/constants/constants.js.map +1 -1
  9. package/out/language-server/agenticChat/context/additionalContextProvider.d.ts +9 -1
  10. package/out/language-server/agenticChat/context/additionalContextProvider.js +55 -32
  11. package/out/language-server/agenticChat/context/additionalContextProvider.js.map +1 -1
  12. package/out/language-server/agenticChat/context/agenticChatTriggerContext.js +4 -0
  13. package/out/language-server/agenticChat/context/agenticChatTriggerContext.js.map +1 -1
  14. package/out/language-server/agenticChat/qAgenticChatServer.d.ts +2 -0
  15. package/out/language-server/agenticChat/qAgenticChatServer.js +18 -1
  16. package/out/language-server/agenticChat/qAgenticChatServer.js.map +1 -1
  17. package/out/language-server/agenticChat/tabBarController.d.ts +1 -0
  18. package/out/language-server/agenticChat/tabBarController.js +7 -0
  19. package/out/language-server/agenticChat/tabBarController.js.map +1 -1
  20. package/out/language-server/agenticChat/tools/chatDb/chatDb.js +14 -2
  21. package/out/language-server/agenticChat/tools/chatDb/chatDb.js.map +1 -1
  22. package/out/language-server/agenticChat/tools/chatDb/util.d.ts +11 -1
  23. package/out/language-server/agenticChat/tools/chatDb/util.js +17 -0
  24. package/out/language-server/agenticChat/tools/chatDb/util.js.map +1 -1
  25. package/out/language-server/agenticChat/tools/executeBash.d.ts +0 -5
  26. package/out/language-server/agenticChat/tools/executeBash.js +11 -14
  27. package/out/language-server/agenticChat/tools/executeBash.js.map +1 -1
  28. package/out/language-server/agenticChat/tools/grepSearch.js +3 -1
  29. package/out/language-server/agenticChat/tools/grepSearch.js.map +1 -1
  30. package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.js +208 -170
  31. package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.js.map +1 -1
  32. package/out/language-server/agenticChat/tools/mcp/mcpManager.d.ts +12 -18
  33. package/out/language-server/agenticChat/tools/mcp/mcpManager.js +353 -216
  34. package/out/language-server/agenticChat/tools/mcp/mcpManager.js.map +1 -1
  35. package/out/language-server/agenticChat/tools/mcp/mcpTypes.d.ts +29 -0
  36. package/out/language-server/agenticChat/tools/mcp/mcpTypes.js +60 -1
  37. package/out/language-server/agenticChat/tools/mcp/mcpTypes.js.map +1 -1
  38. package/out/language-server/agenticChat/tools/mcp/mcpUtils.d.ts +27 -4
  39. package/out/language-server/agenticChat/tools/mcp/mcpUtils.js +464 -2
  40. package/out/language-server/agenticChat/tools/mcp/mcpUtils.js.map +1 -1
  41. package/out/language-server/agenticChat/tools/toolServer.js +8 -10
  42. package/out/language-server/agenticChat/tools/toolServer.js.map +1 -1
  43. package/out/language-server/agenticChat/tools/toolShared.js +6 -2
  44. package/out/language-server/agenticChat/tools/toolShared.js.map +1 -1
  45. package/out/language-server/chat/chatController.d.ts +1 -1
  46. package/out/language-server/chat/chatController.js.map +1 -1
  47. package/out/language-server/chat/contexts/triggerContext.js +11 -2
  48. package/out/language-server/chat/contexts/triggerContext.js.map +1 -1
  49. package/out/language-server/configuration/qConfigurationServer.d.ts +2 -0
  50. package/out/language-server/configuration/qConfigurationServer.js.map +1 -1
  51. package/out/language-server/inline-completion/auto-trigger/autoTrigger.d.ts +1 -0
  52. package/out/language-server/inline-completion/auto-trigger/autoTrigger.js +36 -0
  53. package/out/language-server/inline-completion/auto-trigger/autoTrigger.js.map +1 -1
  54. package/out/language-server/inline-completion/codeWhispererServer.js +25 -36
  55. package/out/language-server/inline-completion/codeWhispererServer.js.map +1 -1
  56. package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.d.ts +4 -1
  57. package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.js +36 -2
  58. package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.js.map +1 -1
  59. package/out/language-server/workspaceContext/dependency/dependencyEventBundler.d.ts +11 -5
  60. package/out/language-server/workspaceContext/dependency/dependencyEventBundler.js +37 -10
  61. package/out/language-server/workspaceContext/dependency/dependencyEventBundler.js.map +1 -1
  62. package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.d.ts +1 -1
  63. package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.js +8 -3
  64. package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.js.map +1 -1
  65. package/out/language-server/workspaceContext/dependency/dependencyHandler/JavaDependencyHandler.js +3 -4
  66. package/out/language-server/workspaceContext/dependency/dependencyHandler/JavaDependencyHandler.js.map +1 -1
  67. package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.d.ts +4 -3
  68. package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js +5 -9
  69. package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js.map +1 -1
  70. package/out/language-server/workspaceContext/dependency/dependencyHandler/PythonDependencyHandler.js +3 -4
  71. package/out/language-server/workspaceContext/dependency/dependencyHandler/PythonDependencyHandler.js.map +1 -1
  72. package/out/language-server/workspaceContext/workspaceContextServer.js +12 -8
  73. package/out/language-server/workspaceContext/workspaceContextServer.js.map +1 -1
  74. package/out/shared/amazonQServiceManager/configurationUtils.d.ts +4 -0
  75. package/out/shared/amazonQServiceManager/configurationUtils.js +6 -0
  76. package/out/shared/amazonQServiceManager/configurationUtils.js.map +1 -1
  77. package/out/shared/streamingClientService.js +12 -1
  78. package/out/shared/streamingClientService.js.map +1 -1
  79. package/out/shared/utils.d.ts +1 -7
  80. package/out/shared/utils.js +39 -36
  81. package/out/shared/utils.js.map +1 -1
  82. package/package.json +3 -4
@@ -6,15 +6,13 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.McpManager = exports.AGENT_TOOLS_CHANGED = exports.MCP_SERVER_STATUS_CHANGED = void 0;
8
8
  const types_1 = require("../../../../shared/telemetry/types");
9
- const mcpUtils_1 = require("./mcpUtils");
10
9
  const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
11
10
  const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
12
11
  const mcpTypes_1 = require("./mcpTypes");
13
- const mcpUtils_2 = require("./mcpUtils");
12
+ const mcpUtils_1 = require("./mcpUtils");
14
13
  const errors_1 = require("../../errors");
15
14
  const events_1 = require("events");
16
15
  const async_mutex_1 = require("async-mutex");
17
- const path = require("path");
18
16
  const vscode_uri_1 = require("vscode-uri");
19
17
  exports.MCP_SERVER_STATUS_CHANGED = 'mcpServerStatusChanged';
20
18
  exports.AGENT_TOOLS_CHANGED = 'agentToolsChanged';
@@ -22,8 +20,7 @@ exports.AGENT_TOOLS_CHANGED = 'agentToolsChanged';
22
20
  * Manages MCP servers and their tools
23
21
  */
24
22
  class McpManager {
25
- configPaths;
26
- personaPaths;
23
+ agentPaths;
27
24
  features;
28
25
  static #instance;
29
26
  clients;
@@ -37,9 +34,9 @@ class McpManager {
37
34
  static personaMutex = new async_mutex_1.Mutex();
38
35
  toolNameMapping;
39
36
  serverNameMapping;
40
- constructor(configPaths, personaPaths, features) {
41
- this.configPaths = configPaths;
42
- this.personaPaths = personaPaths;
37
+ agentConfig;
38
+ constructor(agentPaths, features) {
39
+ this.agentPaths = agentPaths;
43
40
  this.features = features;
44
41
  this.mcpTools = [];
45
42
  this.clients = new Map();
@@ -48,21 +45,21 @@ class McpManager {
48
45
  this.configLoadErrors = new Map();
49
46
  this.mcpServerPermissions = new Map();
50
47
  this.events = new events_1.EventEmitter({ captureRejections: true }).on('error', console.error);
51
- this.features.logging.info(`MCP manager: initialized with ${configPaths.length} configs`);
48
+ this.features.logging.info(`MCP manager: initialized with ${agentPaths.length} configs`);
52
49
  this.toolNameMapping = new Map();
53
50
  this.serverNameMapping = new Map();
54
51
  }
55
- static async init(configPaths, personaPaths, features) {
52
+ static async init(agentPaths, features) {
56
53
  if (!McpManager.#instance) {
57
- const mgr = new McpManager(configPaths, personaPaths, features);
54
+ const mgr = new McpManager(agentPaths, features);
58
55
  McpManager.#instance = mgr;
59
56
  await mgr.discoverAllServers();
60
57
  features.logging.info(`MCP: discovered ${mgr.mcpTools.length} tools across all servers`);
61
58
  // Emit MCP configuration metrics
62
59
  const serverConfigs = mgr.getAllServerConfigs();
63
- const activeServers = Array.from(serverConfigs.entries()).filter(([name, _]) => !mgr.isServerDisabled(name));
60
+ const activeServers = Array.from(serverConfigs.entries());
64
61
  // Count global vs project servers
65
- const globalServers = Array.from(serverConfigs.entries()).filter(([_, config]) => config?.__configPath__ === (0, mcpUtils_1.getGlobalMcpConfigPath)(features.workspace.fs.getUserHomeDir())).length;
62
+ const globalServers = Array.from(serverConfigs.entries()).filter(([_, config]) => config?.__configPath__ === (0, mcpUtils_1.getGlobalAgentConfigPath)(features.workspace.fs.getUserHomeDir())).length;
66
63
  const projectServers = serverConfigs.size - globalServers;
67
64
  // Count tools by permission
68
65
  let toolsAlwaysAllowed = 0;
@@ -118,30 +115,69 @@ class McpManager {
118
115
  * Load configurations and initialize each enabled server.
119
116
  */
120
117
  async discoverAllServers() {
121
- const permissionMap = await (0, mcpUtils_2.loadPersonaPermissions)(this.features.workspace, this.features.logging, this.personaPaths);
122
- this.mcpServerPermissions = permissionMap;
123
- const { servers, serverNameMapping, errors } = await (0, mcpUtils_2.loadMcpServerConfigs)(this.features.workspace, this.features.logging, this.configPaths);
124
- this.mcpServers = servers;
125
- this.serverNameMapping = serverNameMapping;
118
+ // Load agent config
119
+ const result = await (0, mcpUtils_1.loadAgentConfig)(this.features.workspace, this.features.logging, this.agentPaths);
120
+ // Extract agent config and other data
121
+ this.agentConfig = result.agentConfig;
122
+ this.mcpServers = result.servers;
123
+ this.serverNameMapping = result.serverNameMapping;
126
124
  // Reset the configuration errors after every refresh.
127
125
  this.configLoadErrors.clear();
128
126
  // Store any config load errors
129
- errors.forEach((errorMsg, key) => {
127
+ result.errors.forEach((errorMsg, key) => {
130
128
  this.configLoadErrors.set(key, errorMsg);
131
129
  });
132
- // Set all servers to UNINITIALIZED state initially
133
- for (const name of this.mcpServers.keys()) {
134
- this.setState(name, mcpTypes_1.McpServerStatus.UNINITIALIZED, 0);
130
+ this.features.logging.info('Using agent configuration');
131
+ // Reset permissions map
132
+ this.mcpServerPermissions.clear();
133
+ // Initialize permissions for servers from agent config
134
+ for (const [sanitizedName, _] of this.mcpServers.entries()) {
135
+ const name = this.serverNameMapping.get(sanitizedName) || sanitizedName;
136
+ // Set server status to UNINITIALIZED initially
137
+ this.setState(sanitizedName, mcpTypes_1.McpServerStatus.UNINITIALIZED, 0);
138
+ // Initialize permissions for this server
139
+ const serverPrefix = `@${name}`;
140
+ // Extract tool permissions from agent config
141
+ const toolPerms = {};
142
+ // Check if the server is enabled as a whole (@server) or just specific tools (@server/tool)
143
+ const isWholeServerEnabled = this.agentConfig.tools.includes(serverPrefix);
144
+ if (isWholeServerEnabled) {
145
+ // Check for specific tools in allowedTools
146
+ this.agentConfig.allowedTools.forEach(allowedTool => {
147
+ if (allowedTool.startsWith(serverPrefix + '/')) {
148
+ const toolName = allowedTool.substring(serverPrefix.length + 1);
149
+ if (toolName) {
150
+ // This specific tool is in allowedTools
151
+ toolPerms[toolName] = mcpTypes_1.McpPermissionType.alwaysAllow;
152
+ }
153
+ }
154
+ });
155
+ }
156
+ else {
157
+ // Only specific tools are enabled
158
+ this.agentConfig.tools.forEach(tool => {
159
+ if (tool.startsWith(serverPrefix + '/')) {
160
+ const toolName = tool.substring(serverPrefix.length + 1);
161
+ if (toolName) {
162
+ // Check if tool is in allowedTools
163
+ if (this.agentConfig.allowedTools.includes(tool)) {
164
+ toolPerms[toolName] = mcpTypes_1.McpPermissionType.alwaysAllow;
165
+ }
166
+ else {
167
+ toolPerms[toolName] = mcpTypes_1.McpPermissionType.ask;
168
+ }
169
+ }
170
+ }
171
+ });
172
+ }
173
+ this.mcpServerPermissions.set(sanitizedName, {
174
+ enabled: true,
175
+ toolPerms,
176
+ });
135
177
  }
136
178
  // Get all servers that need to be initialized
137
179
  const serversToInit = [];
138
180
  for (const [name, cfg] of this.mcpServers.entries()) {
139
- if (this.isServerDisabled(name)) {
140
- this.features.logging.info(`MCP: server '${name}' is disabled by persona settings, skipping`);
141
- this.setState(name, mcpTypes_1.McpServerStatus.DISABLED, 0);
142
- this.emitToolsChanged(name);
143
- continue;
144
- }
145
181
  serversToInit.push([name, cfg]);
146
182
  }
147
183
  // Process servers in batches of 5 at a time
@@ -171,7 +207,7 @@ class McpManager {
171
207
  const mergedEnv = {
172
208
  ...process.env,
173
209
  // Make sure we do not have empty key and value in mergedEnv, or adding server through UI will fail on Windows
174
- ...(cfg.env && !(0, mcpUtils_2.isEmptyEnv)(cfg.env)
210
+ ...(cfg.env && !(0, mcpUtils_1.isEmptyEnv)(cfg.env)
175
211
  ? Object.fromEntries(Object.entries(cfg.env).filter(([key]) => key && key.trim() !== ''))
176
212
  : {}),
177
213
  };
@@ -208,8 +244,8 @@ class McpManager {
208
244
  }
209
245
  throw new errors_1.AgenticChatError(`MCP: server '${serverName}' failed to connect: ${errorMessage}`, 'MCPServerConnectionFailed');
210
246
  });
211
- // 0 -> no timeout
212
- if (cfg.initializationTimeout === 0) {
247
+ // 0 or undefined -> no timeout
248
+ if (cfg.initializationTimeout === 0 || cfg.initializationTimeout === undefined) {
213
249
  await connectPromise;
214
250
  }
215
251
  else {
@@ -273,34 +309,73 @@ class McpManager {
273
309
  * Return a list of all enabled tools.
274
310
  */
275
311
  getEnabledTools() {
276
- return this.mcpTools.filter(t => !this.isServerDisabled(t.serverName) && !this.isToolDisabled(t.serverName, t.toolName));
312
+ return this.mcpTools.filter(t => !this.isToolDisabled(t.serverName, t.toolName));
277
313
  }
278
314
  /**
279
315
  * Returns true if the given tool on the given server is currently disabled.
280
316
  */
281
317
  isToolDisabled(server, tool) {
282
- return this.getToolPerm(server, tool) === mcpTypes_1.McpPermissionType.deny;
318
+ // built-in tools cannot be disabled
319
+ if (server === 'builtIn') {
320
+ return false;
321
+ }
322
+ // Check if the server is enabled as a whole (@server)
323
+ const serverPrefix = `@${server}`;
324
+ const isWholeServerEnabled = this.agentConfig.tools.includes(serverPrefix);
325
+ // Check if the specific tool is enabled
326
+ const toolId = `${serverPrefix}/${tool}`;
327
+ const isSpecificToolEnabled = this.agentConfig.tools.includes(toolId);
328
+ // If server is enabled as a whole, all tools are enabled
329
+ if (isWholeServerEnabled) {
330
+ return false;
331
+ }
332
+ // Otherwise, check if this specific tool is enabled
333
+ return !isSpecificToolEnabled;
283
334
  }
284
335
  /**
285
336
  * Returns true if the given server is currently disabled.
286
337
  */
287
- isServerDisabled(name) {
288
- const explicit = this.mcpServerPermissions.get(name)?.enabled;
289
- const star = this.mcpServerPermissions.get('*')?.enabled;
290
- return !(explicit ?? star ?? false);
291
- }
338
+ // public isServerDisabled(name: string): boolean {
339
+ // // Check if any tool from this server is enabled
340
+ // return !this.agentConfig.tools.some(tool => {
341
+ // if (tool.startsWith('@')) {
342
+ // // Check if it's the server itself or a tool from the server
343
+ // return tool === `@${name}` || tool.startsWith(`@${name}/`)
344
+ // }
345
+ // return false
346
+ // })
347
+ // }
292
348
  /**
293
349
  * Returns tool permission type for a given tool.
294
350
  */
295
351
  getToolPerm(server, tool) {
296
- const srv = this.mcpServerPermissions.get(server);
297
- const star = this.mcpServerPermissions.get('*');
298
- const result = srv?.toolPerms[tool] ??
299
- srv?.toolPerms['*'] ??
300
- star?.toolPerms[tool] ??
301
- star?.toolPerms['*'] ??
302
- mcpTypes_1.McpPermissionType.ask;
303
- return result;
352
+ // For built-in tools, check directly without prefix
353
+ if (server === 'Built-in') {
354
+ return this.agentConfig.allowedTools.includes(tool) ? mcpTypes_1.McpPermissionType.alwaysAllow : mcpTypes_1.McpPermissionType.ask;
355
+ }
356
+ // Check if the server is enabled as a whole (@server)
357
+ const serverPrefix = `@${server}`;
358
+ const isWholeServerEnabled = this.agentConfig.tools.includes(serverPrefix);
359
+ // Check if the specific tool is enabled
360
+ const toolId = `${serverPrefix}/${tool}`;
361
+ const isSpecificToolEnabled = this.agentConfig.tools.includes(toolId);
362
+ // If the tool is not enabled, return deny
363
+ if (!isWholeServerEnabled && !isSpecificToolEnabled) {
364
+ return mcpTypes_1.McpPermissionType.deny;
365
+ }
366
+ // If server is enabled as a whole, check if the server itself is in allowedTools
367
+ if (isWholeServerEnabled) {
368
+ // If server is in allowedTools, all tools are alwaysAllow
369
+ if (this.agentConfig.allowedTools.includes(serverPrefix)) {
370
+ return mcpTypes_1.McpPermissionType.alwaysAllow;
371
+ }
372
+ // Otherwise, check if specific tool is in allowedTools
373
+ return this.agentConfig.allowedTools.includes(toolId)
374
+ ? mcpTypes_1.McpPermissionType.alwaysAllow
375
+ : mcpTypes_1.McpPermissionType.ask;
376
+ }
377
+ // For specific tools, check if it's in allowedTools
378
+ return this.agentConfig.allowedTools.includes(toolId) ? mcpTypes_1.McpPermissionType.alwaysAllow : mcpTypes_1.McpPermissionType.ask;
304
379
  }
305
380
  /**
306
381
  * Return a list of all server configurations.
@@ -308,9 +383,6 @@ class McpManager {
308
383
  getAllServerConfigs() {
309
384
  return new Map(this.mcpServers);
310
385
  }
311
- getAllPermissions() {
312
- return new Map(this.mcpServerPermissions);
313
- }
314
386
  /**
315
387
  * Map server names to their available tool names.
316
388
  */
@@ -331,8 +403,7 @@ class McpManager {
331
403
  const cfg = this.mcpServers.get(server);
332
404
  if (!cfg)
333
405
  throw new Error(`MCP: server '${server}' is not configured`);
334
- if (this.isServerDisabled(server))
335
- throw new Error(`MCP: server '${server}' is disabled`);
406
+ // if (this.isServerDisabled(server)) throw new Error(`MCP: server '${server}' is disabled`)
336
407
  const available = this.getEnabledTools()
337
408
  .filter(t => t.serverName === server)
338
409
  .map(t => t.toolName);
@@ -366,43 +437,46 @@ class McpManager {
366
437
  /**
367
438
  * Add a new server: persist config, register in memory, and initialize.
368
439
  */
369
- async addServer(serverName, cfg, configPath, personaPath) {
440
+ async addServer(serverName, cfg, agentPath) {
370
441
  try {
371
- const sanitizedName = (0, mcpUtils_2.sanitizeName)(serverName);
372
- if (this.mcpServers.has(sanitizedName)) {
442
+ const sanitizedName = (0, mcpUtils_1.sanitizeName)(serverName);
443
+ if (this.mcpServers.has(sanitizedName) &&
444
+ this.getServerState(sanitizedName)?.status == mcpTypes_1.McpServerStatus.ENABLED) {
373
445
  throw new Error(`MCP: server '${sanitizedName}' already exists`);
374
446
  }
375
- if (!configPath || !personaPath) {
376
- throw new Error(`Both MCP config file path and Persona config file path are required`);
447
+ // Add server to agent config
448
+ const serverConfig = {
449
+ command: cfg.command,
450
+ initializationTimeout: cfg.initializationTimeout,
451
+ };
452
+ // Only add timeout to agent config if it's not 0
453
+ if (cfg.timeout !== 0) {
454
+ serverConfig.timeout = cfg.timeout;
455
+ }
456
+ if (cfg.args && cfg.args.length > 0) {
457
+ serverConfig.args = cfg.args;
377
458
  }
378
- await this.mutateConfigFile(configPath, json => {
379
- const serverConfig = {
380
- command: cfg.command,
381
- initializationTimeout: cfg.initializationTimeout,
382
- timeout: cfg.timeout,
383
- };
384
- if (cfg.args && cfg.args.length > 0) {
385
- serverConfig.args = cfg.args;
386
- }
387
- if (cfg.env && !(0, mcpUtils_2.isEmptyEnv)(cfg.env)) {
388
- serverConfig.env = cfg.env;
389
- }
390
- json.mcpServers[serverName] = serverConfig;
391
- });
392
- const newCfg = { ...cfg, __configPath__: configPath };
459
+ if (cfg.env && !(0, mcpUtils_1.isEmptyEnv)(cfg.env)) {
460
+ serverConfig.env = cfg.env;
461
+ }
462
+ // Add to agent config
463
+ this.agentConfig.mcpServers[serverName] = serverConfig;
464
+ // We don't need to store configPath anymore as we're using agent config
465
+ const newCfg = { ...cfg, __configPath__: agentPath };
393
466
  this.mcpServers.set(sanitizedName, newCfg);
394
467
  this.serverNameMapping.set(sanitizedName, serverName);
395
- await this.mutatePersonaFile(personaPath, p => p.addServer(serverName));
396
- this.personaPaths = [...new Set([...this.personaPaths, personaPath])];
397
- const permissionMap = await (0, mcpUtils_2.loadPersonaPermissions)(this.features.workspace, this.features.logging, this.personaPaths);
398
- this.mcpServerPermissions = permissionMap;
399
- if (this.isServerDisabled(sanitizedName)) {
400
- this.setState(sanitizedName, mcpTypes_1.McpServerStatus.DISABLED, 0);
401
- this.emitToolsChanged(serverName);
402
- }
403
- else {
404
- await this.initOneServer(sanitizedName, newCfg);
468
+ // Check if the server already has permissions in the agent config
469
+ const serverPrefix = `@${serverName}`;
470
+ const hasServerInTools = this.agentConfig.tools.some(tool => tool === serverPrefix || tool.startsWith(`${serverPrefix}/`));
471
+ // Only set permissions if the server doesn't already have them
472
+ if (!hasServerInTools) {
473
+ // Enable the server as a whole rather than individual tools
474
+ this.agentConfig.tools.push(serverPrefix);
405
475
  }
476
+ // Save agent config once with all changes
477
+ await (0, mcpUtils_1.saveAgentConfig)(this.features.workspace, this.features.logging, this.agentConfig, agentPath);
478
+ // Add server tools to tools list after initialization
479
+ await this.initOneServer(sanitizedName, newCfg);
406
480
  }
407
481
  catch (err) {
408
482
  this.features.logging.error(`Failed to add MCP server '${serverName}': ${err instanceof Error ? err.message : String(err)}`);
@@ -420,8 +494,6 @@ class McpManager {
420
494
  if (!cfg || !cfg.__configPath__) {
421
495
  throw new Error(`MCP: server '${serverName}' not found`);
422
496
  }
423
- // Capture the remaining server keys before deletion for persona file update
424
- const remainingServer = Array.from(this.mcpServers.keys()).filter(key => key !== serverName);
425
497
  const client = this.clients.get(serverName);
426
498
  if (client) {
427
499
  await client.close();
@@ -429,38 +501,54 @@ class McpManager {
429
501
  }
430
502
  this.mcpTools = this.mcpTools.filter(t => t.serverName !== serverName);
431
503
  this.mcpServerStates.delete(serverName);
432
- // Remove from config file first
433
- if (unsanitizedName) {
434
- await this.mutateConfigFile(cfg.__configPath__, json => {
435
- delete json.mcpServers[unsanitizedName];
504
+ // Remove from agent config
505
+ if (unsanitizedName && this.agentConfig) {
506
+ // Remove server from mcpServers
507
+ delete this.agentConfig.mcpServers[unsanitizedName];
508
+ // Remove server tools from tools list
509
+ this.agentConfig.tools = this.agentConfig.tools.filter(tool => {
510
+ if (tool.startsWith('@')) {
511
+ if (tool === `@${unsanitizedName}`) {
512
+ return false;
513
+ }
514
+ if (tool.startsWith(`@${unsanitizedName}/`)) {
515
+ return false;
516
+ }
517
+ }
518
+ return true;
436
519
  });
437
- }
438
- // Remove from persona file with the correct remaining server list
439
- if (permission && permission.__configPath__ && unsanitizedName) {
440
- await this.mutatePersonaFile(permission.__configPath__, p => p.removeServer(unsanitizedName, remainingServer));
520
+ // Remove server tools from allowedTools
521
+ this.agentConfig.allowedTools = this.agentConfig.allowedTools.filter(tool => {
522
+ if (tool.startsWith('@')) {
523
+ if (tool === `@${unsanitizedName}`) {
524
+ return false;
525
+ }
526
+ if (tool.startsWith(`@${unsanitizedName}/`)) {
527
+ return false;
528
+ }
529
+ }
530
+ return true;
531
+ });
532
+ // Save agent config
533
+ await (0, mcpUtils_1.saveAgentConfig)(this.features.workspace, this.features.logging, this.agentConfig, cfg.__configPath__);
441
534
  }
442
535
  this.mcpServers.delete(serverName);
443
536
  this.serverNameMapping.delete(serverName);
444
- this.mcpServerPermissions.delete(serverName);
445
- this.mcpServerPermissions = await (0, mcpUtils_2.loadPersonaPermissions)(this.features.workspace, this.features.logging, this.personaPaths);
446
537
  this.emitToolsChanged(serverName);
447
538
  }
448
539
  /**
449
540
  * Update a server: persist changes, teardown old client/tools, and re-init if enabled.
450
541
  */
451
- async updateServer(serverName, configUpdates, configPath) {
542
+ async updateServer(serverName, configUpdates, agentPath) {
452
543
  try {
453
- if (!configPath) {
454
- throw new Error(`Missing configPath for '${serverName}'`);
455
- }
456
544
  const oldCfg = this.mcpServers.get(serverName);
457
- if (!oldCfg || !oldCfg.__configPath__) {
545
+ if (!oldCfg) {
458
546
  throw new Error(`MCP: server '${serverName}' not found`);
459
547
  }
460
548
  const unsanitizedServerName = this.serverNameMapping.get(serverName);
461
- await this.mutateConfigFile(configPath, json => {
462
- json.mcpServers ||= {};
463
- const updatedConfig = { ...(json.mcpServers[unsanitizedServerName] || {}) };
549
+ // Update agent config
550
+ if (this.agentConfig && unsanitizedServerName) {
551
+ const updatedConfig = { ...(this.agentConfig.mcpServers[unsanitizedServerName] || {}) };
464
552
  if (configUpdates.command !== undefined)
465
553
  updatedConfig.command = configUpdates.command;
466
554
  if (configUpdates.initializationTimeout !== undefined)
@@ -476,19 +564,20 @@ class McpManager {
476
564
  }
477
565
  }
478
566
  if (configUpdates.env !== undefined) {
479
- if (!(0, mcpUtils_2.isEmptyEnv)(configUpdates.env)) {
567
+ if (!(0, mcpUtils_1.isEmptyEnv)(configUpdates.env)) {
480
568
  updatedConfig.env = configUpdates.env;
481
569
  }
482
570
  else {
483
571
  delete updatedConfig.env;
484
572
  }
485
573
  }
486
- json.mcpServers[unsanitizedServerName] = updatedConfig;
487
- });
574
+ this.agentConfig.mcpServers[unsanitizedServerName] = updatedConfig;
575
+ // Save agent config
576
+ await (0, mcpUtils_1.saveAgentConfig)(this.features.workspace, this.features.logging, this.agentConfig, agentPath);
577
+ }
488
578
  const newCfg = {
489
579
  ...oldCfg,
490
580
  ...configUpdates,
491
- __configPath__: configPath,
492
581
  };
493
582
  const oldClient = this.clients.get(serverName);
494
583
  if (oldClient) {
@@ -498,13 +587,13 @@ class McpManager {
498
587
  this.mcpTools = this.mcpTools.filter(t => t.serverName !== serverName);
499
588
  this.mcpServers.set(serverName, newCfg);
500
589
  this.serverNameMapping.set(serverName, unsanitizedServerName);
501
- if (this.isServerDisabled(serverName)) {
502
- this.setState(serverName, mcpTypes_1.McpServerStatus.DISABLED, 0);
503
- this.emitToolsChanged(serverName);
504
- }
505
- else {
506
- await this.initOneServer(serverName, newCfg);
507
- }
590
+ // if (this.isServerDisabled(serverName)) {
591
+ // this.setState(serverName, McpServerStatus.DISABLED, 0)
592
+ // this.emitToolsChanged(serverName)
593
+ // } else {
594
+ // await this.initOneServer(serverName, newCfg)
595
+ // }
596
+ await this.initOneServer(serverName, newCfg);
508
597
  }
509
598
  catch (err) {
510
599
  this.handleError(serverName, err);
@@ -529,6 +618,17 @@ class McpManager {
529
618
  this.mcpTools = [];
530
619
  this.mcpServers.clear();
531
620
  this.mcpServerStates.clear();
621
+ this.agentConfig = {
622
+ name: 'default-agent',
623
+ version: '1.0.0',
624
+ description: 'Agent configuration',
625
+ mcpServers: {},
626
+ tools: [],
627
+ allowedTools: [],
628
+ toolsSettings: {},
629
+ includedFiles: [],
630
+ resources: [],
631
+ };
532
632
  if (!keepInstance) {
533
633
  McpManager.#instance = undefined;
534
634
  }
@@ -559,49 +659,106 @@ class McpManager {
559
659
  */
560
660
  async updateServerPermission(serverName, perm) {
561
661
  try {
562
- const personaPath = perm.__configPath__;
563
- if (!personaPath) {
564
- throw new Error(`Missing personaPath for '${serverName}'`);
565
- }
566
- await this.mutatePersonaFile(personaPath, p => {
567
- if (perm.enabled === undefined) {
568
- throw new Error('Server disabled state must be explicitly set');
569
- }
570
- const unsanitizedServerName = this.serverNameMapping.get(serverName);
571
- // disable whole server
572
- if (!perm.enabled) {
573
- p.removeServer(unsanitizedServerName, Array.from(this.mcpServers.keys())); // removes from list clears tool perms
574
- return;
575
- }
576
- // server must be enabled from here on
577
- p.addServer(unsanitizedServerName);
578
- // handle permission updates
579
- if (perm.toolPerms) {
580
- const existing = p.toJson().toolPerms?.[unsanitizedServerName] ?? {};
581
- const merged = { ...existing, ...perm.toolPerms };
582
- p.replaceToolPerms(unsanitizedServerName, merged);
662
+ const unsanitizedServerName = this.serverNameMapping.get(serverName) || serverName;
663
+ // Get server config
664
+ // const serverConfig = this.mcpServers.get(serverName)
665
+ // if (!serverConfig) {
666
+ // throw new Error(`Server '${serverName}' not found`)
667
+ // }
668
+ const serverPrefix = `@${unsanitizedServerName}`;
669
+ // Track tools that should be enabled
670
+ const toolsToEnable = new Set();
671
+ const toolsToAlwaysAllow = new Set();
672
+ // Check if server is enabled as a whole
673
+ const isWholeServerEnabled = this.agentConfig.tools.includes(serverPrefix);
674
+ // Process each tool permission
675
+ for (const [toolName, permission] of Object.entries(perm.toolPerms || {})) {
676
+ const toolId = (unsanitizedServerName !== 'Built-in' ? `${serverPrefix}/` : '') + `${toolName}`;
677
+ if (permission === mcpTypes_1.McpPermissionType.deny) {
678
+ // For deny: if server is enabled as a whole, we need to switch to individual tools
679
+ if (isWholeServerEnabled) {
680
+ // Get all tools for this server
681
+ const serverTools = this.mcpTools.filter(t => t.serverName === serverName);
682
+ // Remove server prefix from tools
683
+ this.agentConfig.tools = this.agentConfig.tools.filter(t => t !== serverPrefix);
684
+ // Add all tools except the denied one
685
+ for (const t of serverTools) {
686
+ if (t.toolName !== toolName) {
687
+ const tid = `${serverPrefix}/${t.toolName}`;
688
+ if (!this.agentConfig.tools.includes(tid)) {
689
+ this.agentConfig.tools.push(tid);
690
+ }
691
+ toolsToEnable.add(tid);
692
+ }
693
+ }
694
+ }
695
+ else {
696
+ // Just remove the specific tool
697
+ this.agentConfig.tools = this.agentConfig.tools.filter(t => t !== toolId);
698
+ }
699
+ // Always remove from allowedTools
700
+ this.agentConfig.allowedTools = this.agentConfig.allowedTools.filter(t => t !== toolId);
583
701
  }
584
702
  else {
585
- p.ensureWildcardAsk(unsanitizedServerName);
703
+ // For ask or alwaysAllow: add to tools
704
+ toolsToEnable.add(toolId);
705
+ // For alwaysAllow: also add to allowedTools
706
+ if (permission === mcpTypes_1.McpPermissionType.alwaysAllow) {
707
+ toolsToAlwaysAllow.add(toolId);
708
+ }
709
+ else {
710
+ // For ask: remove from allowedTools if present
711
+ this.agentConfig.allowedTools = this.agentConfig.allowedTools.filter(t => t !== toolId);
712
+ }
586
713
  }
587
- });
588
- const permissionMap = await (0, mcpUtils_2.loadPersonaPermissions)(this.features.workspace, this.features.logging, this.personaPaths);
589
- this.mcpServerPermissions = permissionMap;
590
- // enable/disable server
591
- if (this.isServerDisabled(serverName)) {
592
- const client = this.clients.get(serverName);
593
- if (client) {
594
- await client.close();
595
- this.clients.delete(serverName);
714
+ }
715
+ // If all tools are enabled, use @serverName instead of individual tools
716
+ const allTools = this.mcpTools.filter(t => t.serverName === serverName).map(t => t.toolName);
717
+ // Check if all tools are enabled, considering both:
718
+ // 1. The server might already be enabled as a whole (isWholeServerEnabled)
719
+ // 2. All tools might be individually enabled in toolsToEnable
720
+ const allToolsEnabled = allTools.length > 0 &&
721
+ // If server is already enabled as a whole and no tools are being denied
722
+ ((isWholeServerEnabled && !Object.values(perm.toolPerms || {}).includes(mcpTypes_1.McpPermissionType.deny)) ||
723
+ // Or if all tools are individually enabled
724
+ allTools.every(toolName => toolsToEnable.has(`${serverPrefix}/${toolName}`) ||
725
+ !Object.keys(perm.toolPerms || {}).includes(toolName)));
726
+ // Update tools list
727
+ if (allToolsEnabled) {
728
+ // Remove individual tool entries
729
+ this.agentConfig.tools = this.agentConfig.tools.filter(t => !t.startsWith(`${serverPrefix}/`));
730
+ // Add server prefix if not already there
731
+ if (!this.agentConfig.tools.includes(serverPrefix)) {
732
+ this.agentConfig.tools.push(serverPrefix);
596
733
  }
597
- this.setState(serverName, mcpTypes_1.McpServerStatus.DISABLED, 0);
598
734
  }
599
735
  else {
600
- if (!this.clients.has(serverName)) {
601
- await this.initOneServer(serverName, this.mcpServers.get(serverName));
736
+ // Remove server prefix if present
737
+ this.agentConfig.tools = this.agentConfig.tools.filter(t => t !== serverPrefix);
738
+ // Add individual tools
739
+ for (const toolId of toolsToEnable) {
740
+ if (!this.agentConfig.tools.includes(toolId)) {
741
+ this.agentConfig.tools.push(toolId);
742
+ }
602
743
  }
603
744
  }
604
- this.features.logging.info(`Permissions updated for '${serverName}' in ${personaPath}`);
745
+ // Update allowedTools list
746
+ for (const toolId of toolsToAlwaysAllow) {
747
+ if (!this.agentConfig.allowedTools.includes(toolId)) {
748
+ this.agentConfig.allowedTools.push(toolId);
749
+ }
750
+ }
751
+ // Save agent config
752
+ const agentPath = perm.__configPath__;
753
+ if (agentPath) {
754
+ await (0, mcpUtils_1.saveAgentConfig)(this.features.workspace, this.features.logging, this.agentConfig, agentPath);
755
+ }
756
+ // Update mcpServerPermissions map
757
+ this.mcpServerPermissions.set(serverName, {
758
+ enabled: perm.enabled,
759
+ toolPerms: perm.toolPerms || {},
760
+ });
761
+ this.features.logging.info(`Permissions updated for '${serverName}' in agent config`);
605
762
  this.emitToolsChanged(serverName);
606
763
  }
607
764
  catch (err) {
@@ -609,73 +766,13 @@ class McpManager {
609
766
  return;
610
767
  }
611
768
  }
612
- /**
613
- * Read, mutate, and write the MCP JSON config at the given path.
614
- * @private
615
- */
616
- async mutateConfigFile(configPath, mutator) {
617
- return McpManager.configMutex
618
- .runExclusive(async () => {
619
- let json = { mcpServers: {} };
620
- try {
621
- const raw = await this.features.workspace.fs.readFile(configPath);
622
- this.features.logging.info(`Updating MCP config file: ${configPath}`);
623
- const existing = JSON.parse(raw.toString());
624
- json = { mcpServers: {}, ...existing };
625
- }
626
- catch (err) {
627
- // ignore fire not exist error
628
- if (err?.code !== 'ENOENT')
629
- throw err;
630
- }
631
- mutator(json);
632
- let fsPath;
633
- try {
634
- const uri = vscode_uri_1.URI.parse(configPath);
635
- fsPath = uri.scheme === 'file' ? uri.fsPath : configPath;
636
- }
637
- catch {
638
- fsPath = configPath;
639
- }
640
- fsPath = path.normalize(fsPath);
641
- const dir = path.dirname(fsPath);
642
- await this.features.workspace.fs.mkdir(dir, { recursive: true });
643
- await this.features.workspace.fs.writeFile(fsPath, JSON.stringify(json, null, 2));
644
- this.features.logging.debug(`MCP config file write complete: ${configPath}`);
645
- })
646
- .catch((e) => {
647
- this.features.logging.error(`MCP: failed to update config at ${configPath}: ${e.message}`);
648
- throw e;
649
- });
650
- }
651
- /**
652
- * Read, mutate, and write the Persona YAML config at the given path.
653
- * @private
654
- */
655
- async mutatePersonaFile(personaPath, mutator) {
656
- await McpManager.personaMutex
657
- .runExclusive(async () => {
658
- this.features.logging.info(`Updating persona file: ${personaPath}`);
659
- let raw = '';
660
- try {
661
- raw = (await this.features.workspace.fs.readFile(personaPath)).toString();
662
- }
663
- catch { }
664
- const model = mcpTypes_1.PersonaModel.fromJson(raw ? JSON.parse(raw) : {});
665
- mutator(model);
666
- await this.features.workspace.fs.writeFile(personaPath, JSON.stringify(model.toJson(), null, 2));
667
- this.features.logging.debug(`Persona file write complete: ${personaPath}`);
668
- })
669
- .catch((e) => {
670
- this.features.logging.error(`MCP: failed to update persona file at ${personaPath}: ${e.message}`);
671
- throw e;
672
- });
673
- }
674
769
  /**
675
770
  * Check if a tool requires approval.
676
771
  */
677
772
  requiresApproval(server, tool) {
678
- return this.getToolPerm(server, tool) === mcpTypes_1.McpPermissionType.ask;
773
+ // For built-in tools, check directly without prefix
774
+ const toolId = server === 'Built-in' ? tool : `@${server}/${tool}`;
775
+ return !this.agentConfig.allowedTools.includes(toolId);
679
776
  }
680
777
  /**
681
778
  * Updates the runtime state for a given server, including status, tool count, and optional error message.
@@ -723,7 +820,7 @@ class McpManager {
723
820
  .join('\n\n');
724
821
  }
725
822
  /**
726
- * Remove a server from the config file but keep it in memory.
823
+ * Remove a server from the agent config file but keep it in memory.
727
824
  * This is used when there's a server status error during initialization.
728
825
  */
729
826
  async removeServerFromConfigFile(serverName) {
@@ -733,13 +830,41 @@ class McpManager {
733
830
  this.features.logging.warn(`Cannot remove config for server '${serverName}': Config not found or missing path`);
734
831
  return;
735
832
  }
736
- await this.mutateConfigFile(cfg.__configPath__, json => {
737
- delete json.mcpServers[this.serverNameMapping.get(serverName)];
738
- });
739
- this.features.logging.info(`Removed server '${serverName}' from config file but kept in memory`);
833
+ const unsanitizedName = this.serverNameMapping.get(serverName) || serverName;
834
+ // Remove from agent config
835
+ if (unsanitizedName && this.agentConfig) {
836
+ // Remove server from mcpServers
837
+ delete this.agentConfig.mcpServers[unsanitizedName];
838
+ // Remove server tools from tools list
839
+ this.agentConfig.tools = this.agentConfig.tools.filter(tool => {
840
+ if (tool.startsWith('@')) {
841
+ if (tool === `@${unsanitizedName}`) {
842
+ return false;
843
+ }
844
+ if (tool.startsWith(`@${unsanitizedName}/`)) {
845
+ return false;
846
+ }
847
+ }
848
+ return true;
849
+ });
850
+ // Remove server tools from allowedTools
851
+ this.agentConfig.allowedTools = this.agentConfig.allowedTools.filter(tool => {
852
+ if (tool.startsWith('@')) {
853
+ if (tool === `@${unsanitizedName}`) {
854
+ return false;
855
+ }
856
+ if (tool.startsWith(`@${unsanitizedName}/`)) {
857
+ return false;
858
+ }
859
+ }
860
+ return true;
861
+ });
862
+ // Save agent config
863
+ await (0, mcpUtils_1.saveAgentConfig)(this.features.workspace, this.features.logging, this.agentConfig, cfg.__configPath__);
864
+ }
740
865
  }
741
866
  catch (err) {
742
- this.features.logging.error(`Error removing server '${serverName}' from config file: ${err}`);
867
+ this.features.logging.error(`Error removing server '${serverName}' from agent config file: ${err}`);
743
868
  }
744
869
  }
745
870
  getOriginalToolNames(namespacedName) {
@@ -751,6 +876,18 @@ class McpManager {
751
876
  getToolNameMapping() {
752
877
  return new Map(this.toolNameMapping);
753
878
  }
879
+ /**
880
+ * Determines if a server is global or workspace-specific
881
+ * @param serverName The name of the server to check
882
+ * @returns true if the server is global, false if workspace-specific
883
+ */
884
+ isServerGlobal(serverName) {
885
+ const config = this.mcpServers.get(serverName);
886
+ if (!config)
887
+ return false;
888
+ const globalAgentPath = (0, mcpUtils_1.getGlobalAgentConfigPath)(this.features.workspace.fs.getUserHomeDir());
889
+ return config.__configPath__ === globalAgentPath;
890
+ }
754
891
  setToolNameMapping(mapping) {
755
892
  this.toolNameMapping = new Map(mapping);
756
893
  }