Skip to content

Commit 888d809

Browse files
committed
optimize McpHub.ts
1 parent 7c96acc commit 888d809

File tree

2 files changed

+106
-17
lines changed

2 files changed

+106
-17
lines changed

src/services/mcp/McpHub.ts

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,59 @@ export class McpHub {
113113
this.watchMcpSettingsFile()
114114
this.watchProjectMcpFile()
115115
this.setupWorkspaceFoldersWatcher()
116-
this.initializeMcpServers()
116+
this.initializeGlobalMcpServers()
117+
this.initializeProjectMcpServers()
118+
}
119+
120+
public setupWorkspaceFoldersWatcher(): void {
121+
// Skip if test environment is detected
122+
if (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== undefined) {
123+
return
124+
}
125+
this.disposables.push(
126+
vscode.workspace.onDidChangeWorkspaceFolders(async () => {
127+
await this.updateProjectMcpServers()
128+
this.watchProjectMcpFile()
129+
}),
130+
)
131+
}
132+
133+
private watchProjectMcpFile(): void {
134+
this.projectMcpWatcher?.dispose()
135+
136+
this.projectMcpWatcher = vscode.workspace.createFileSystemWatcher("**/.roo/mcp.json", false, false, false)
137+
138+
this.disposables.push(
139+
this.projectMcpWatcher.onDidChange(async () => {
140+
await this.updateProjectMcpServers()
141+
}),
142+
this.projectMcpWatcher.onDidCreate(async () => {
143+
await this.updateProjectMcpServers()
144+
}),
145+
this.projectMcpWatcher.onDidDelete(async () => {
146+
await this.cleanupProjectMcpServers()
147+
}),
148+
)
149+
150+
this.disposables.push(this.projectMcpWatcher)
151+
}
152+
153+
private async updateProjectMcpServers(): Promise<void> {
154+
// Only clean up and initialize project servers, not affecting global servers
155+
await this.cleanupProjectMcpServers()
156+
await this.initializeProjectMcpServers()
157+
}
158+
159+
private async cleanupProjectMcpServers(): Promise<void> {
160+
// Only filter and delete project servers
161+
const projectServers = this.connections.filter((conn) => conn.server.source === "project")
162+
163+
for (const conn of projectServers) {
164+
await this.deleteConnection(conn.server.name)
165+
}
166+
167+
// Notify webview of changes after cleanup
168+
await this.notifyWebviewOfServerChanges()
117169
}
118170

