@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
@@ -16,6 +16,8 @@ class McpEventHandler {
16
16
  #newlyAddedServers = new Set();
17
17
  #fileWatcher;
18
18
  #isProgrammaticChange = false;
19
+ #debounceTimer = null;
20
+ #lastProgrammaticState = false;
19
21
  constructor(features, telemetryService) {
20
22
  this.#features = features;
21
23
  this.#eventListenerRegistered = false;
@@ -53,6 +55,7 @@ class McpEventHandler {
53
55
  * Handles the list MCP servers event
54
56
  */
55
57
  async onListMcpServers(params) {
58
+ this.#currentEditingServerName = undefined;
56
59
  const mcpManager = mcpManager_1.McpManager.instance;
57
60
  // Check for errors in loading MCP config files
58
61
  const configLoadErrors = mcpManager.getConfigLoadErrors();
@@ -85,36 +88,39 @@ class McpEventHandler {
85
88
  // Transform server configs into DetailedListItem objects
86
89
  const activeItems = [];
87
90
  const disabledItems = [];
88
- const builtInItems = [];
89
91
  // Get built-in tools programmatically
90
92
  const allTools = this.#features.agent.getTools({ format: 'bedrock' });
91
- const mcpToolNames = new Set(mcpManager.getAllTools().map(tool => tool.toolName));
93
+ const builtInToolNames = new Set(this.#features.agent.getBuiltInToolNames());
92
94
  const builtInTools = allTools
93
- .filter(tool => !mcpToolNames.has(tool.toolSpecification.name))
95
+ .filter(tool => {
96
+ return builtInToolNames.has(tool.toolSpecification.name) && tool.toolSpecification.name !== 'fsReplace';
97
+ })
94
98
  .map(tool => ({
95
99
  name: tool.toolSpecification.name,
96
- description: tool.toolSpecification.description || `${tool.toolSpecification.name} tool`,
100
+ description: this.#getBuiltInToolDescription(tool.toolSpecification.name) ||
101
+ tool.toolSpecification.description ||
102
+ `${tool.toolSpecification.name} tool`,
97
103
  }));
98
104
  // Add built-in tools as a server in the active items
99
- // activeItems.push({
100
- // title: 'Built-in',
101
- // description: `${builtInTools.length} tools`,
102
- // children: [
103
- // {
104
- // groupName: 'serverInformation',
105
- // children: [
106
- // {
107
- // title: 'status',
108
- // description: 'ENABLED',
109
- // },
110
- // {
111
- // title: 'toolcount',
112
- // description: `${builtInTools.length}`,
113
- // },
114
- // ],
115
- // },
116
- // ],
117
- // })
105
+ activeItems.push({
106
+ title: 'Built-in',
107
+ description: `${builtInTools.length} tools`,
108
+ children: [
109
+ {
110
+ groupName: 'serverInformation',
111
+ children: [
112
+ {
113
+ title: 'status',
114
+ description: 'ENABLED',
115
+ },
116
+ {
117
+ title: 'toolcount',
118
+ description: `${builtInTools.length}`,
119
+ },
120
+ ],
121
+ },
122
+ ],
123
+ });
118
124
  Array.from(mcpManagerServerConfigs.entries()).forEach(([serverName, config]) => {
119
125
  const toolsWithPermissions = mcpManager.getAllToolsWithPermissions(serverName);
120
126
  const toolsCount = toolsWithPermissions.length;
@@ -140,15 +146,13 @@ class McpEventHandler {
140
146
  },
141
147
  ],
142
148
  };
143
- if (mcpManager.isServerDisabled(serverName)) {
144
- disabledItems.push(item);
145
- }
146
- else {
147
- activeItems.push({
148
- ...item,
149
- description: `${toolsCount}`,
150
- });
151
- }
149
+ // if (mcpManager.isServerDisabled(serverName)) {
150
+ // disabledItems.push(item)
151
+ // } else {
152
+ activeItems.push({
153
+ ...item,
154
+ description: `${toolsCount}`,
155
+ });
152
156
  });
153
157
  // Create the groups
154
158
  const groups = [];
@@ -172,7 +176,7 @@ class McpEventHandler {
172
176
  }
173
177
  // Return the result in the expected format
174
178
  const header = {
175
- title: 'MCP Servers',
179
+ title: 'MCP Servers and Built-in Tools',
176
180
  description: "Add MCP servers to extend Q's capabilities.",
177
181
  // only show error on list mcp server page if unable to read mcp.json file
178
182
  status: configLoadErrors
@@ -214,7 +218,7 @@ class McpEventHandler {
214
218
  return {
215
219
  id,
216
220
  header: {
217
- title: 'MCP Servers',
221
+ title: 'MCP Servers and Built-in Tools',
218
222
  status: {},
219
223
  description: `Add MCP servers to extend Q's capabilities.`,
220
224
  actions: [],
@@ -260,12 +264,16 @@ class McpEventHandler {
260
264
  const serverName = existingValues.name;
261
265
  const sanitizedServerName = (0, mcpUtils_1.sanitizeName)(serverName);
262
266
  const serverState = mcpManager_1.McpManager.instance.getAllServerConfigs().get(sanitizedServerName);
263
- if (!serverState ||
264
- serverState?.__configPath__ === (0, mcpUtils_1.getGlobalMcpConfigPath)(this.#features.workspace.fs.getUserHomeDir())) {
265
- existingValues.scope = 'global';
267
+ // Check if the server exists in McpManager
268
+ const mcpManager = mcpManager_1.McpManager.instance;
269
+ const serverConfig = mcpManager.getAllServerConfigs().get(sanitizedServerName);
270
+ if (serverConfig) {
271
+ // Use the helper method to determine if the server is global
272
+ existingValues.scope = mcpManager.isServerGlobal(sanitizedServerName) ? 'global' : 'workspace';
266
273
  }
267
274
  else {
268
- existingValues.scope = 'workspace';
275
+ // Default to global scope for new servers
276
+ existingValues.scope = 'global';
269
277
  }
270
278
  }
271
279
  const serverStatusError = this.#getServerStatusError(existingValues.name) || {};
@@ -440,7 +448,10 @@ class McpEventHandler {
440
448
  else {
441
449
  if (checkExistingServerName) {
442
450
  const existingServers = mcpManager_1.McpManager.instance.getAllServerConfigs();
443
- if (existingServers.has(values.name) && values.name !== originalServerName) {
451
+ const serverState = mcpManager_1.McpManager.instance.getServerState(values.name);
452
+ if (existingServers.has(values.name) &&
453
+ values.name !== originalServerName &&
454
+ serverState?.status === mcpTypes_1.McpServerStatus.ENABLED) {
444
455
  errors.push(`Server name "${values.name}" already exists`);
445
456
  }
446
457
  }
@@ -539,22 +550,11 @@ class McpEventHandler {
539
550
  env,
540
551
  timeout: timeoutInMs,
541
552
  };
542
- let configPath = (0, mcpUtils_1.getGlobalMcpConfigPath)(this.#features.workspace.fs.getUserHomeDir());
543
- let personaPath = await this.#getPersonaPath();
544
- if (params.optionsValues['scope'] !== 'global') {
545
- // Get workspace folders and convert to paths
546
- const workspaceFolders = this.#features.workspace.getAllWorkspaceFolders();
547
- // Extract paths from workspace folders - uri is already a string
548
- const workspacePaths = workspaceFolders.map(folder => folder.uri);
549
- // Get the first path from the result or fall back to configPath
550
- const workspaceMcpPaths = (0, mcpUtils_1.getWorkspaceMcpConfigPaths)(workspacePaths);
551
- configPath =
552
- Array.isArray(workspaceMcpPaths) && workspaceMcpPaths.length > 0
553
- ? (0, mcpUtils_1.normalizePathFromUri)(workspaceMcpPaths[0], this.#features.logging)
554
- : configPath;
555
- // Get the appropriate persona path using our helper method
556
- personaPath = await this.#getPersonaPath();
557
- }
553
+ // Get agent path based on scope
554
+ const isGlobal = params.optionsValues['scope'] === 'global';
555
+ const agentPath = await this.#getAgentPath(isGlobal);
556
+ // We still need a configPath for backward compatibility, but it's not used anymore
557
+ const configPath = '';
558
558
  // needs to false BEFORE changing any server state, to prevent going to list servers page after clicking save button
559
559
  this.#shouldDisplayListMCPServers = false;
560
560
  // Set flag to ignore file changes during server operations
@@ -562,17 +562,16 @@ class McpEventHandler {
562
562
  try {
563
563
  if (isEditMode && originalServerName) {
564
564
  await mcpManager_1.McpManager.instance.removeServer(originalServerName);
565
- await mcpManager_1.McpManager.instance.addServer(serverName, config, configPath, personaPath);
565
+ await mcpManager_1.McpManager.instance.addServer(serverName, config, agentPath);
566
566
  }
567
567
  else {
568
568
  // Create new server
569
- await mcpManager_1.McpManager.instance.addServer(serverName, config, configPath, personaPath);
569
+ await mcpManager_1.McpManager.instance.addServer(serverName, config, agentPath);
570
570
  this.#newlyAddedServers.add(serverName);
571
571
  }
572
572
  }
573
- finally {
574
- // Reset flag after operations
575
- this.#isProgrammaticChange = false;
573
+ catch (error) {
574
+ this.#features.logging.error(`Failed to enable MCP server: ${error}`);
576
575
  }
577
576
  this.#currentEditingServerName = undefined;
578
577
  // need to check server state now, as there is possibility of error during server initialization
@@ -588,12 +587,12 @@ class McpEventHandler {
588
587
  languageServerVersion: this.#features.runtime.serverInfo.version,
589
588
  });
590
589
  if (serverStatusError) {
591
- // Error case: remove config from config file only if it's a newly added server
590
+ await mcpManager_1.McpManager.instance.removeServerFromConfigFile(serverName);
592
591
  if (this.#newlyAddedServers.has(serverName)) {
593
- await mcpManager_1.McpManager.instance.removeServerFromConfigFile(serverName);
594
592
  this.#newlyAddedServers.delete(serverName);
595
593
  }
596
594
  // Stay on add/edit page and show error to user
595
+ // Keep isProgrammaticChange true during error handling to prevent file watcher triggers
597
596
  if (isEditMode) {
598
597
  params.id = 'edit-mcp';
599
598
  params.title = sanitizedServerName;
@@ -609,6 +608,7 @@ class McpEventHandler {
609
608
  if (this.#newlyAddedServers.has(serverName)) {
610
609
  this.#newlyAddedServers.delete(serverName);
611
610
  }
611
+ this.#isProgrammaticChange = false;
612
612
  // Go to tools permissions page
613
613
  return this.#handleOpenMcpServer({ id: 'open-mcp-server', title: sanitizedServerName });
614
614
  }
@@ -626,16 +626,20 @@ class McpEventHandler {
626
626
  if (serverName === 'Built-in') {
627
627
  // Handle Built-in server specially
628
628
  const allTools = this.#features.agent.getTools({ format: 'bedrock' });
629
- const mcpToolNames = new Set(mcpManager_1.McpManager.instance.getAllTools().map(tool => tool.toolName));
629
+ const builtInToolNames = new Set(this.#features.agent.getBuiltInToolNames());
630
+ // combine fsWrite and fsReplace into fsWrite
630
631
  const builtInTools = allTools
631
- .filter(tool => !mcpToolNames.has(tool.toolSpecification.name))
632
+ .filter(tool => {
633
+ return (builtInToolNames.has(tool.toolSpecification.name) && tool.toolSpecification.name !== 'fsReplace');
634
+ })
632
635
  .map(tool => {
633
- // Set default permission based on tool name
634
- const permission = 'alwaysAllow';
636
+ const permission = mcpManager_1.McpManager.instance.getToolPerm(serverName, tool.toolSpecification.name);
635
637
  return {
636
638
  tool: {
637
639
  toolName: tool.toolSpecification.name,
638
- description: tool.toolSpecification.description || `${tool.toolSpecification.name} tool`,
640
+ description: this.#getBuiltInToolDescription(tool.toolSpecification.name) ||
641
+ tool.toolSpecification.description ||
642
+ `${tool.toolSpecification.name} tool`,
639
643
  },
640
644
  permission,
641
645
  };
@@ -646,6 +650,7 @@ class McpEventHandler {
646
650
  header: {
647
651
  title: serverName,
648
652
  status: serverStatusError || {},
653
+ description: 'TOOLS',
649
654
  actions: [],
650
655
  },
651
656
  list: [],
@@ -689,12 +694,12 @@ class McpEventHandler {
689
694
  if (!serverName) {
690
695
  return { id: params.id };
691
696
  }
692
- // Get the appropriate persona path
693
- const personaPath = await this.#getPersonaPath();
697
+ // Get the appropriate agent path
698
+ const agentPath = await this.#getAgentPath();
694
699
  const perm = {
695
700
  enabled: true,
696
701
  toolPerms: {},
697
- __configPath__: personaPath,
702
+ __configPath__: agentPath,
698
703
  };
699
704
  // Set flag to ignore file changes during permission update
700
705
  this.#isProgrammaticChange = true;
@@ -705,10 +710,7 @@ class McpEventHandler {
705
710
  catch (error) {
706
711
  this.#features.logging.error(`Failed to enable MCP server: ${error}`);
707
712
  }
708
- finally {
709
- // Reset flag after operations
710
- this.#isProgrammaticChange = false;
711
- }
713
+ this.#isProgrammaticChange = false;
712
714
  return { id: params.id };
713
715
  }
714
716
  /**
@@ -719,12 +721,12 @@ class McpEventHandler {
719
721
  if (!serverName) {
720
722
  return { id: params.id };
721
723
  }
722
- // Get the appropriate persona path
723
- const personaPath = await this.#getPersonaPath();
724
+ // Get the appropriate agent path
725
+ const agentPath = await this.#getAgentPath();
724
726
  const perm = {
725
727
  enabled: false,
726
728
  toolPerms: {},
727
- __configPath__: personaPath,
729
+ __configPath__: agentPath,
728
730
  };
729
731
  // Set flag to ignore file changes during permission update
730
732
  this.#isProgrammaticChange = true;
@@ -735,10 +737,7 @@ class McpEventHandler {
735
737
  catch (error) {
736
738
  this.#features.logging.error(`Failed to disable MCP server: ${error}`);
737
739
  }
738
- finally {
739
- // Reset flag after operations
740
- this.#isProgrammaticChange = false;
741
- }
740
+ this.#isProgrammaticChange = false;
742
741
  return { id: params.id };
743
742
  }
744
743
  /**
@@ -753,23 +752,24 @@ class McpEventHandler {
753
752
  this.#isProgrammaticChange = true;
754
753
  try {
755
754
  await mcpManager_1.McpManager.instance.removeServer(serverName);
755
+ return { id: params.id };
756
756
  }
757
757
  catch (error) {
758
758
  this.#features.logging.error(`Failed to delete MCP server: ${error}`);
759
- }
760
- finally {
761
- // Reset flag after operations
762
759
  this.#isProgrammaticChange = false;
760
+ return { id: params.id };
763
761
  }
764
- return { id: params.id };
765
762
  }
766
763
  /**
767
764
  * Handles edit MCP configuration
768
765
  */
769
766
  async #handleEditMcpServer(params, error) {
767
+ // Set programmatic change flag to true to prevent file watcher triggers
768
+ this.#isProgrammaticChange = true;
770
769
  await this.#handleSavePermissionChange({ id: 'save-mcp-permission' });
771
770
  const serverName = params.title;
772
771
  if (!serverName) {
772
+ this.#isProgrammaticChange = false;
773
773
  return { id: params.id };
774
774
  }
775
775
  this.#currentEditingServerName = serverName;
@@ -822,16 +822,21 @@ class McpEventHandler {
822
822
  // Add tool select options
823
823
  toolsWithPermissions.forEach(item => {
824
824
  const toolName = item.tool.toolName;
825
- const currentPermission = this.#getCurrentPermission(item.permission);
826
825
  // For Built-in server, use a special function that doesn't include the 'Deny' option
827
- const permissionOptions = this.#buildPermissionOptions(item.permission);
826
+ let permissionOptions = this.#buildPermissionOptions(item.permission);
827
+ if (serverName === 'Built-in') {
828
+ permissionOptions = this.#buildBuiltInPermissionOptions(item.permission);
829
+ }
828
830
  filterOptions.push({
829
831
  type: 'select',
830
832
  id: `${toolName}`,
831
833
  title: toolName,
832
834
  description: item.tool.description,
833
- placeholder: currentPermission,
834
835
  options: permissionOptions,
836
+ ...(toolName === 'fsWrite'
837
+ ? { disabled: true, selectTooltip: 'Permission for this tool is not configurable yet' }
838
+ : {}),
839
+ ...{ value: item.permission, boldTitle: true, mandatory: true, hideMandatoryIcon: true },
835
840
  });
836
841
  });
837
842
  return filterOptions;
@@ -855,41 +860,65 @@ class McpEventHandler {
855
860
  */
856
861
  #buildPermissionOptions(currentPermission) {
857
862
  const permissionOptions = [];
858
- if (currentPermission !== mcpTypes_1.McpPermissionType.alwaysAllow) {
859
- permissionOptions.push({ label: 'Always allow', value: mcpTypes_1.McpPermissionType.alwaysAllow });
860
- }
861
- if (currentPermission !== mcpTypes_1.McpPermissionType.ask) {
862
- permissionOptions.push({ label: 'Ask', value: mcpTypes_1.McpPermissionType.ask });
863
- }
864
- if (currentPermission !== mcpTypes_1.McpPermissionType.deny) {
865
- permissionOptions.push({ label: 'Deny', value: mcpTypes_1.McpPermissionType.deny });
866
- }
863
+ permissionOptions.push({
864
+ label: 'Ask',
865
+ value: mcpTypes_1.McpPermissionType.ask,
866
+ description: 'Ask for your approval each time this tool is run',
867
+ });
868
+ permissionOptions.push({
869
+ label: 'Always allow',
870
+ value: mcpTypes_1.McpPermissionType.alwaysAllow,
871
+ description: 'Always allow this tool to run without asking for approval',
872
+ });
873
+ permissionOptions.push({ label: 'Deny', value: mcpTypes_1.McpPermissionType.deny, description: 'Never run this tool' });
867
874
  return permissionOptions;
868
875
  }
869
876
  /**
870
877
  * Builds permission options for Built-in tools (no 'Disable' option)
871
878
  */
872
- // #buildBuiltInPermissionOptions(currentPermission: string) {
873
- // const permissionOptions: PermissionOption[] = []
874
- // if (currentPermission !== 'alwaysAllow') {
875
- // permissionOptions.push({
876
- // label: 'Always run',
877
- // value: 'alwaysAllow',
878
- // })
879
- // }
880
- // if (currentPermission !== 'ask') {
881
- // permissionOptions.push({
882
- // label: 'Ask to run',
883
- // value: 'ask',
884
- // })
885
- // }
886
- // return permissionOptions
887
- // }
879
+ #buildBuiltInPermissionOptions(currentPermission) {
880
+ const permissionOptions = [];
881
+ permissionOptions.push({
882
+ label: 'Ask',
883
+ value: 'ask',
884
+ description: 'Ask for your approval each time this tool is run',
885
+ });
886
+ permissionOptions.push({
887
+ label: 'Always Allow',
888
+ value: 'alwaysAllow',
889
+ description: 'Always allow this tool to run without asking for approval',
890
+ });
891
+ return permissionOptions;
892
+ }
893
+ #getBuiltInToolDescription(toolName) {
894
+ switch (toolName) {
895
+ case 'fsRead':
896
+ return 'Read the content of files.';
897
+ case 'listDirectory':
898
+ return 'List the structure of a directory and its subdirectories.';
899
+ case 'fileSearch':
900
+ return 'Search for files and directories using fuzzy name matching.';
901
+ case 'executeBash':
902
+ return 'Run shell or powershell commands.\n\nNote: read-only commands are auto-run';
903
+ case 'fsWrite':
904
+ case 'fsReplace':
905
+ return 'Create or edit files.';
906
+ case 'qCodeReview':
907
+ return 'Review tool analyzes code for security vulnerabilities, quality issues, and best practices across multiple programming languages.';
908
+ default:
909
+ return '';
910
+ }
911
+ }
888
912
  /**
889
913
  * Handles MCP permission change events to update the pending permission config without applying changes
890
914
  */
891
915
  async #handleMcpPermissionChange(params) {
892
916
  const serverName = params.title;
917
+ // combine fsWrite and fsReplace into fsWrite
918
+ if (serverName === 'Built-in' && params.optionsValues?.fsWrite) {
919
+ // add fsReplace along
920
+ params.optionsValues.fsReplace = params.optionsValues.fsWrite;
921
+ }
893
922
  const updatedPermissionConfig = params.optionsValues;
894
923
  if (!serverName || !updatedPermissionConfig) {
895
924
  return { id: params.id };
@@ -941,8 +970,8 @@ class McpEventHandler {
941
970
  command: serverConfig.command,
942
971
  enabled: true,
943
972
  numTools: mcpManager_1.McpManager.instance.getAllToolsWithPermissions(serverName).length,
944
- scope: serverConfig?.__configPath__ ===
945
- (0, mcpUtils_1.getGlobalMcpConfigPath)(this.#features.workspace.fs.getUserHomeDir())
973
+ scope: serverConfig.__configPath__ ===
974
+ (0, mcpUtils_1.getGlobalAgentConfigPath)(this.#features.workspace.fs.getUserHomeDir())
946
975
  ? 'global'
947
976
  : 'workspace',
948
977
  transportType: 'stdio',
@@ -952,24 +981,24 @@ class McpEventHandler {
952
981
  // Clear the pending permission config after applying
953
982
  this.#pendingPermissionConfig = undefined;
954
983
  this.#features.logging.info(`Applied permission changes for server: ${serverName}`);
984
+ this.#isProgrammaticChange = false;
955
985
  return { id: params.id };
956
986
  }
957
987
  catch (error) {
958
988
  this.#features.logging.error(`Failed to save MCP permissions: ${error}`);
959
- return { id: params.id };
960
- }
961
- finally {
962
- // Reset flag after operations
963
989
  this.#isProgrammaticChange = false;
990
+ return { id: params.id };
964
991
  }
965
992
  }
966
993
  #emitMCPConfigEvent() {
967
994
  // Emit MCP config event after reinitialization
968
995
  const mcpManager = mcpManager_1.McpManager.instance;
969
996
  const serverConfigs = mcpManager.getAllServerConfigs();
970
- const activeServers = Array.from(serverConfigs.entries()).filter(([name, _]) => !mcpManager.isServerDisabled(name));
997
+ const activeServers = Array.from(serverConfigs.entries());
998
+ // Get the global agent path
999
+ const globalAgentPath = (0, mcpUtils_1.getGlobalAgentConfigPath)(this.#features.workspace.fs.getUserHomeDir());
971
1000
  // Count global vs project servers
972
- const globalServers = Array.from(serverConfigs.entries()).filter(([_, config]) => config?.__configPath__ === (0, mcpUtils_1.getGlobalMcpConfigPath)(this.#features.workspace.fs.getUserHomeDir())).length;
1001
+ const globalServers = Array.from(serverConfigs.entries()).filter(([_, config]) => config.__configPath__ === globalAgentPath).length;
973
1002
  const projectServers = serverConfigs.size - globalServers;
974
1003
  // Count tools by permission
975
1004
  let toolsAlwaysAllowed = 0;
@@ -995,20 +1024,16 @@ class McpEventHandler {
995
1024
  });
996
1025
  // Emit server initialize events for all active servers
997
1026
  for (const [serverName, config] of serverConfigs.entries()) {
998
- const enabled = !mcpManager.isServerDisabled(serverName);
999
- if (enabled) {
1000
- this.#telemetryController?.emitMCPServerInitializeEvent({
1001
- source: 'reload',
1002
- command: config.command,
1003
- enabled,
1004
- numTools: mcpManager.getAllToolsWithPermissions(serverName).length,
1005
- scope: config?.__configPath__ === (0, mcpUtils_1.getGlobalMcpConfigPath)(this.#features.workspace.fs.getUserHomeDir())
1006
- ? 'global'
1007
- : 'workspace',
1008
- transportType: 'stdio',
1009
- languageServerVersion: this.#features.runtime.serverInfo.version,
1010
- });
1011
- }
1027
+ // const enabled = !mcpManager.isServerDisabled(serverName)
1028
+ this.#telemetryController?.emitMCPServerInitializeEvent({
1029
+ source: 'reload',
1030
+ command: config.command,
1031
+ enabled: true,
1032
+ numTools: mcpManager.getAllToolsWithPermissions(serverName).length,
1033
+ scope: config.__configPath__ === globalAgentPath ? 'global' : 'workspace',
1034
+ transportType: 'stdio',
1035
+ languageServerVersion: this.#features.runtime.serverInfo.version,
1036
+ });
1012
1037
  }
1013
1038
  }
1014
1039
  /**
@@ -1039,50 +1064,43 @@ class McpEventHandler {
1039
1064
  }
1040
1065
  }
1041
1066
  /**
1042
- * Gets the appropriate persona path, checking workspace path first if it exists
1043
- * @returns The persona path to use (workspace if exists, otherwise global)
1067
+ * Gets the appropriate agent path, checking workspace path first if it exists
1068
+ * @returns The agent path to use (workspace if exists, otherwise global)
1044
1069
  */
1045
- async #getPersonaPath() {
1046
- const allPermissions = mcpManager_1.McpManager.instance.getAllPermissions();
1047
- for (const [, permission] of allPermissions) {
1048
- if (permission.__configPath__) {
1049
- return permission.__configPath__;
1050
- }
1070
+ async #getAgentPath(isGlobal = true) {
1071
+ if (isGlobal) {
1072
+ return (0, mcpUtils_1.getGlobalAgentConfigPath)(this.#features.workspace.fs.getUserHomeDir());
1051
1073
  }
1052
- const globalPersonaPath = (0, mcpUtils_1.getGlobalPersonaConfigPath)(this.#features.workspace.fs.getUserHomeDir());
1053
- // Get workspace folders and check for workspace persona path
1074
+ const globalAgentPath = (0, mcpUtils_1.getGlobalAgentConfigPath)(this.#features.workspace.fs.getUserHomeDir());
1075
+ // Get workspace folders and check for workspace agent path
1054
1076
  const workspaceFolders = this.#features.workspace.getAllWorkspaceFolders();
1055
1077
  if (workspaceFolders && workspaceFolders.length > 0) {
1056
1078
  const workspacePaths = workspaceFolders.map(folder => folder.uri);
1057
- const workspacePersonaPaths = (0, mcpUtils_1.getWorkspacePersonaConfigPaths)(workspacePaths);
1058
- if (Array.isArray(workspacePersonaPaths) && workspacePersonaPaths.length > 0) {
1079
+ const workspaceAgentPaths = (0, mcpUtils_1.getWorkspaceAgentConfigPaths)(workspacePaths);
1080
+ if (Array.isArray(workspaceAgentPaths) && workspaceAgentPaths.length > 0) {
1059
1081
  try {
1060
1082
  // Convert URI format to filesystem path if needed using the utility function
1061
- const personaPath = (0, mcpUtils_1.normalizePathFromUri)(workspacePersonaPaths[0], this.#features.logging);
1062
- // Check if the workspace persona path exists
1063
- const fileExists = await this.#features.workspace.fs.exists(personaPath);
1064
- if (fileExists) {
1065
- return personaPath;
1066
- }
1083
+ const agentPath = (0, mcpUtils_1.normalizePathFromUri)(workspaceAgentPaths[0], this.#features.logging);
1084
+ return agentPath;
1067
1085
  }
1068
1086
  catch (e) {
1069
- this.#features.logging.warn(`Failed to check if workspace persona path exists: ${e}`);
1087
+ this.#features.logging.warn(`Failed to check if workspace agent path exists: ${e}`);
1070
1088
  }
1071
1089
  }
1072
1090
  }
1073
1091
  // Return global path if workspace path doesn't exist or there was an error
1074
- return globalPersonaPath;
1092
+ return globalAgentPath;
1075
1093
  }
1076
1094
  /**
1077
1095
  * Processes permission updates from the UI
1078
1096
  */
1079
1097
  async #processPermissionUpdates(updatedPermissionConfig) {
1080
- // Get the appropriate persona path
1081
- const personaPath = await this.#getPersonaPath();
1098
+ // Get the appropriate agent path
1099
+ const agentPath = await this.#getAgentPath();
1082
1100
  const perm = {
1083
1101
  enabled: true,
1084
1102
  toolPerms: {},
1085
- __configPath__: personaPath,
1103
+ __configPath__: agentPath,
1086
1104
  };
1087
1105
  // Process each tool permission setting
1088
1106
  for (const [key, val] of Object.entries(updatedPermissionConfig)) {
@@ -1142,26 +1160,46 @@ class McpEventHandler {
1142
1160
  catch (e) {
1143
1161
  this.#features.logging.warn(`Failed to get user home directory: ${e}`);
1144
1162
  }
1145
- const configPaths = [
1146
- ...(0, mcpUtils_1.getWorkspaceMcpConfigPaths)(wsUris),
1147
- ...(homeDir ? [(0, mcpUtils_1.getGlobalMcpConfigPath)(homeDir)] : []),
1163
+ // Only watch agent config files
1164
+ const agentPaths = [
1165
+ ...(0, mcpUtils_1.getWorkspaceAgentConfigPaths)(wsUris),
1166
+ ...(homeDir ? [(0, mcpUtils_1.getGlobalAgentConfigPath)(homeDir)] : []),
1148
1167
  ];
1149
- const personaPaths = [
1150
- ...(0, mcpUtils_1.getWorkspacePersonaConfigPaths)(wsUris),
1151
- ...(homeDir ? [(0, mcpUtils_1.getGlobalPersonaConfigPath)(homeDir)] : []),
1152
- ];
1153
- const allPaths = [...configPaths, ...personaPaths];
1154
- this.#fileWatcher.watchPaths(allPaths, async () => {
1155
- if (this.#isProgrammaticChange) {
1156
- return;
1168
+ const allPaths = [...agentPaths];
1169
+ this.#fileWatcher.watchPaths(allPaths, () => {
1170
+ // Store the current programmatic state when the event is triggered
1171
+ this.#lastProgrammaticState = this.#isProgrammaticChange;
1172
+ // Log the values for debugging
1173
+ this.#features.logging.info(`File watcher triggered - isProgrammaticChange: ${this.#isProgrammaticChange}, ` +
1174
+ `lastProgrammaticState: ${this.#lastProgrammaticState}`);
1175
+ // Clear any existing timer
1176
+ if (this.#debounceTimer) {
1177
+ clearTimeout(this.#debounceTimer);
1157
1178
  }
1158
- await this.#handleRefreshMCPList({ id: 'refresh-mcp-list' });
1179
+ // Set a new timer with 2 second debounce
1180
+ this.#debounceTimer = setTimeout(async () => {
1181
+ // Log the values again when the timer fires
1182
+ this.#features.logging.debug(`Debounce timer fired - lastProgrammaticState: ${this.#lastProgrammaticState}`);
1183
+ // Only proceed if the stored state allows it
1184
+ if (!this.#lastProgrammaticState) {
1185
+ await this.#handleRefreshMCPList({ id: 'refresh-mcp-list' });
1186
+ }
1187
+ else {
1188
+ this.#isProgrammaticChange = false;
1189
+ this.#features.logging.debug('Skipping refresh due to programmatic change');
1190
+ }
1191
+ this.#debounceTimer = null;
1192
+ }, 2000);
1159
1193
  });
1160
1194
  }
1161
1195
  /**
1162
1196
  * Cleanup file watchers
1163
1197
  */
1164
1198
  dispose() {
1199
+ if (this.#debounceTimer) {
1200
+ clearTimeout(this.#debounceTimer);
1201
+ this.#debounceTimer = null;
1202
+ }
1165
1203
  this.#fileWatcher.close();
1166
1204
  }
1167
1205
  }