|
|
@@ -39,7 +39,8 @@ const StdioConfigSchema = z.object({
|
|
|
command: z.string(),
|
|
|
args: z.array(z.string()).optional(),
|
|
|
env: z.record(z.string()).optional(),
|
|
|
- alwaysAllow: AlwaysAllowSchema.optional()
|
|
|
+ alwaysAllow: AlwaysAllowSchema.optional(),
|
|
|
+ disabled: z.boolean().optional()
|
|
|
})
|
|
|
|
|
|
const McpSettingsSchema = z.object({
|
|
|
@@ -61,7 +62,10 @@ export class McpHub {
|
|
|
}
|
|
|
|
|
|
getServers(): McpServer[] {
|
|
|
- return this.connections.map((conn) => conn.server)
|
|
|
+ // Only return enabled servers
|
|
|
+ return this.connections
|
|
|
+ .filter((conn) => !conn.server.disabled)
|
|
|
+ .map((conn) => conn.server)
|
|
|
}
|
|
|
|
|
|
async getMcpServersPath(): Promise<string> {
|
|
|
@@ -117,9 +121,7 @@ export class McpHub {
|
|
|
return
|
|
|
}
|
|
|
try {
|
|
|
- vscode.window.showInformationMessage("Updating MCP servers...")
|
|
|
await this.updateServerConnections(result.data.mcpServers || {})
|
|
|
- vscode.window.showInformationMessage("MCP servers updated")
|
|
|
} catch (error) {
|
|
|
console.error("Failed to process MCP settings change:", error)
|
|
|
}
|
|
|
@@ -202,11 +204,13 @@ export class McpHub {
|
|
|
}
|
|
|
|
|
|
// valid schema
|
|
|
+ const parsedConfig = StdioConfigSchema.parse(config)
|
|
|
const connection: McpConnection = {
|
|
|
server: {
|
|
|
name,
|
|
|
config: JSON.stringify(config),
|
|
|
status: "connecting",
|
|
|
+ disabled: parsedConfig.disabled,
|
|
|
},
|
|
|
client,
|
|
|
transport,
|
|
|
@@ -466,13 +470,89 @@ export class McpHub {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- // Using server
|
|
|
+ // Public methods for server management
|
|
|
+
|
|
|
+ public async toggleServerDisabled(serverName: string, disabled: boolean): Promise<void> {
|
|
|
+ let settingsPath: string
|
|
|
+ try {
|
|
|
+ settingsPath = await this.getMcpSettingsFilePath()
|
|
|
+
|
|
|
+ // Ensure the settings file exists and is accessible
|
|
|
+ try {
|
|
|
+ await fs.access(settingsPath)
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Settings file not accessible:', error)
|
|
|
+ throw new Error('Settings file not accessible')
|
|
|
+ }
|
|
|
+ const content = await fs.readFile(settingsPath, "utf-8")
|
|
|
+ const config = JSON.parse(content)
|
|
|
+
|
|
|
+ // Validate the config structure
|
|
|
+ if (!config || typeof config !== 'object') {
|
|
|
+ throw new Error('Invalid config structure')
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
|
+ config.mcpServers = {}
|
|
|
+ }
|
|
|
+
|
|
|
+ if (config.mcpServers[serverName]) {
|
|
|
+ // Create a new server config object to ensure clean structure
|
|
|
+ const serverConfig = {
|
|
|
+ ...config.mcpServers[serverName],
|
|
|
+ disabled
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ensure required fields exist
|
|
|
+ if (!serverConfig.alwaysAllow) {
|
|
|
+ serverConfig.alwaysAllow = []
|
|
|
+ }
|
|
|
+
|
|
|
+ config.mcpServers[serverName] = serverConfig
|
|
|
+
|
|
|
+ // Write the entire config back
|
|
|
+ const updatedConfig = {
|
|
|
+ mcpServers: config.mcpServers
|
|
|
+ }
|
|
|
+
|
|
|
+ await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2))
|
|
|
+
|
|
|
+ const connection = this.connections.find(conn => conn.server.name === serverName)
|
|
|
+ if (connection) {
|
|
|
+ try {
|
|
|
+ connection.server.disabled = disabled
|
|
|
+
|
|
|
+ // Only refresh capabilities if connected
|
|
|
+ if (connection.server.status === "connected") {
|
|
|
+ connection.server.tools = await this.fetchToolsList(serverName)
|
|
|
+ connection.server.resources = await this.fetchResourcesList(serverName)
|
|
|
+ connection.server.resourceTemplates = await this.fetchResourceTemplatesList(serverName)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`Failed to refresh capabilities for ${serverName}:`, error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ await this.notifyWebviewOfServerChanges()
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to update server disabled state:", error)
|
|
|
+ if (error instanceof Error) {
|
|
|
+ console.error("Error details:", error.message, error.stack)
|
|
|
+ }
|
|
|
+ vscode.window.showErrorMessage(`Failed to update server state: ${error instanceof Error ? error.message : String(error)}`)
|
|
|
+ throw error
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
async readResource(serverName: string, uri: string): Promise<McpResourceResponse> {
|
|
|
const connection = this.connections.find((conn) => conn.server.name === serverName)
|
|
|
if (!connection) {
|
|
|
throw new Error(`No connection found for server: ${serverName}`)
|
|
|
}
|
|
|
+ if (connection.server.disabled) {
|
|
|
+ throw new Error(`Server "${serverName}" is disabled`)
|
|
|
+ }
|
|
|
return await connection.client.request(
|
|
|
{
|
|
|
method: "resources/read",
|
|
|
@@ -495,6 +575,9 @@ export class McpHub {
|
|
|
`No connection found for server: ${serverName}. Please make sure to use MCP servers available under 'Connected MCP Servers'.`,
|
|
|
)
|
|
|
}
|
|
|
+ if (connection.server.disabled) {
|
|
|
+ throw new Error(`Server "${serverName}" is disabled and cannot be used`)
|
|
|
+ }
|
|
|
|
|
|
return await connection.client.request(
|
|
|
{
|