JuliusBrussee/caveman · error · Error

Gemini caveman MCP entry changed after enable; refusing dest

Error message

Gemini caveman MCP entry changed after enable; refusing destructive disable

What it means

For gemini-settings, disable first strips managed hooks and then only removes mcpServers.caveman from ~/.gemini/settings.json when it still JSON-matches the entry recorded at enable time (owned.installed_mcp). A modified `caveman` entry is treated as user-owned; deleting it blindly could lose your edits, so the operation aborts with the same refusal family as the Claude MCP guard.

Source

Thrown at packages/cli/src/index.ts:7633

    if (Object.keys(servers).length > 0) currentRoot.mcpServers = servers;
    else delete currentRoot.mcpServers;
    return jsonBytes(currentRoot);
  }
  if (operation.kind === "codex-hooks") {
    return jsonBytes(removeNativeHookEntries(parseJsonFileObject(operation.file, current), "codex"));
  }
  if (operation.kind === "gemini-settings") {
    const currentRoot = removeNativeHookEntries(parseJsonFileObject(operation.file, current), "gemini");
    const beforeRoot = parseJsonFileObject(operation.file, before);
    const servers = currentRoot.mcpServers && typeof currentRoot.mcpServers === "object" && !Array.isArray(currentRoot.mcpServers)
      ? currentRoot.mcpServers as Record<string, unknown>
      : {};
    const beforeServers = beforeRoot.mcpServers && typeof beforeRoot.mcpServers === "object" && !Array.isArray(beforeRoot.mcpServers)
      ? beforeRoot.mcpServers as Record<string, unknown>
      : {};
    const installed = operation.owned?.installed_mcp;
    if (servers.caveman !== undefined && JSON.stringify(servers.caveman) !== JSON.stringify(installed)) {
      throw new Error("Gemini caveman MCP entry changed after enable; refusing destructive disable");
    }
    if (servers.caveman !== undefined) {
      if (beforeServers.caveman === undefined) delete servers.caveman;
      else servers.caveman = beforeServers.caveman;
    }
    if (Object.keys(servers).length > 0) currentRoot.mcpServers = servers;
    else delete currentRoot.mcpServers;
    return jsonBytes(currentRoot);
  }
  if (operation.kind === "gemini-env") {
    const block = operation.owned?.route_block;
    if (typeof block !== "string") throw new Error("Gemini integration journal lacks owned routing block");
    const text = current.toString("utf8");
    if (text.includes(GEMINI_NATIVE_ENV_BEGIN) && !text.includes(block)) {
      throw new Error("Gemini routing block changed after enable; refusing destructive disable");
    }
    return Buffer.from(text.replace(`${block}\n\n`, "").replace(`\n\n${block}\n`, "\n").replace(`${block}\n`, "").replace(block, ""));
  }

View on GitHub (pinned to 2f49f0e1a3)

Solutions

  1. Revert mcpServers.caveman to the journalled value (owned.installed_mcp in ~/.caveman/integrations/gemini.json), then re-run `caveman disable gemini`.
  2. Or delete the `caveman` key from mcpServers yourself, then re-run disable.
  3. Keep personal MCP servers under names other than `caveman`.

Example fix

// before ~/.gemini/settings.json
"mcpServers": { "caveman": { "type": "stdio", "command": "./wrapper" } }

// after - journalled entry restored (or key removed), then `caveman disable gemini`
"mcpServers": { "caveman": { "type": "stdio", "command": "/usr/local/bin/caveman-mcp", "args": [], "env": {} } }
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs';
const s = JSON.parse(readFileSync(`${process.env.HOME}/.gemini/settings.json`, 'utf8'));
const journal = JSON.parse(readFileSync(`${process.env.HOME}/.caveman/integrations/gemini.json`, 'utf8'));
const installed = journal.operations.find((o: any) => o.kind === 'gemini-settings')?.owned?.installed_mcp;
if (s?.mcpServers?.caveman !== undefined && JSON.stringify(s.mcpServers.caveman) !== JSON.stringify(installed)) { console.error('Gemini mcpServers.caveman drifted from journal; revert or remove it before disable'); process.exit(1); }

Prevention

When it happens

Trigger: `caveman disable gemini` after editing the `caveman` entry in mcpServers of ~/.gemini/settings.json - changed command, added args/env, or reformatted by another MCP-config tool.

Common situations: Pointing the MCP command at a wrapper; moving the caveman-mcp binary; tools that rewrite settings.json and alter key order or formatting.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18). Data as JSON: /api/errors/89ba1a2bafadf3aa. Report an issue: GitHub.