Skip to content

Commit 208909b

Browse files
authored
fix(amazonq): fix for mcp server unnecessary refresh from file watchers (aws#1933)
1 parent be3231f commit 208909b

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

chat-client/src/client/mcpMynahUi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ export class McpMynahUi {
508508
},
509509
onClose: () => {
510510
this.messager.onMcpServerClick(MCP_IDS.SAVE_PERMISSION_CHANGE)
511+
this.isMcpServersListActive = false
511512
},
512513
onBackClick: () => {
513514
this.messager.onMcpServerClick(MCP_IDS.SAVE_PERMISSION_CHANGE)

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpEventHandler.ts

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export class McpEventHandler {
4646
#newlyAddedServers: Set<string> = new Set()
4747
#fileWatcher: ChokidarFileWatcher
4848
#isProgrammaticChange: boolean = false
49+
#debounceTimer: NodeJS.Timeout | null = null
50+
#lastProgrammaticState: boolean = false
4951

5052
constructor(features: Features, telemetryService: TelemetryService) {
5153
this.#features = features
@@ -665,9 +667,8 @@ export class McpEventHandler {
665667
await McpManager.instance.addServer(serverName, config, configPath, personaPath)
666668
this.#newlyAddedServers.add(serverName)
667669
}
668-
} finally {
669-
// Reset flag after operations
670-
this.#isProgrammaticChange = false
670+
} catch (error) {
671+
this.#features.logging.error(`Failed to enable MCP server: ${error}`)
671672
}
672673

673674
this.#currentEditingServerName = undefined
@@ -694,6 +695,7 @@ export class McpEventHandler {
694695
}
695696

696697
// Stay on add/edit page and show error to user
698+
// Keep isProgrammaticChange true during error handling to prevent file watcher triggers
697699
if (isEditMode) {
698700
params.id = 'edit-mcp'
699701
params.title = sanitizedServerName
@@ -708,6 +710,8 @@ export class McpEventHandler {
708710
this.#newlyAddedServers.delete(serverName)
709711
}
710712

713+
this.#isProgrammaticChange = false
714+
711715
// Go to tools permissions page
712716
return this.#handleOpenMcpServer({ id: 'open-mcp-server', title: sanitizedServerName })
713717
}
@@ -812,10 +816,8 @@ export class McpEventHandler {
812816
this.#emitMCPConfigEvent()
813817
} catch (error) {
814818
this.#features.logging.error(`Failed to enable MCP server: ${error}`)
815-
} finally {
816-
// Reset flag after operations
817-
this.#isProgrammaticChange = false
818819
}
820+
this.#isProgrammaticChange = false
819821
return { id: params.id }
820822
}
821823

@@ -845,11 +847,9 @@ export class McpEventHandler {
845847
this.#emitMCPConfigEvent()
846848
} catch (error) {
847849
this.#features.logging.error(`Failed to disable MCP server: ${error}`)
848-
} finally {
849-
// Reset flag after operations
850-
this.#isProgrammaticChange = false
851850
}
852851

852+
this.#isProgrammaticChange = false
853853
return { id: params.id }
854854
}
855855

@@ -867,23 +867,25 @@ export class McpEventHandler {
867867

868868
try {
869869
await McpManager.instance.removeServer(serverName)
870+
871+
return { id: params.id }
870872
} catch (error) {
871873
this.#features.logging.error(`Failed to delete MCP server: ${error}`)
872-
} finally {
873-
// Reset flag after operations
874874
this.#isProgrammaticChange = false
875+
return { id: params.id }
875876
}
876-
877-
return { id: params.id }
878877
}
879878

880879
/**
881880
* Handles edit MCP configuration
882881
*/
883882
async #handleEditMcpServer(params: McpServerClickParams, error?: string) {
883+
// Set programmatic change flag to true to prevent file watcher triggers
884+
this.#isProgrammaticChange = true
884885
await this.#handleSavePermissionChange({ id: 'save-mcp-permission' })
885886
const serverName = params.title
886887
if (!serverName) {
888+
this.#isProgrammaticChange = false
887889
return { id: params.id }
888890
}
889891
this.#currentEditingServerName = serverName
@@ -1101,13 +1103,12 @@ export class McpEventHandler {
11011103
this.#pendingPermissionConfig = undefined
11021104

11031105
this.#features.logging.info(`Applied permission changes for server: ${serverName}`)
1106+
this.#isProgrammaticChange = false
11041107
return { id: params.id }
11051108
} catch (error) {
11061109
this.#features.logging.error(`Failed to save MCP permissions: ${error}`)
1107-
return { id: params.id }
1108-
} finally {
1109-
// Reset flag after operations
11101110
this.#isProgrammaticChange = false
1111+
return { id: params.id }
11111112
}
11121113
}
11131114

@@ -1331,18 +1332,48 @@ export class McpEventHandler {
13311332

13321333
const allPaths = [...configPaths, ...personaPaths]
13331334

1334-
this.#fileWatcher.watchPaths(allPaths, async () => {
1335-
if (this.#isProgrammaticChange) {
1336-
return
1335+
this.#fileWatcher.watchPaths(allPaths, () => {
1336+
// Store the current programmatic state when the event is triggered
1337+
this.#lastProgrammaticState = this.#isProgrammaticChange
1338+
1339+
// Log the values for debugging
1340+
this.#features.logging.info(
1341+
`File watcher triggered - isProgrammaticChange: ${this.#isProgrammaticChange}, ` +
1342+
`lastProgrammaticState: ${this.#lastProgrammaticState}`
1343+
)
1344+
1345+
// Clear any existing timer
1346+
if (this.#debounceTimer) {
1347+
clearTimeout(this.#debounceTimer)
13371348
}
1338-
await this.#handleRefreshMCPList({ id: 'refresh-mcp-list' })
1349+
1350+
// Set a new timer with 2 second debounce
1351+
this.#debounceTimer = setTimeout(async () => {
1352+
// Log the values again when the timer fires
1353+
this.#features.logging.debug(
1354+
`Debounce timer fired - lastProgrammaticState: ${this.#lastProgrammaticState}`
1355+
)
1356+
1357+
// Only proceed if the stored state allows it
1358+
if (!this.#lastProgrammaticState) {
1359+
await this.#handleRefreshMCPList({ id: 'refresh-mcp-list' })
1360+
} else {
1361+
this.#isProgrammaticChange = false
1362+
this.#features.logging.debug('Skipping refresh due to programmatic change')
1363+
}
1364+
this.#debounceTimer = null
1365+
}, 2000)
13391366
})
13401367
}
13411368

13421369
/**
13431370
* Cleanup file watchers
13441371
*/
13451372
dispose(): void {
1373+
if (this.#debounceTimer) {
1374+
clearTimeout(this.#debounceTimer)
1375+
this.#debounceTimer = null
1376+
}
13461377
this.#fileWatcher.close()
13471378
}
13481379
}

0 commit comments

Comments
 (0)