Skip to content

Commit 8f4145e

Browse files
author
aheizi
committed
optimize McpHub.ts
1 parent c948e81 commit 8f4145e

File tree

1 file changed

+71
-28
lines changed

1 file changed

+71
-28
lines changed

src/services/mcp/McpHub.ts

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ export class McpHub {
112112
this.watchMcpSettingsFile()
113113
this.watchProjectMcpFile()
114114
this.setupWorkspaceFoldersWatcher()
115-
this.initializeMcpServers()
115+
this.initializeGlobalMcpServers()
116+
this.initializeProjectMcpServers()
116117
}
117118

118119
public setupWorkspaceFoldersWatcher(): void {
@@ -149,17 +150,20 @@ export class McpHub {
149150
}
150151

151152
private async updateProjectMcpServers(): Promise<void> {
153+
// Only clean up and initialize project servers, not affecting global servers
152154
await this.cleanupProjectMcpServers()
153155
await this.initializeProjectMcpServers()
154156
}
155157

156158
private async cleanupProjectMcpServers(): Promise<void> {
159+
// Only filter and delete project servers
157160
const projectServers = this.connections.filter((conn) => conn.server.source === "project")
158161

159162
for (const conn of projectServers) {
160163
await this.deleteConnection(conn.server.name)
161164
}
162165

166+
// Notify webview of changes after cleanup
163167
await this.notifyWebviewOfServerChanges()
164168
}
165169

@@ -299,7 +303,8 @@ export class McpHub {
299303
return
300304
}
301305
try {
302-
await this.updateServerConnections(result.data.mcpServers || {})
306+
// Only update global servers when global settings change
307+
await this.updateServerConnections(result.data.mcpServers || {}, "global")
303308
} catch (error) {
304309
this.showErrorMessage("Failed to process MCP settings change", error)
305310
}
@@ -308,9 +313,9 @@ export class McpHub {
308313
)
309314
}
310315

