diff --git a/packages/omo-codex/scripts/install-config.test.mjs b/packages/omo-codex/scripts/install-config.test.mjs index e1fb42e80..f984b3dc5 100644 --- a/packages/omo-codex/scripts/install-config.test.mjs +++ b/packages/omo-codex/scripts/install-config.test.mjs @@ -27,6 +27,61 @@ test("#given empty Codex config #when script installer updates config #then enab assert.match(config, /max_concurrent_threads_per_session = 10000/); }); +test("#given empty Codex config #when script installer updates config #then installs Context7 MCP", async () => { + // given + const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-context7-")); + const configPath = join(root, "config.toml"); + + // when + await updateCodexConfig({ + configPath, + repoRoot: "/repo/packages/omo-codex", + marketplaceName: "debug", + marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" }, + pluginNames: ["omo"], + }); + + // then + const config = await readFile(configPath, "utf8"); + assert.match(config, /\[mcp_servers\.context7\]/); + assert.match(config, /command = "npx"/); + assert.match(config, /args = \["-y", "@upstash\/context7-mcp", "--api-key", "YOUR_API_KEY"\]/); + assert.match(config, /startup_timeout_sec = 20/); +}); + +test("#given existing Context7 MCP config #when script installer updates config #then preserves user setup", async () => { + // given + const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-context7-existing-")); + const configPath = join(root, "config.toml"); + await writeFile( + configPath, + [ + "[mcp_servers.context7]", + 'command = "node"', + 'args = ["/opt/context7/server.js"]', + 'startup_timeout_sec = 40', + "", + ].join("\n"), + ); + + // when + await updateCodexConfig({ + configPath, + repoRoot: "/repo/packages/omo-codex", + marketplaceName: "debug", + marketplaceSource: { sourceType: "local", source: "/repo/packages/omo-codex" }, + pluginNames: ["omo"], + }); + + // then + const config = await readFile(configPath, "utf8"); + assert.match(config, /\[mcp_servers\.context7\]/); + assert.match(config, /command = "node"/); + assert.match(config, /args = \["\/opt\/context7\/server\.js"\]/); + assert.match(config, /startup_timeout_sec = 40/); + assert.doesNotMatch(config, /YOUR_API_KEY/); +}); + test("#given sisyphuslabs config without explicit source #when script installer updates config #then uses local marketplace", async () => { // given const root = await mkdtemp(join(tmpdir(), "omo-codex-script-config-sisyphuslabs-")); diff --git a/packages/omo-codex/scripts/install/config.mjs b/packages/omo-codex/scripts/install/config.mjs index 30ac0d8e4..f03093434 100644 --- a/packages/omo-codex/scripts/install/config.mjs +++ b/packages/omo-codex/scripts/install/config.mjs @@ -15,6 +15,14 @@ const MANAGED_CODEX_AGENT_NAMES = [ "momus", "plan", ]; +const CONTEXT7_MCP_SERVER_HEADER = "mcp_servers.context7"; +const CONTEXT7_MCP_SERVER_BLOCK = [ + `[${CONTEXT7_MCP_SERVER_HEADER}]`, + `command = "npx"`, + `args = ["-y", "@upstash/context7-mcp", "--api-key", "YOUR_API_KEY"]`, + `startup_timeout_sec = 20`, + "", +].join("\n"); export async function updateCodexConfig({ configPath, @@ -40,6 +48,7 @@ export async function updateCodexConfig({ config = ensureFeatureEnabled(config, "plugins"); config = ensureFeatureEnabled(config, "plugin_hooks"); config = ensureCodexMultiAgentV2Config(config); + config = ensureContext7McpServer(config); config = ensureMarketplaceBlock(config, marketplaceName, marketplaceSource); for (const pluginName of pluginNames) { config = ensurePluginEnabled(config, `${pluginName}@${marketplaceName}`); @@ -129,6 +138,11 @@ function ensureMarketplaceBlock(config, marketplaceName, source) { return appendBlock(config, block); } +function ensureContext7McpServer(config) { + if (findTomlSection(config, CONTEXT7_MCP_SERVER_HEADER)) return config; + return appendBlock(config, CONTEXT7_MCP_SERVER_BLOCK); +} + function ensurePluginEnabled(config, pluginKey) { const header = `plugins.${JSON.stringify(pluginKey)}`; const section = findTomlSection(config, header);