Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/services/mcp/McpHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export class McpHub {
isConnecting: boolean = false
private refCount: number = 0 // Reference counter for active clients
private configChangeDebounceTimers: Map<string, NodeJS.Timeout> = new Map()
private isProgrammaticUpdate: boolean = false

constructor(provider: ClineProvider) {
this.providerRef = new WeakRef(provider)
Expand Down Expand Up @@ -278,6 +279,11 @@ export class McpHub {
* Debounced wrapper for handling config file changes
*/
private debounceConfigChange(filePath: string, source: "global" | "project"): void {
// Skip processing if this is a programmatic update to prevent unnecessary server restarts
if (this.isProgrammaticUpdate) {
return
}

const key = `${source}-${filePath}`

// Clear existing timer if any
Expand Down Expand Up @@ -1463,7 +1469,15 @@ export class McpHub {
mcpServers: config.mcpServers,
}

await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
// Set flag to prevent file watcher from triggering server restart
this.isProgrammaticUpdate = true
try {
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
// Delay longer than debounce timer (500ms) to ensure watcher event is processed while flag is set
await delay(600)
} finally {
this.isProgrammaticUpdate = false
}
}

public async updateServerTimeout(
Expand Down Expand Up @@ -1686,7 +1700,15 @@ export class McpHub {
targetList.splice(toolIndex, 1)
}

await fs.writeFile(normalizedPath, JSON.stringify(config, null, 2))
// Set flag to prevent file watcher from triggering server restart
this.isProgrammaticUpdate = true
try {
await fs.writeFile(normalizedPath, JSON.stringify(config, null, 2))
// Delay longer than debounce timer (500ms) to ensure watcher event is processed while flag is set
await delay(600)
} finally {
this.isProgrammaticUpdate = false
}

if (connection) {
connection.server.tools = await this.fetchToolsList(serverName, source)
Expand Down Expand Up @@ -1796,6 +1818,9 @@ export class McpHub {
}
this.configChangeDebounceTimers.clear()

// Reset programmatic update flag
this.isProgrammaticUpdate = false

this.removeAllFileWatchers()
for (const connection of this.connections) {
try {
Expand Down