import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; import { createMcpConfig, EXA_TOOLS } from "../extensions/mcp-config.ts"; test("one MCP adapter config contains both Exa and CodeGraph", () => { assert.deepEqual(createMcpConfig({}), { settings: { disableProxyTool: true, scriptMode: false, }, mcpServers: { exa: { url: `https://mcp.exa.ai/mcp?tools=${EXA_TOOLS.join(",")}`, protocolVersion: "auto", lifecycle: "eager", directTools: EXA_TOOLS, includeTools: EXA_TOOLS, toolPrefix: "none", approveTools: false, exposeResources: false, }, codegraph: { command: "codegraph", args: ["serve", "--mcp"], lifecycle: "keep-alive", directTools: ["codegraph_explore"], includeTools: ["codegraph_explore"], toolPrefix: "none", approveTools: false, exposeResources: false, }, }, }); }); test("Exa API key is resolved from the environment into a header and never placed in the URL", () => { const config = createMcpConfig({ EXA_API_KEY: "exa-test-key" }); assert.deepEqual(config.mcpServers.exa.headers, { "x-api-key": "$env:EXA_API_KEY" }); assert.equal(config.mcpServers.exa.url?.includes("exa-test-key"), false); assert.equal(JSON.stringify(config).includes("exa-test-key"), false); }); test("the package has exactly one local MCP adapter owner", async () => { const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8")) as { pi: { extensions: string[] }; }; const localEntries = packageJson.pi.extensions.filter((entry) => !entry.startsWith("./node_modules/")); const adapterOwners: string[] = []; for (const entry of localEntries) { const source = await readFile(new URL(`../${entry.slice(2)}`, import.meta.url), "utf8"); if (source.includes("createMcpAdapter") || source.includes('from "pi-mcp-adapter"')) { adapterOwners.push(entry); } } assert.deepEqual(adapterOwners, ["./extensions/mcp.ts"]); }); test("search credentials load before all search providers", async () => { const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8")) as { pi: { extensions: string[] }; }; const entries = packageJson.pi.extensions; const loaderIndex = entries.indexOf("./extensions/search-config.ts"); for (const provider of [ "./extensions/tavily-override.ts", "./node_modules/@keenable/pi-search/src/index.ts", "./extensions/mcp.ts", ]) { assert.ok(loaderIndex >= 0 && loaderIndex < entries.indexOf(provider), `${provider} must load after search config`); } });