@aws/lsp-codewhisperer 0.0.66 → 0.0.67
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.
- package/CHANGELOG.md +27 -0
- package/README.md +5 -0
- package/out/language-server/agenticChat/agenticChatController.d.ts +2 -2
- package/out/language-server/agenticChat/agenticChatController.js +109 -34
- package/out/language-server/agenticChat/agenticChatController.js.map +1 -1
- package/out/language-server/agenticChat/constants/constants.d.ts +5 -0
- package/out/language-server/agenticChat/constants/constants.js +7 -1
- package/out/language-server/agenticChat/constants/constants.js.map +1 -1
- package/out/language-server/agenticChat/context/additionalContextProvider.d.ts +9 -1
- package/out/language-server/agenticChat/context/additionalContextProvider.js +55 -32
- package/out/language-server/agenticChat/context/additionalContextProvider.js.map +1 -1
- package/out/language-server/agenticChat/context/agenticChatTriggerContext.js +4 -0
- package/out/language-server/agenticChat/context/agenticChatTriggerContext.js.map +1 -1
- package/out/language-server/agenticChat/qAgenticChatServer.d.ts +2 -0
- package/out/language-server/agenticChat/qAgenticChatServer.js +18 -2
- package/out/language-server/agenticChat/qAgenticChatServer.js.map +1 -1
- package/out/language-server/agenticChat/tools/chatDb/chatDb.js +14 -2
- package/out/language-server/agenticChat/tools/chatDb/chatDb.js.map +1 -1
- package/out/language-server/agenticChat/tools/chatDb/util.d.ts +11 -1
- package/out/language-server/agenticChat/tools/chatDb/util.js +17 -0
- package/out/language-server/agenticChat/tools/chatDb/util.js.map +1 -1
- package/out/language-server/agenticChat/tools/executeBash.d.ts +0 -5
- package/out/language-server/agenticChat/tools/executeBash.js +11 -14
- package/out/language-server/agenticChat/tools/executeBash.js.map +1 -1
- package/out/language-server/agenticChat/tools/grepSearch.js +3 -1
- package/out/language-server/agenticChat/tools/grepSearch.js.map +1 -1
- package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.js +208 -170
- package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.js.map +1 -1
- package/out/language-server/agenticChat/tools/mcp/mcpManager.d.ts +12 -18
- package/out/language-server/agenticChat/tools/mcp/mcpManager.js +353 -216
- package/out/language-server/agenticChat/tools/mcp/mcpManager.js.map +1 -1
- package/out/language-server/agenticChat/tools/mcp/mcpTypes.d.ts +29 -0
- package/out/language-server/agenticChat/tools/mcp/mcpTypes.js +60 -1
- package/out/language-server/agenticChat/tools/mcp/mcpTypes.js.map +1 -1
- package/out/language-server/agenticChat/tools/mcp/mcpUtils.d.ts +27 -4
- package/out/language-server/agenticChat/tools/mcp/mcpUtils.js +464 -2
- package/out/language-server/agenticChat/tools/mcp/mcpUtils.js.map +1 -1
- package/out/language-server/agenticChat/tools/toolServer.js +8 -10
- package/out/language-server/agenticChat/tools/toolServer.js.map +1 -1
- package/out/language-server/agenticChat/tools/toolShared.js +6 -2
- package/out/language-server/agenticChat/tools/toolShared.js.map +1 -1
- package/out/language-server/chat/chatController.d.ts +1 -1
- package/out/language-server/chat/chatController.js.map +1 -1
- package/out/language-server/chat/contexts/triggerContext.js +11 -2
- package/out/language-server/chat/contexts/triggerContext.js.map +1 -1
- package/out/language-server/configuration/qConfigurationServer.d.ts +2 -0
- package/out/language-server/configuration/qConfigurationServer.js.map +1 -1
- package/out/language-server/inline-completion/auto-trigger/autoTrigger.d.ts +1 -0
- package/out/language-server/inline-completion/auto-trigger/autoTrigger.js +36 -0
- package/out/language-server/inline-completion/auto-trigger/autoTrigger.js.map +1 -1
- package/out/language-server/inline-completion/codeWhispererServer.js +25 -36
- package/out/language-server/inline-completion/codeWhispererServer.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.d.ts +4 -1
- package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.js +36 -2
- package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyEventBundler.d.ts +11 -5
- package/out/language-server/workspaceContext/dependency/dependencyEventBundler.js +37 -10
- package/out/language-server/workspaceContext/dependency/dependencyEventBundler.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.d.ts +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.js +8 -3
- package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/JavaDependencyHandler.js +3 -4
- package/out/language-server/workspaceContext/dependency/dependencyHandler/JavaDependencyHandler.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.d.ts +4 -3
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js +5 -9
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/PythonDependencyHandler.js +3 -4
- package/out/language-server/workspaceContext/dependency/dependencyHandler/PythonDependencyHandler.js.map +1 -1
- package/out/language-server/workspaceContext/workspaceContextServer.js +12 -8
- package/out/language-server/workspaceContext/workspaceContextServer.js.map +1 -1
- package/out/shared/amazonQServiceManager/configurationUtils.d.ts +4 -0
- package/out/shared/amazonQServiceManager/configurationUtils.js +6 -0
- package/out/shared/amazonQServiceManager/configurationUtils.js.map +1 -1
- package/out/shared/streamingClientService.js +12 -1
- package/out/shared/streamingClientService.js.map +1 -1
- package/out/shared/utils.d.ts +1 -7
- package/out/shared/utils.js +39 -36
- package/out/shared/utils.js.map +1 -1
- package/package.json +2 -3
|
@@ -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
|
|
93
|
+
const builtInToolNames = new Set(this.#features.agent.getBuiltInToolNames());
|
|
92
94
|
const builtInTools = allTools
|
|
93
|
-
.filter(tool =>
|
|
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.
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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,
|
|
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,
|
|
569
|
+
await mcpManager_1.McpManager.instance.addServer(serverName, config, agentPath);
|
|
570
570
|
this.#newlyAddedServers.add(serverName);
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
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
|
|
629
|
+
const builtInToolNames = new Set(this.#features.agent.getBuiltInToolNames());
|
|
630
|
+
// combine fsWrite and fsReplace into fsWrite
|
|
630
631
|
const builtInTools = allTools
|
|
631
|
-
.filter(tool =>
|
|
632
|
+
.filter(tool => {
|
|
633
|
+
return (builtInToolNames.has(tool.toolSpecification.name) && tool.toolSpecification.name !== 'fsReplace');
|
|
634
|
+
})
|
|
632
635
|
.map(tool => {
|
|
633
|
-
|
|
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.
|
|
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
|
|
693
|
-
const
|
|
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__:
|
|
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
|
-
|
|
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
|
|
723
|
-
const
|
|
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__:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
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
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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
|
|
945
|
-
(0, mcpUtils_1.
|
|
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())
|
|
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
|
|
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
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
|
1043
|
-
* @returns The
|
|
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 #
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
|
1053
|
-
// Get workspace folders and check for workspace
|
|
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
|
|
1058
|
-
if (Array.isArray(
|
|
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
|
|
1062
|
-
|
|
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
|
|
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
|
|
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
|
|
1081
|
-
const
|
|
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__:
|
|
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
|
-
|
|
1146
|
-
|
|
1147
|
-
...(
|
|
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
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
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
|
-
|
|
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
|
}
|