Skip to content

Corrupt MCP enablement config silently re-enables servers, then disable() erases it #28786

Description

@chiruu12

What happened?

readConfig() in packages/cli/src/config/mcp/mcpServerEnablement.ts collapses a JSON parse failure into the same empty object it uses for "file does not exist":

try {
  const content = await fs.readFile(this.configFilePath, 'utf-8');
  return JSON.parse(content) as McpServerEnablementConfig;
} catch (error) {
  if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
    return {};
  }
  coreEvents.emitFeedback('error', 'Failed to read MCP server enablement config.', error);
  return {};
}

A SyntaxError from a hand-edit, a truncated write, or disk corruption is indistinguishable downstream from an absent file. Two consequences:

isFileEnabled defaults to enabled when a server has no entry:

const state = config[normalizeServerId(serverName)];
return state?.enabled ?? true;

With {}, every server the user deliberately disabled reports enabled, so it gets connected and its tools are exposed to the model.

Then disable() writes that same {} back with one key added:

async disable(serverName: string): Promise<void> {
  const config = await this.readConfig();
  config[normalizeServerId(serverName)] = { enabled: false };
  await this.writeConfig(config);
}

Every other entry in the file is gone. (enable() is guarded by normalizedName in config, so it is a no-op on {} and does not overwrite.)

What did you expect to happen?

A malformed config is distinguished from a missing one. Disabled servers stay disabled, and the file is not overwritten while its contents cannot be read.

Client information

Client Information

Source-level report, no /about output. Verified against:

repository checkout: 4238b0b
package version:     0.56.0-nightly.20260806.g761f604c1
published CLI:       0.52.0
node:                v24.10.0
OS:                  macOS 26.5.1

Anything else we need to know?

Found by reading, not from a runtime failure, so no user report backs it. The path is straightforward though: readConfig is private and the only reader, and nothing validates upstream.

The security-relevant part is that this fails open across a trust boundary. A user who disabled an MCP server has it silently reconnected.

Fix direction: separate ENOENT from a parse failure. On parse failure, either fail closed or refuse to write until the user resolves it, and preserve the existing file rather than overwriting it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/coreIssues related to User Interface, OS Support, Core Functionalityeffort/small1 day or less: trivial logic, UI adjustments, docskind/bugpriority/p1Important and should be addressed in the near term.status/bot-triaged

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions