Skip to content

Commit ca27bbb

Browse files
committed
fix: prevent MCP server restart when toggling tool permissions
- Added isProgrammaticUpdate flag to track when config changes are made programmatically - Modified debounceConfigChange to skip file watcher triggers during programmatic updates - Updated updateServerToolList to set the flag when writing config changes - Updated updateServerConfig to handle minor updates (alwaysAllow, disabledTools, timeout) without triggering restarts - Added small delay after programmatic writes to ensure file watchers process the flag This prevents unnecessary server restarts when users toggle the 'Always allow' checkbox for MCP tools, improving the user experience by maintaining active connections. Fixes #8231
1 parent 0e1b23d commit ca27bbb

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

src/services/mcp/McpHub.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export class McpHub {
151151
isConnecting: boolean = false
152152
private refCount: number = 0 // Reference counter for active clients
153153
private configChangeDebounceTimers: Map<string, NodeJS.Timeout> = new Map()
154+
private isProgrammaticUpdate: boolean = false // Flag to track programmatic config updates
154155

155156
constructor(provider: ClineProvider) {
156157
this.providerRef = new WeakRef(provider)
@@ -278,6 +279,11 @@ export class McpHub {
278279
* Debounced wrapper for handling config file changes
279280
*/
280281
private debounceConfigChange(filePath: string, source: "global" | "project"): void {
282+
// Skip if this is a programmatic update
283+
if (this.isProgrammaticUpdate) {
284+
return
285+
}
286+
281287
const key = `${source}-${filePath}`
282288

283289
// Clear existing timer if any
@@ -1463,7 +1469,28 @@ export class McpHub {
14631469
mcpServers: config.mcpServers,
14641470
}
14651471

1466-
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
1472+
// Check if this is a minor update that shouldn't trigger a restart
1473+
const isMinorUpdate = Object.keys(configUpdate).every(
1474+
(key) => key === "alwaysAllow" || key === "disabledTools" || key === "timeout",
1475+
)
1476+
1477+
if (isMinorUpdate) {
1478+
// Set flag to indicate programmatic update
1479+
this.isProgrammaticUpdate = true
1480+
1481+
try {
1482+
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
1483+
1484+
// Give file system watchers a moment to process
1485+
await new Promise((resolve) => setTimeout(resolve, 100))
1486+
} finally {
1487+
// Always reset the flag
1488+
this.isProgrammaticUpdate = false
1489+
}
1490+
} else {
1491+
// For major updates, allow normal file watcher behavior
1492+
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
1493+
}
14671494
}
14681495

14691496
public async updateServerTimeout(
@@ -1686,7 +1713,18 @@ export class McpHub {
16861713
targetList.splice(toolIndex, 1)
16871714
}
16881715

1689-
await fs.writeFile(normalizedPath, JSON.stringify(config, null, 2))
1716+
// Set flag to indicate programmatic update
1717+
this.isProgrammaticUpdate = true
1718+
1719+
try {
1720+
await fs.writeFile(normalizedPath, JSON.stringify(config, null, 2))
1721+
1722+
// Give file system watchers a moment to process
1723+
await new Promise((resolve) => setTimeout(resolve, 100))
1724+
} finally {
1725+
// Always reset the flag
1726+
this.isProgrammaticUpdate = false
1727+
}
16901728

16911729
if (connection) {
16921730
connection.server.tools = await this.fetchToolsList(serverName, source)

0 commit comments

Comments
 (0)