///
import { describe, test, expect, spyOn, beforeEach, afterEach } from "bun:test"
import type { OhMyOpenCodeConfig } from "../config"
import * as mcpLoader from "../features/claude-code-mcp-loader"
import * as mcpModule from "../mcp"
import * as shared from "../shared"
let loadMcpConfigsSpy: ReturnType
let createBuiltinMcpsSpy: ReturnType
beforeEach(() => {
loadMcpConfigsSpy = spyOn(mcpLoader, "loadMcpConfigs" as any).mockResolvedValue({
servers: {},
})
createBuiltinMcpsSpy = spyOn(mcpModule, "createBuiltinMcps" as any).mockReturnValue({})
spyOn(shared, "log" as any).mockImplementation(() => {})
})
afterEach(() => {
loadMcpConfigsSpy.mockRestore()
createBuiltinMcpsSpy.mockRestore()
;(shared.log as any)?.mockRestore?.()
})
function createPluginConfig(overrides: Partial = {}): OhMyOpenCodeConfig {
return {
disabled_mcps: [],
...overrides,
} as OhMyOpenCodeConfig
}
const EMPTY_PLUGIN_COMPONENTS = {
commands: {},
skills: {},
agents: {},
mcpServers: {},
hooksConfigs: [],
plugins: [],
errors: [],
}
describe("applyMcpConfig", () => {
test("preserves enabled:false from user config after merge with .mcp.json MCPs", async () => {
//#given
const userMcp = {
firecrawl: { type: "remote", url: "https://firecrawl.example.com", enabled: false },
exa: { type: "remote", url: "https://exa.example.com", enabled: true },
}
loadMcpConfigsSpy.mockResolvedValue({
servers: {
firecrawl: { type: "remote", url: "https://firecrawl.example.com", enabled: true },
exa: { type: "remote", url: "https://exa.example.com", enabled: true },
},
})
const config: Record = { mcp: userMcp }
const pluginConfig = createPluginConfig()
//#when
const { applyMcpConfig } = await import("./mcp-config-handler")
await applyMcpConfig({ config, pluginConfig, pluginComponents: EMPTY_PLUGIN_COMPONENTS })
//#then
const mergedMcp = config.mcp as Record>
expect(mergedMcp.firecrawl.enabled).toBe(false)
expect(mergedMcp.exa.enabled).toBe(true)
})
test("applies disabled_mcps to MCPs from all sources", async () => {
//#given
createBuiltinMcpsSpy.mockReturnValue({
websearch: { type: "remote", url: "https://mcp.exa.ai/mcp", enabled: true },
})
loadMcpConfigsSpy.mockResolvedValue({
servers: {
playwright: { type: "local", command: ["npx", "@playwright/mcp"], enabled: true },
},
})
const config: Record = { mcp: {} }
const pluginConfig = createPluginConfig({ disabled_mcps: ["playwright"] as any })
//#when
const { applyMcpConfig } = await import("./mcp-config-handler")
await applyMcpConfig({
config,
pluginConfig,
pluginComponents: {
...EMPTY_PLUGIN_COMPONENTS,
mcpServers: {
"plugin:custom": { type: "local", command: ["npx", "custom"], enabled: true },
},
},
})
//#then
const mergedMcp = config.mcp as Record>
expect(mergedMcp).not.toHaveProperty("playwright")
expect(mergedMcp).toHaveProperty("websearch")
expect(mergedMcp).toHaveProperty("plugin:custom")
})
test("passes disabled_mcps to loadMcpConfigs", async () => {
//#given
const config: Record = { mcp: {} }
const pluginConfig = createPluginConfig({ disabled_mcps: ["firecrawl", "exa"] as any })
//#when
const { applyMcpConfig } = await import("./mcp-config-handler")
await applyMcpConfig({ config, pluginConfig, pluginComponents: EMPTY_PLUGIN_COMPONENTS })
//#then
expect(loadMcpConfigsSpy).toHaveBeenCalledWith(["firecrawl", "exa"])
})
test("works when no user MCPs have enabled:false", async () => {
//#given
const userMcp = {
exa: { type: "remote", url: "https://exa.example.com", enabled: true },
}
loadMcpConfigsSpy.mockResolvedValue({
servers: {
firecrawl: { type: "remote", url: "https://firecrawl.example.com", enabled: true },
},
})
const config: Record = { mcp: userMcp }
const pluginConfig = createPluginConfig()
//#when
const { applyMcpConfig } = await import("./mcp-config-handler")
await applyMcpConfig({ config, pluginConfig, pluginComponents: EMPTY_PLUGIN_COMPONENTS })
//#then
const mergedMcp = config.mcp as Record>
expect(mergedMcp.exa.enabled).toBe(true)
expect(mergedMcp.firecrawl.enabled).toBe(true)
})
test("deletes plugin MCPs that are in disabled_mcps", async () => {
//#given
const config: Record = { mcp: {} }
const pluginConfig = createPluginConfig({ disabled_mcps: ["plugin:custom"] as any })
//#when
const { applyMcpConfig } = await import("./mcp-config-handler")
await applyMcpConfig({
config,
pluginConfig,
pluginComponents: {
...EMPTY_PLUGIN_COMPONENTS,
mcpServers: {
"plugin:custom": { type: "local", command: ["npx", "custom"], enabled: true },
},
},
})
//#then
const mergedMcp = config.mcp as Record>
expect(mergedMcp).not.toHaveProperty("plugin:custom")
})
})