JuliusBrussee/caveman · error · Error

MCP changes require the ownership transaction

Error message

${a.display_name} MCP changes require the ownership transaction

What it means

For the kilo and qwen agents, MCP add/remove is not allowed through the plain per-agent mutation helpers because these agents use a dedicated ownership transaction (journal + canonical marker path, see errors 483/484) instead of direct config edits. The remove switch-case throws to enforce that invariants are maintained only through the transactional path.

Solutions

  1. Use the ownership-transaction command/flow for kilo and qwen MCP changes (the install/uninstall with journaling)
  2. For other agents, keep using the direct remove path — the restriction is agent-specific
  3. Update automation scripts to dispatch kilo/qwen to the transactional API

Example fix

// before
removeMcpAgent({ id: "kilo", display_name: "Kilo" }, serverName);
// after
await runMcpOwnershipTransaction("kilo", serverName, /* remove */ true);
Defensive patterns

Strategy: validation

Validate before calling

const TRANSACTIONAL_AGENTS = new Set(["kilo", "qwen"]);
if (TRANSACTIONAL_AGENTS.has(agent.id)) {
  await runMcpOwnershipTransaction(agent.id, serverName);
} else {
  removeMcpDirect(agent, serverName);
}

Type guard

function requiresOwnershipTransaction(agentId: string): boolean {
  return agentId === "kilo" || agentId === "qwen";
}

Try / catch

try {
  removeMcpForAgent(agent, serverName);
} catch (e) {
  if ((e as Error).message.includes("ownership transaction")) {
    await runMcpOwnershipTransaction(agent.id, serverName);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the MCP remove/uninstall path with agent 'kilo' or 'qwen' instead of the transactional ownership flow — i.e. invoking a direct mutation API or CLI subcommand that bypasses the ownership transaction.

Common situations: Scripts written for other agents (codex, gemini, opencode) applied to kilo/qwen; older automation predating the ownership-transaction requirement; attempting manual config cleanup with the generic remove command.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/924d223b2dda794b. Report an issue: GitHub.

Appendix: source

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

function uninstallMcpForAgent(a: AgentProfile, serverName = "caveman"): boolean {
  switch (a.id) {
    case "claude": {
      const claude = which("claude");
      if (!claude) return true; // agent itself is gone; dropping the marker is all that's left
      try {
        execPortableIgnore(claude, ["mcp", "remove", serverName]);
      } catch {
        // not registered — fine, still a successful uninstall.
      }
      return true;
    }
    case "codex":
      return removeMcpCodexToml(serverName);
    case "opencode":
      return removeMcpJson(join(homedir(), ".config", "opencode", "opencode.json"), ["mcp", serverName]);
    case "kilo":
    case "qwen":
      throw new Error(`${a.display_name} MCP changes require the ownership transaction`);
    case "gemini":
      return removeMcpJson(geminiSettingsPath(), ["mcpServers", serverName]);
    case "hermes":
      return removeMcpHermesYaml(serverName);
    case "openclaw":
      return uninstallMcpOpenClaw(serverName);
    default:
      return false;
  }
}

// removeMcpCodexToml drops the exact [mcp_servers.<name>] block mcpInstall wrote
// (header + its command/args lines, up to the next section or EOF).
function removeMcpCodexToml(serverName = "caveman"): boolean {
  const path = join(codexHomeDir(), "config.toml");
  let existing = "";
  try {
    existing = readFileSync(path, "utf8");

View on GitHub (pinned to 3ee70a1026)