Skip to content

Commit a898f12

Browse files
committed
fix project mcp
1 parent 51dcc5b commit a898f12

File tree

1 file changed

+185
-108
lines changed

1 file changed

+185
-108
lines changed

src/services/mcp/McpHub.ts

Lines changed: 185 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,6 @@ export class McpHub {
332332
const result = McpSettingsSchema.safeParse(config)
333333
if (result.success) {
334334
await this.updateServerConnections(result.data.mcpServers || {})
335-
336-
// 2. Initialize project-level MCP servers
337-
await this.initializeProjectMcpServers()
338335
} else {
339336
// Format validation errors for better user feedback
340337
const errorMessages = result.error.errors
@@ -555,15 +552,40 @@ export class McpHub {
555552

556553
private async fetchToolsList(serverName: string): Promise<McpTool[]> {
557554
try {
558-
const response = await this.connections
559-
.find((conn) => conn.server.name === serverName)
560-
?.client.request({ method: "tools/list" }, ListToolsResultSchema)
555+
const connection = this.connections.find((conn) => conn.server.name === serverName)
556+
if (!connection) {
557+
throw new Error(`Server ${serverName} not found`)
558+
}
561559

562-
// Get always allow settings
563-
const settingsPath = await this.getMcpSettingsFilePath()
564-
const content = await fs.readFile(settingsPath, "utf-8")
565-
const config = JSON.parse(content)
566-
const alwaysAllowConfig = config.mcpServers[serverName]?.alwaysAllow || []
560+
const response = await connection.client.request({ method: "tools/list" }, ListToolsResultSchema)
561+
562+
// Determine which config file to read based on server source
563+
const isProjectServer = connection.server.source === "project"
564+
let configPath: string
565+
let alwaysAllowConfig: string[] = []
566+
567+
// Read from the appropriate config file
568+
try {
569+
if (isProjectServer) {
570+
// Get project MCP config path
571+
const projectMcpPath = await this.getProjectMcpPath()
572+
if (projectMcpPath) {
573+
configPath = projectMcpPath
574+
const content = await fs.readFile(configPath, "utf-8")
575+
const config = JSON.parse(content)
576+
alwaysAllowConfig = config.mcpServers?.[serverName]?.alwaysAllow || []
577+
}
578+
} else {
579+
// Get global MCP settings path
580+
configPath = await this.getMcpSettingsFilePath()
581+
const content = await fs.readFile(configPath, "utf-8")
582+
const config = JSON.parse(content)
583+
alwaysAllowConfig = config.mcpServers?.[serverName]?.alwaysAllow || []
584+
}
585+
} catch (error) {
586+
console.error(`Failed to read alwaysAllow config for ${serverName}:`, error)
587+
// Continue with empty alwaysAllowConfig
588+
}
567589

568590
// Mark tools as always allowed based on settings
569591
const tools = (response?.tools || []).map((tool) => ({
@@ -574,7 +596,7 @@ export class McpHub {
574596
console.log(`[MCP] Fetched tools for ${serverName}:`, tools)
575597
return tools
576598
} catch (error) {
577-
// console.error(`Failed to fetch tools for ${serverName}:`, error)
599+
console.error(`Failed to fetch tools for ${serverName}:`, error)
578600
return []
579601
}
580602
}
@@ -794,115 +816,120 @@ export class McpHub {
794816
}
795817

796818
public async toggleServerDisabled(serverName: string, disabled: boolean): Promise<void> {
797-
let settingsPath: string
798819
try {
799-
settingsPath = await this.getMcpSettingsFilePath()
800-
801-
// Ensure the settings file exists and is accessible
802-
try {
803-
await fs.access(settingsPath)
804-
} catch (error) {
805-
console.error("Settings file not accessible:", error)
806-
throw new Error("Settings file not accessible")
807-
}
808-
const content = await fs.readFile(settingsPath, "utf-8")
809-
const config = JSON.parse(content)
810-
811-
// Validate the config structure
812-
if (!config || typeof config !== "object") {
813-
throw new Error("Invalid config structure")
814-
}
815-
816-
if (!config.mcpServers || typeof config.mcpServers !== "object") {
817-
config.mcpServers = {}
820+
// Find the connection to determine if it's a global or project server
821+
const connection = this.connections.find((conn) => conn.server.name === serverName)
822+
if (!connection) {
823+
throw new Error(`Server ${serverName} not found`)
818824
}
819825

820-
if (config.mcpServers[serverName]) {
821-
// Create a new server config object to ensure clean structure
822-
const serverConfig = {
823-
...config.mcpServers[serverName],
824-
disabled,
825-
}
826-
827-
// Ensure required fields exist
828-
if (!serverConfig.alwaysAllow) {
829-
serverConfig.alwaysAllow = []
830-
}
831-
832-
config.mcpServers[serverName] = serverConfig
833-
834-
// Write the entire config back
835-
const updatedConfig = {
836-
mcpServers: config.mcpServers,
837-
}
838-
839-
await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2))
826+
// Update the server config in the appropriate file
827+
await this.updateServerConfig(serverName, { disabled }, connection.server.source || "global")
840828

841-
const connection = this.connections.find((conn) => conn.server.name === serverName)
842-
if (connection) {
843-
try {
844-
connection.server.disabled = disabled
829+
// Update the connection object
830+
if (connection) {
831+
try {
832+
connection.server.disabled = disabled
845833

846-
// Only refresh capabilities if connected
847-
if (connection.server.status === "connected") {
848-
connection.server.tools = await this.fetchToolsList(serverName)
849-
connection.server.resources = await this.fetchResourcesList(serverName)
850-
connection.server.resourceTemplates = await this.fetchResourceTemplatesList(serverName)
851-
}
852-
} catch (error) {
853-
console.error(`Failed to refresh capabilities for ${serverName}:`, error)
834+
// Only refresh capabilities if connected
835+
if (connection.server.status === "connected") {
836+
connection.server.tools = await this.fetchToolsList(serverName)
837+
connection.server.resources = await this.fetchResourcesList(serverName)
838+
connection.server.resourceTemplates = await this.fetchResourceTemplatesList(serverName)
854839
}
840+
} catch (error) {
841+
console.error(`Failed to refresh capabilities for ${serverName}:`, error)
855842
}
856-
857-
await this.notifyWebviewOfServerChanges()
858843
}
844+
845+
await this.notifyWebviewOfServerChanges()
859846
} catch (error) {
860847
this.showErrorMessage(`Failed to update server ${serverName} state`, error)
861848
throw error
862849
}
863850
}
864851

865-
public async updateServerTimeout(serverName: string, timeout: number): Promise<void> {
866-
let settingsPath: string
852+
/**
853+
* Helper method to update a server's configuration in the appropriate settings file
854+
* @param serverName The name of the server to update
855+
* @param configUpdate The configuration updates to apply
856+
* @param source Whether to update the global or project config
857+
*/
858+
private async updateServerConfig(
859+
serverName: string,
860+
configUpdate: Record<string, any>,
861+
source: "global" | "project" = "global",
862+
): Promise<void> {
863+
// Determine which config file to update
864+
let configPath: string
865+
if (source === "project") {
866+
const projectMcpPath = await this.getProjectMcpPath()
867+
if (!projectMcpPath) {
868+
throw new Error("Project MCP configuration file not found")
869+
}
870+
configPath = projectMcpPath
871+
} else {
872+
configPath = await this.getMcpSettingsFilePath()
873+
}
874+
875+
// Ensure the settings file exists and is accessible
867876
try {
868-
settingsPath = await this.getMcpSettingsFilePath()
877+
await fs.access(configPath)
878+
} catch (error) {
879+
console.error("Settings file not accessible:", error)
880+
throw new Error("Settings file not accessible")
881+
}
869882

870-
// Ensure the settings file exists and is accessible
871-
try {
872-
await fs.access(settingsPath)
873-
} catch (error) {
874-
console.error("Settings file not accessible:", error)
875-
throw new Error("Settings file not accessible")
876-
}
877-
const content = await fs.readFile(settingsPath, "utf-8")
878-
const config = JSON.parse(content)
883+
// Read and parse the config file
884+
const content = await fs.readFile(configPath, "utf-8")
885+
const config = JSON.parse(content)
879886

880-
// Validate the config structure
881-
if (!config || typeof config !== "object") {
882-
throw new Error("Invalid config structure")
883-
}
887+
// Validate the config structure
888+
if (!config || typeof config !== "object") {
889+
throw new Error("Invalid config structure")
890+
}
884891

885-
if (!config.mcpServers || typeof config.mcpServers !== "object") {
886-
config.mcpServers = {}
887-
}
892+
if (!config.mcpServers || typeof config.mcpServers !== "object") {
893+
config.mcpServers = {}
894+
}
888895

889-
if (config.mcpServers[serverName]) {
890-
// Create a new server config object to ensure clean structure
891-
const serverConfig = {
892-
...config.mcpServers[serverName],
893-
timeout,
894-
}
896+
if (!config.mcpServers[serverName]) {
897+
config.mcpServers[serverName] = {}
898+
}
895899

896-
config.mcpServers[serverName] = serverConfig
900+
// Create a new server config object to ensure clean structure
901+
const serverConfig = {
902+
...config.mcpServers[serverName],
903+
...configUpdate,
904+
}
897905

898-
// Write the entire config back
899-
const updatedConfig = {
900-
mcpServers: config.mcpServers,
901-
}
906+
// Ensure required fields exist
907+
if (!serverConfig.alwaysAllow) {
908+
serverConfig.alwaysAllow = []
909+
}
902910

903-
await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2))
904-
await this.notifyWebviewOfServerChanges()
911+
config.mcpServers[serverName] = serverConfig
912+
913+
// Write the entire config back
914+
const updatedConfig = {
915+
mcpServers: config.mcpServers,
916+
}
917+
918+
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
919+
}
920+
921+
public async updateServerTimeout(serverName: string, timeout: number): Promise<void> {
922+
try {
923+
// Find the connection to determine if it's a global or project server
924+
const connection = this.connections.find((conn) => conn.server.name === serverName)
925+
if (!connection) {
926+
throw new Error(`Server ${serverName} not found`)
905927
}
928+
929+
// Update the server config in the appropriate file
930+
await this.updateServerConfig(serverName, { timeout }, connection.server.source || "global")
931+
932+
await this.notifyWebviewOfServerChanges()
906933
} catch (error) {
907934
this.showErrorMessage(`Failed to update server ${serverName} timeout settings`, error)
908935
throw error
@@ -911,16 +938,36 @@ export class McpHub {
911938

912939
public async deleteServer(serverName: string): Promise<void> {
913940
try {
914-
const settingsPath = await this.getMcpSettingsFilePath()
941+
// Find the connection to determine if it's a global or project server
942+
const connection = this.connections.find((conn) => conn.server.name === serverName)
943+
if (!connection) {
944+
throw new Error(`Server ${serverName} not found`)
945+
}
946+
947+
// Determine config file based on server source
948+
const isProjectServer = connection.server.source === "project"
949+
let configPath: string
950+
951+
if (isProjectServer) {
952+
// Get project MCP config path
953+
const projectMcpPath = await this.getProjectMcpPath()
954+
if (!projectMcpPath) {
955+
throw new Error("Project MCP configuration file not found")
956+
}
957+
configPath = projectMcpPath
958+
} else {
959+
// Get global MCP settings path
960+
configPath = await this.getMcpSettingsFilePath()
961+
}
915962

916963
// Ensure the settings file exists and is accessible
917964
try {
918-
await fs.access(settingsPath)
965+
await fs.access(configPath)
919966
} catch (error) {
920967
throw new Error("Settings file not accessible")
921968
}
922969

923-
const content = await fs.readFile(settingsPath, "utf-8")
970+
const content = await fs.readFile(configPath, "utf-8")
924971
const config = JSON.parse(content)
925972

926973
// Validate the config structure
@@ -941,10 +988,10 @@ export class McpHub {
941988
mcpServers: config.mcpServers,
942989
}
943990

944-
await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2))
991+
await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2))
945992

946-
// Update server connections
947-
await this.updateServerConnections(config.mcpServers)
993+
// Update server connections with the correct source
994+
await this.updateServerConnections(config.mcpServers, connection.server.source || "global")
948995

949996
vscode.window.showInformationMessage(t("common:info.mcp_server_deleted", { serverName }))
950997
} else {
@@ -1017,10 +1064,41 @@ export class McpHub {
10171064

10181065
async toggleToolAlwaysAllow(serverName: string, toolName: string, shouldAllow: boolean): Promise<void> {
10191066
try {
1020-
const settingsPath = await this.getMcpSettingsFilePath()
1021-
const content = await fs.readFile(settingsPath, "utf-8")
1067+
// Find the connection to determine if it's a global or project server
1068+
const connection = this.connections.find((conn) => conn.server.name === serverName)
1069+
if (!connection) {
1070+
throw new Error(`Server ${serverName} not found`)
1071+
}
1072+
1073+
const isProjectServer = connection.server.source === "project"
1074+
let configPath: string
1075+
1076+
if (isProjectServer) {
1077+
// Get project MCP config path
1078+
const projectMcpPath = await this.getProjectMcpPath()
1079+
if (!projectMcpPath) {
1080+
throw new Error("Project MCP configuration file not found")
1081+
}
1082+
configPath = projectMcpPath
1083+
} else {
1084+
// Get global MCP settings path
1085+
configPath = await this.getMcpSettingsFilePath()
1086+
}
1087+
1088+
// Read the appropriate config file
1089+
const content = await fs.readFile(configPath, "utf-8")
10221090
const config = JSON.parse(content)
10231091

1092+
// Initialize mcpServers if it doesn't exist
1093+
if (!config.mcpServers) {
1094+
config.mcpServers = {}
1095+
}
1096+
1097+
// Initialize server config if it doesn't exist
1098+
if (!config.mcpServers[serverName]) {
1099+
config.mcpServers[serverName] = {}
1100+
}
1101+
10241102
// Initialize alwaysAllow if it doesn't exist
10251103
if (!config.mcpServers[serverName].alwaysAllow) {
10261104
config.mcpServers[serverName].alwaysAllow = []
@@ -1038,10 +1116,9 @@ export class McpHub {
10381116
}
10391117

10401118
// Write updated config back to file
1041-
await fs.writeFile(settingsPath, JSON.stringify(config, null, 2))
1119+
await fs.writeFile(configPath, JSON.stringify(config, null, 2))
10421120

10431121
// Update the tools list to reflect the change
1044-
const connection = this.connections.find((conn) => conn.server.name === serverName)
10451122
if (connection) {
10461123
connection.server.tools = await this.fetchToolsList(serverName)
10471124
await this.notifyWebviewOfServerChanges()

0 commit comments

Comments
 (0)