feat(mcp): support disabling discovered servers from third-party configs
Previously, /mcp enable|disable only worked for servers defined directly in user or project config files. Discovered servers from third-party configs (e.g. capability-provided) could not be toggled off without removing them at the source. This adds a disabledServers list to the user-level .mcp.json config that acts as an overlay. When loading MCP configs, servers whose names appear in this list are excluded alongside those with enabled: false. Changes: - Add disabledServers field to MCPConfigFile type - Add readDisabledServers/setServerDisabled helpers in config-writer - Filter discovered servers against the disabled list during config load - Handle enable/disable toggle for discovered servers in the MCP command controller, including reconnection on re-enable - Show disabled discovered servers in /mcp list output
This commit is contained in:
@@ -8,7 +8,14 @@ import { getMCPConfigPath, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { SourceMeta } from "../../capability/types";
|
||||
import { analyzeAuthError, discoverOAuthEndpoints, MCPManager } from "../../mcp";
|
||||
import { connectToServer, disconnectServer, listTools } from "../../mcp/client";
|
||||
import { addMCPServer, readMCPConfigFile, removeMCPServer, updateMCPServer } from "../../mcp/config-writer";
|
||||
import {
|
||||
addMCPServer,
|
||||
readDisabledServers,
|
||||
readMCPConfigFile,
|
||||
removeMCPServer,
|
||||
setServerDisabled,
|
||||
updateMCPServer,
|
||||
} from "../../mcp/config-writer";
|
||||
import { MCPOAuthFlow } from "../../mcp/oauth-flow";
|
||||
import type { MCPServerConfig, MCPServerConnection } from "../../mcp/types";
|
||||
import type { OAuthCredential } from "../../session/auth-storage";
|
||||
@@ -774,7 +781,12 @@ export class MCPCommandController {
|
||||
}
|
||||
}
|
||||
|
||||
if (userServers.length === 0 && projectServers.length === 0 && discoveredServers.length === 0) {
|
||||
if (
|
||||
userServers.length === 0 &&
|
||||
projectServers.length === 0 &&
|
||||
discoveredServers.length === 0 &&
|
||||
(userConfig.disabledServers ?? []).length === 0
|
||||
) {
|
||||
this.#showMessage(
|
||||
[
|
||||
"",
|
||||
@@ -868,6 +880,17 @@ export class MCPCommandController {
|
||||
lines.push("");
|
||||
}
|
||||
}
|
||||
|
||||
// Show servers disabled via /mcp disable (from third-party configs)
|
||||
const disabledServers = await readDisabledServers(userPath);
|
||||
const relevantDisabled = disabledServers.filter(n => !configServerNames.has(n));
|
||||
if (relevantDisabled.length > 0) {
|
||||
lines.push(theme.fg("accent", "Disabled") + theme.fg("muted", " (discovered servers):"));
|
||||
for (const name of relevantDisabled) {
|
||||
lines.push(` ${theme.fg("accent", name)}${theme.fg("warning", " ◌ disabled")}`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
this.#showMessage(lines.join("\n"));
|
||||
} catch (error) {
|
||||
this.ctx.showError(`Failed to list servers: ${error instanceof Error ? error.message : String(error)}`);
|
||||
@@ -1061,7 +1084,41 @@ export class MCPCommandController {
|
||||
try {
|
||||
const found = await this.#findConfiguredServer(name);
|
||||
if (!found) {
|
||||
this.ctx.showError(`Server "${name}" not found.`);
|
||||
// Check if this is a discovered server from a third-party config
|
||||
const userConfigPath = getMCPConfigPath("user", getProjectDir());
|
||||
const disabledServers = new Set(await readDisabledServers(userConfigPath));
|
||||
const isDiscovered = this.ctx.mcpManager?.getSource(name);
|
||||
const isCurrentlyDisabled = disabledServers.has(name);
|
||||
if (!isDiscovered && !isCurrentlyDisabled) {
|
||||
this.ctx.showError(`Server "${name}" not found.`);
|
||||
return;
|
||||
}
|
||||
if (isCurrentlyDisabled === !enabled) {
|
||||
this.#showMessage(
|
||||
["", theme.fg("muted", `Server "${name}" is already ${enabled ? "enabled" : "disabled"}.`), ""].join(
|
||||
"\n",
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
await setServerDisabled(userConfigPath, name, !enabled);
|
||||
if (enabled) {
|
||||
await this.#reloadMCP();
|
||||
const state = await this.#waitForServerConnectionWithAnimation(name);
|
||||
const status =
|
||||
state === "connected"
|
||||
? theme.fg("success", "Connected")
|
||||
: state === "connecting"
|
||||
? theme.fg("muted", "Connecting")
|
||||
: theme.fg("warning", "Not connected yet");
|
||||
this.#showMessage(
|
||||
["", theme.fg("success", `\u2713 Enabled "${name}"`), "", ` Status: ${status}`, ""].join("\n"),
|
||||
);
|
||||
} else {
|
||||
await this.ctx.mcpManager?.disconnectServer(name);
|
||||
await this.ctx.session.refreshMCPTools(this.ctx.mcpManager?.getTools() ?? []);
|
||||
this.#showMessage(["", theme.fg("success", `\u2713 Disabled "${name}"`), ""].join("\n"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user