fix(converters): preserve user config when writing MCP servers (#479)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,11 @@
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import type { ClaudeHomeConfig } from "../parsers/claude-home"
|
||||
import { renderCodexConfig } from "../targets/codex"
|
||||
import { mergeCodexConfig, renderCodexConfig } from "../targets/codex"
|
||||
import { writeTextSecure } from "../utils/files"
|
||||
import { syncCodexCommands } from "./commands"
|
||||
import { syncSkills } from "./skills"
|
||||
|
||||
const CURRENT_START_MARKER = "# BEGIN compound-plugin Claude Code MCP"
|
||||
const CURRENT_END_MARKER = "# END compound-plugin Claude Code MCP"
|
||||
const LEGACY_MARKER = "# MCP servers synced from Claude Code"
|
||||
|
||||
export async function syncToCodex(
|
||||
config: ClaudeHomeConfig,
|
||||
outputRoot: string,
|
||||
@@ -17,52 +13,19 @@ export async function syncToCodex(
|
||||
await syncSkills(config.skills, path.join(outputRoot, "skills"))
|
||||
await syncCodexCommands(config, outputRoot)
|
||||
|
||||
// Write MCP servers to config.toml (TOML format)
|
||||
if (Object.keys(config.mcpServers).length > 0) {
|
||||
const configPath = path.join(outputRoot, "config.toml")
|
||||
const mcpToml = renderCodexConfig(config.mcpServers)
|
||||
if (!mcpToml) {
|
||||
return
|
||||
// Write MCP servers to config.toml, or clean up stale managed block if none remain
|
||||
const configPath = path.join(outputRoot, "config.toml")
|
||||
let existingContent = ""
|
||||
try {
|
||||
existingContent = await fs.readFile(configPath, "utf-8")
|
||||
} catch (err) {
|
||||
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
|
||||
throw err
|
||||
}
|
||||
|
||||
// Read existing config and merge idempotently
|
||||
let existingContent = ""
|
||||
try {
|
||||
existingContent = await fs.readFile(configPath, "utf-8")
|
||||
} catch (err) {
|
||||
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
const managedBlock = [
|
||||
CURRENT_START_MARKER,
|
||||
mcpToml.trim(),
|
||||
CURRENT_END_MARKER,
|
||||
"",
|
||||
].join("\n")
|
||||
|
||||
const withoutCurrentBlock = existingContent.replace(
|
||||
new RegExp(
|
||||
`${escapeForRegex(CURRENT_START_MARKER)}[\\s\\S]*?${escapeForRegex(CURRENT_END_MARKER)}\\n?`,
|
||||
"g",
|
||||
),
|
||||
"",
|
||||
).trimEnd()
|
||||
|
||||
const legacyMarkerIndex = withoutCurrentBlock.indexOf(LEGACY_MARKER)
|
||||
const cleaned = legacyMarkerIndex === -1
|
||||
? withoutCurrentBlock
|
||||
: withoutCurrentBlock.slice(0, legacyMarkerIndex).trimEnd()
|
||||
|
||||
const newContent = cleaned
|
||||
? `${cleaned}\n\n${managedBlock}`
|
||||
: `${managedBlock}`
|
||||
|
||||
await writeTextSecure(configPath, newContent)
|
||||
}
|
||||
const mcpToml = renderCodexConfig(config.mcpServers)
|
||||
const merged = mergeCodexConfig(existingContent, mcpToml)
|
||||
if (merged !== null) {
|
||||
await writeTextSecure(configPath, merged)
|
||||
}
|
||||
}
|
||||
|
||||
function escapeForRegex(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user