119171
/**
@@ -301,7 +353,8 @@ export class McpHub {
301353
return
302354
}
303355
try {
304-
await this.updateServerConnections(result.data.mcpServers || {})
356+
// Only update global servers when global settings change
357+
await this.updateServerConnections(result.data.mcpServers || {}, "global")
305358
} catch (error) {
306359
this.showErrorMessage("Failed to process MCP settings change", error)
307360
}
@@ -310,9 +363,9 @@ export class McpHub {
310363
)
311364
}
312365

313-
private async initializeMcpServers(): Promise<void> {
366+
private async initializeGlobalMcpServers(): Promise<void> {
314367
try {
315-
// 1. Initialize global MCP servers
368+
// Initialize global MCP servers
316369
const settingsPath = await this.getMcpSettingsFilePath()
317370
const content = await fs.readFile(settingsPath, "utf-8")
318371
let config: any
@@ -343,13 +396,13 @@ export class McpHub {
343396

344397
// Still try to connect with the raw config, but show warnings
345398
try {
346-
await this.updateServerConnections(config.mcpServers || {})
399+
await this.updateServerConnections(config.mcpServers || {}, "global")
347400
} catch (error) {
348-
this.showErrorMessage("Failed to initialize MCP servers with raw config", error)
401+
this.showErrorMessage("Failed to initialize global MCP servers with raw config", error)
349402
}
350403
}
351404
} catch (error) {
352-
this.showErrorMessage("Failed to initialize MCP servers", error)
405+
this.showErrorMessage("Failed to initialize global MCP servers", error)
353406
}
354407
}
355408

@@ -637,7 +690,12 @@ export class McpHub {
637690

638691
// Update or add servers
639692
for (const [name, config] of Object.entries(newServers)) {
640-
const currentConnection = this.connections.find((conn) => conn.server.name === name)
693+
// Only consider connections that match the current source
694+
const currentConnection = this.connections.find(
695+
(conn) =>
696+
conn.server.name === name &&
697+
(conn.server.source === source || (!conn.server.source && source === "global")),
698+
)
641699

642700
// Validate and transform the config
643701
let validatedConfig: z.infer<typeof ServerConfigSchema>
@@ -740,20 +798,49 @@ export class McpHub {
740798
}
741799

742800
private async notifyWebviewOfServerChanges(): Promise<void> {
743-
// servers should always be sorted in the order they are defined in the settings file
801+
// Get global server order from settings file
744802
const settingsPath = await this.getMcpSettingsFilePath()
745803
const content = await fs.readFile(settingsPath, "utf-8")
746804
const config = JSON.parse(content)
747-
const serverOrder = Object.keys(config.mcpServers || {})
805+
const globalServerOrder = Object.keys(config.mcpServers || {})
806+
807+
// Get project server order if available
808+
const projectMcpPath = await this.getProjectMcpPath()
809+
let projectServerOrder: string[] = []
810+
if (projectMcpPath) {
811+
try {
812+
const projectContent = await fs.readFile(projectMcpPath, "utf-8")
813+
const projectConfig = JSON.parse(projectContent)
814+
projectServerOrder = Object.keys(projectConfig.mcpServers || {})
815+
} catch (error) {
816+
console.error("Failed to read project MCP config:", error)
817+
}
818+
}
819+
820+
// Sort connections: first global servers in their defined order, then project servers in their defined order
821+
const sortedConnections = [...this.connections].sort((a, b) => {
822+
const aIsGlobal = a.server.source === "global" || !a.server.source
823+
const bIsGlobal = b.server.source === "global" || !b.server.source
824+
825+
// If both are global or both are project, sort by their respective order
826+
if (aIsGlobal && bIsGlobal) {
827+
const indexA = globalServerOrder.indexOf(a.server.name)
828+
const indexB = globalServerOrder.indexOf(b.server.name)
829+
return indexA - indexB
830+
} else if (!aIsGlobal && !bIsGlobal) {
831+
const indexA = projectServerOrder.indexOf(a.server.name)
832+
const indexB = projectServerOrder.indexOf(b.server.name)
833+
return indexA - indexB
834+
}
835+
836+
// Global servers come before project servers
837+
return aIsGlobal ? -1 : 1
838+
})
839+
840+
// Send sorted servers to webview
748841
await this.providerRef.deref()?.postMessageToWebview({
749842
type: "mcpServers",
750-
mcpServers: [...this.connections]
751-
.sort((a, b) => {
752-
const indexA = serverOrder.indexOf(a.server.name)
753-
const indexB = serverOrder.indexOf(b.server.name)
754-
return indexA - indexB
755-
})
756-
.map((connection) => connection.server),
843+
mcpServers: sortedConnections.map((connection) => connection.server),
757844
})
758845
}
759846

webview-ui/src/i18n/locales/vi/mcp.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"title": "Bật tạo máy chủ MCP",
1111
"description": "Khi được bật, Roo có thể giúp bạn tạo máy chủ MCP mới thông qua các lệnh như \"thêm công cụ mới để...\". Nếu bạn không cần tạo máy chủ MCP, bạn có thể tắt tính năng này để giảm lượng token mà Roo sử dụng."
1212
},
13+
"editGlobalMCP": "Chỉnh sửa MCP toàn cục",
14+
"editProjectMCP": "Chỉnh sửa MCP dự án",
1315
"editSettings": "Chỉnh sửa cài đặt MCP",
1416
"tool": {
1517
"alwaysAllow": "Luôn cho phép",

0 commit comments

Comments
 (0)