311-
private async initializeMcpServers(): Promise<void> {
316+
private async initializeGlobalMcpServers(): Promise<void> {
312317
try {
313-
// 1. Initialize global MCP servers
318+
// Initialize global MCP servers
314319
const settingsPath = await this.getMcpSettingsFilePath()
315320
const content = await fs.readFile(settingsPath, "utf-8")
316321
let config: any
@@ -340,15 +345,12 @@ export class McpHub {
340345
// Still try to connect with the raw config, but show warnings
341346
try {
342347
await this.updateServerConnections(config.mcpServers || {}, "global")
343-
344-
// 2. Initialize project-level MCP servers
345-
await this.initializeProjectMcpServers()
346348
} catch (error) {
347-
this.showErrorMessage("Failed to initialize MCP servers with raw config", error)
349+
this.showErrorMessage("Failed to initialize global MCP servers with raw config", error)
348350
}
349351
}
350352
} catch (error) {
351-
this.showErrorMessage("Failed to initialize MCP servers", error)
353+
this.showErrorMessage("Failed to initialize global MCP servers", error)
352354
}
353355
}
354356

@@ -454,15 +456,22 @@ export class McpHub {
454456
const stderrStream = transport.stderr
455457
if (stderrStream) {
456458
stderrStream.on("data", async (data: Buffer) => {
457-
const errorOutput = data.toString()
458-
console.error(`Server "${name}" stderr:`, errorOutput)
459-
const connection = this.connections.find((conn) => conn.server.name === name)
460-
if (connection) {
461-
// NOTE: we do not set server status to "disconnected" because stderr logs do not necessarily mean the server crashed or disconnected, it could just be informational. In fact when the server first starts up, it immediately logs "<name> server running on stdio" to stderr.
462-
this.appendErrorMessage(connection, errorOutput)
463-
// Only need to update webview right away if it's already disconnected
464-
if (connection.server.status === "disconnected") {
465-
await this.notifyWebviewOfServerChanges()
459+
const output = data.toString()
460+
461+
// Check if this is a startup info message or a real error
462+
const isStartupInfo = output.includes("server running") || output.includes("MCP server running")
463+
464+
if (!isStartupInfo) {
465+
// Only log and process real errors, ignore startup info messages
466+
console.error(`Server "${name}" stderr:`, output)
467+
const connection = this.connections.find((conn) => conn.server.name === name)
468+
if (connection) {
469+
// NOTE: we do not set server status to "disconnected" because stderr logs do not necessarily mean the server crashed or disconnected
470+
this.appendErrorMessage(connection, output)
471+
// Only need to update webview right away if it's already disconnected
472+
if (connection.server.status === "disconnected") {
473+
await this.notifyWebviewOfServerChanges()
474+
}
466475
}
467476
}
468477
})
@@ -630,7 +639,12 @@ export class McpHub {
630639

631640
// Update or add servers
632641
for (const [name, config] of Object.entries(newServers)) {
633-
const currentConnection = this.connections.find((conn) => conn.server.name === name)
642+
// Only consider connections that match the current source
643+
const currentConnection = this.connections.find(
644+
(conn) =>
645+
conn.server.name === name &&
646+
(conn.server.source === source || (!conn.server.source && source === "global")),
647+
)
634648

635649
// Validate and transform the config
636650
let validatedConfig: z.infer<typeof ServerConfigSchema>
@@ -735,20 +749,49 @@ export class McpHub {
735749
}
736750

737751
private async notifyWebviewOfServerChanges(): Promise<void> {
738-
// servers should always be sorted in the order they are defined in the settings file
752+
// Get global server order from settings file
739753
const settingsPath = await this.getMcpSettingsFilePath()
740754
const content = await fs.readFile(settingsPath, "utf-8")
741755
const config = JSON.parse(content)
742-
const serverOrder = Object.keys(config.mcpServers || {})
756+
const globalServerOrder = Object.keys(config.mcpServers || {})
757+
758+
// Get project server order if available
759+
const projectMcpPath = await this.getProjectMcpPath()
760+
let projectServerOrder: string[] = []
761+
if (projectMcpPath) {
762+
try {
763+
const projectContent = await fs.readFile(projectMcpPath, "utf-8")
764+
const projectConfig = JSON.parse(projectContent)
765+
projectServerOrder = Object.keys(projectConfig.mcpServers || {})
766+
} catch (error) {
767+
console.error("Failed to read project MCP config:", error)
768+
}
769+
}
770+
771+
// Sort connections: first global servers in their defined order, then project servers in their defined order
772+
const sortedConnections = [...this.connections].sort((a, b) => {
773+
const aIsGlobal = a.server.source === "global" || !a.server.source
774+
const bIsGlobal = b.server.source === "global" || !b.server.source
775+
776+
// If both are global or both are project, sort by their respective order
777+
if (aIsGlobal && bIsGlobal) {
778+
const indexA = globalServerOrder.indexOf(a.server.name)
779+
const indexB = globalServerOrder.indexOf(b.server.name)
780+
return indexA - indexB
781+
} else if (!aIsGlobal && !bIsGlobal) {
782+
const indexA = projectServerOrder.indexOf(a.server.name)
783+
const indexB = projectServerOrder.indexOf(b.server.name)
784+
return indexA - indexB
785+
}
786+
787+
// Global servers come before project servers
788+
return aIsGlobal ? -1 : 1
789+
})
790+
791+
// Send sorted servers to webview
743792
await this.providerRef.deref()?.postMessageToWebview({
744793
type: "mcpServers",
745-
mcpServers: [...this.connections]
746-
.sort((a, b) => {
747-
const indexA = serverOrder.indexOf(a.server.name)
748-
const indexB = serverOrder.indexOf(b.server.name)
749-
return indexA - indexB
750-
})
751-
.map((connection) => connection.server),
794+
mcpServers: sortedConnections.map((connection) => connection.server),
752795
})
753796
}
754797

0 commit comments

Comments
 (0)