Skip to main content
Glama
index.test.ts17.4 kB
import { describe, it, expect, mock, beforeEach } from "bun:test"; import { buildOracleArgs, invokeOracle, } from "./index.js"; describe("buildOracleArgs", () => { describe("Codex command construction", () => { it("should construct codex args with model and reasoning", () => { const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", "test prompt"); expect(args[0]).toBe("exec"); expect(args[1]).toBe("--model"); expect(args[2]).toBe("gpt-5.1-codex-mini"); expect(args[3]).toBe("-c"); expect(args[4]).toBe("reasoning_level=medium"); expect(args[5]).toBe("test prompt"); }); it("should construct codex args without reasoning when undefined", () => { const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", undefined, "test prompt"); expect(args[0]).toBe("exec"); expect(args[1]).toBe("--model"); expect(args[2]).toBe("gpt-5.1-codex-mini"); expect(args[3]).toBe("test prompt"); expect(args).not.toContain("reasoning_level=medium"); }); it("should support custom codex command names", () => { const args = buildOracleArgs("my-codex", "gpt-5.1", "high", "prompt"); expect(args[0]).toBe("exec"); expect(args[1]).toBe("--model"); }); }); describe("Gemini command construction", () => { it("should construct gemini args with model", () => { const args = buildOracleArgs("gemini", "gemini-2.5-pro", undefined, "test prompt"); expect(args[0]).toBe("-p"); expect(args[1]).toBe("--model"); expect(args[2]).toBe("gemini-2.5-pro"); expect(args[3]).toBe("test prompt"); }); it("should not include reasoning_level for gemini even when specified", () => { const args = buildOracleArgs("gemini", "gemini-2.5-pro", "high", "prompt"); expect(args).not.toContain("reasoning_level=high"); expect(args[0]).toBe("-p"); }); it("should support custom gemini command names", () => { const args = buildOracleArgs("my-gemini", "gemini-2.5-pro", undefined, "prompt"); expect(args[0]).toBe("-p"); expect(args[1]).toBe("--model"); expect(args[2]).toBe("gemini-2.5-pro"); }); }); describe("Claude command construction", () => { it("should construct claude args with model", () => { const args = buildOracleArgs("claude", "opus", undefined, "test prompt"); expect(args[0]).toBe("-p"); expect(args[1]).toBe("--model"); expect(args[2]).toBe("opus"); expect(args[3]).toBe("test prompt"); }); it("should not include reasoning_level for claude even when specified", () => { const args = buildOracleArgs("claude", "opus", "high", "prompt"); expect(args).not.toContain("reasoning_level=high"); expect(args[0]).toBe("-p"); }); }); describe("Special characters handling", () => { it("should preserve double quotes in prompt without escaping", () => { const prompt = 'Say "hello world"'; const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt); expect(args[5]).toBe(prompt); expect(args[5]).toContain('"hello world"'); }); it("should preserve backticks in prompt without escaping", () => { const prompt = "Use `variable` here"; const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt); expect(args[5]).toBe(prompt); expect(args[5]).toContain("`variable`"); }); it("should preserve dollar signs and parens without escaping", () => { const prompt = "What about $(test) and $var?"; const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt); expect(args[5]).toBe(prompt); expect(args[5]).toContain("$(test)"); expect(args[5]).toContain("$var"); }); it("should preserve newlines in prompt", () => { const prompt = "Line 1\nLine 2\nLine 3"; const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt); expect(args[5]).toBe(prompt); expect(args[5]).toContain("\n"); }); it("should preserve mixed special characters", () => { const prompt = 'Mix: `code`, "string", $(cmd), & and |'; const args = buildOracleArgs("codex", "gpt-5.1-codex-mini", "medium", prompt); expect(args[5]).toBe(prompt); expect(args[5]).toContain("`code`"); expect(args[5]).toContain('"string"'); expect(args[5]).toContain("$(cmd)"); }); }); }); describe("invokeOracle", () => { describe("Return type structure", () => { it("should return an object with stdout, status, and optional error", () => { // This tests the function signature and return type // In real usage, invokeOracle will be mocked in the server logic const result = invokeOracle("nonexistent-command", []); expect(typeof result).toBe("object"); expect("stdout" in result).toBe(true); expect("status" in result).toBe(true); expect("error" in result).toBe(true); }); it("should have a numeric status field", () => { const result = invokeOracle("nonexistent-command", []); expect(typeof result.status).toBe("number"); }); it("should have an error field when command not found", () => { const result = invokeOracle("nonexistent-command-xyz", []); expect(result.error).toBeDefined(); }); }); }); describe("Error messages", () => { it("should format Error objects correctly", () => { const error = new Error("Test error message"); const message = error instanceof Error ? error.message : String(error); expect(message).toBe("Test error message"); }); it("should format non-Error objects correctly", () => { const error: unknown = "String error"; const message = error instanceof Error ? error.message : String(error); expect(message).toBe("String error"); }); it("should construct oracle error response", () => { const errorMsg = "Command failed"; const response = { content: [ { type: "text", text: `Error consulting oracle: ${errorMsg}`, }, ], isError: true, }; expect(response.isError).toBe(true); expect(response.content[0].text).toContain("Error consulting oracle"); expect(response.content[0].text).toContain(errorMsg); }); }); describe("Configuration", () => { it("should have a default Oracle config constant", () => { const DEFAULT_ORACLE_CONFIG = { models: ["gpt-5.1-codex-mini"], reasoning: "medium", command: "codex", }; expect(DEFAULT_ORACLE_CONFIG.models[0]).toBe("gpt-5.1-codex-mini"); expect(DEFAULT_ORACLE_CONFIG.reasoning).toBe("medium"); expect(DEFAULT_ORACLE_CONFIG.command).toBe("codex"); }); it("should accept custom oracle config with single model", () => { const customConfig = { model: "gpt-6.0", reasoning: "high", command: "custom-codex", }; expect(customConfig.model).toBe("gpt-6.0"); expect(customConfig.reasoning).toBe("high"); expect(customConfig.command).toBe("custom-codex"); }); it("should accept custom oracle config with multiple models", () => { const customConfig = { models: ["gpt-5.1", "gpt-5.1-codex-max"], reasoning: "high", command: "codex", }; expect(customConfig.models).toBeDefined(); expect(customConfig.models.length).toBe(2); expect(customConfig.models[0]).toBe("gpt-5.1"); expect(customConfig.models[1]).toBe("gpt-5.1-codex-max"); }); it("should allow optional reasoning field", () => { const config: Record<string, unknown> = { model: "gpt-5.1", command: "codex", }; expect(config.model).toBe("gpt-5.1"); expect(config.reasoning).toBeUndefined(); }); it("should support single oracle config with single model (backward compatible)", () => { const config = { oracle: { model: "gpt-5.1", command: "codex", }, }; expect(config.oracle).toBeDefined(); expect(config.oracle.model).toBe("gpt-5.1"); }); it("should support multiple oracles config with single models each", () => { const config = { oracles: [ { model: "gpt-5.1", command: "codex" }, { model: "gemini-2.5-pro", command: "gemini" }, { model: "opus", command: "claude" }, ], }; expect(config.oracles).toBeDefined(); expect(config.oracles.length).toBe(3); expect(config.oracles[0].model).toBe("gpt-5.1"); expect(config.oracles[1].model).toBe("gemini-2.5-pro"); expect(config.oracles[2].model).toBe("opus"); }); it("should support oracle config with multiple models per oracle", () => { const config = { oracles: [ { models: ["gpt-5.1", "gpt-5.1-codex-max"], command: "codex" }, { models: ["gemini-2.5-pro", "gemini-1.5-pro"], command: "gemini" }, { models: ["opus", "sonnet"], command: "claude" }, ], }; expect(config.oracles).toBeDefined(); expect(config.oracles.length).toBe(3); expect(config.oracles[0].models).toEqual(["gpt-5.1", "gpt-5.1-codex-max"]); expect(config.oracles[1].models).toEqual(["gemini-2.5-pro", "gemini-1.5-pro"]); expect(config.oracles[2].models).toEqual(["opus", "sonnet"]); }); it("should support mixed single and multiple models across oracles", () => { const config = { oracles: [ { models: ["gpt-5.1", "gpt-5.1-codex-max"], command: "codex" }, { model: "gemini-2.5-pro", command: "gemini" }, { models: ["opus"], command: "claude" }, ], }; expect(config.oracles[0].models).toEqual(["gpt-5.1", "gpt-5.1-codex-max"]); expect(config.oracles[1].model).toBe("gemini-2.5-pro"); expect(config.oracles[2].models).toEqual(["opus"]); }); }); describe("Tool descriptions", () => { it("should generate description with reasoning when specified", () => { const model = "gpt-5.1-codex-mini"; const command = "codex"; const reasoning = "medium"; const baseDescription = `Consult the oracle (${model}) via ${command} CLI`; const reasoningPart = reasoning ? ` with ${reasoning}-level reasoning` : ""; const capabilityPart = ". The oracle provides expert reasoning and analysis for complex problem-solving."; const usagePart = " Use when: (1) planning complex tasks with multiple tradeoffs, (2) you are <=90% confident in your approach, (3) you need analysis of architectural decisions or design patterns."; const description = baseDescription + reasoningPart + capabilityPart + usagePart; expect(description).toContain(model); expect(description).toContain(command); expect(description).toContain(reasoning); expect(description).toContain("planning complex tasks"); expect(description).toContain("expert reasoning"); }); it("should generate description without reasoning when undefined", () => { const model = "gpt-5.1-codex-mini"; const command = "codex"; const reasoning = undefined; const baseDescription = `Consult the oracle (${model}) via ${command} CLI`; const reasoningPart = reasoning ? ` with ${reasoning}-level reasoning` : ""; const capabilityPart = ". The oracle provides expert reasoning and analysis for complex problem-solving."; const usagePart = " Use when: (1) planning complex tasks with multiple tradeoffs, (2) you are <=90% confident in your approach, (3) you need analysis of architectural decisions or design patterns."; const description = baseDescription + reasoningPart + capabilityPart + usagePart; expect(description).not.toContain("reasoning-level"); expect(description).toContain(model); expect(description).toContain(command); expect(description).toContain("expert reasoning"); }); }); describe("Type safety", () => { it("should handle dynamic args as Record<string, unknown>", () => { const args: Record<string, unknown> = { prompt: "test prompt", extra: 123, }; const prompt = (args as Record<string, unknown>).prompt as string; expect(typeof prompt).toBe("string"); expect(prompt).toBe("test prompt"); }); it("should handle missing prompt in args", () => { const args = {} as Record<string, unknown>; const prompt = (args as Record<string, unknown>).prompt as string; expect(prompt).toBeUndefined(); }); it("should safely cast different types", () => { const args: Record<string, unknown> = { value: 42 }; const value = (args.value as unknown) as number; expect(typeof value).toBe("number"); }); }); describe("Response format", () => { it("should wrap success response correctly", () => { const stdout = "Success output"; const response = { content: [ { type: "text", text: stdout, }, ], }; expect(response.content[0].type).toBe("text"); expect(response.content[0].text).toBe(stdout); expect((response as Record<string, unknown>).isError).toBeUndefined(); }); it("should wrap error response with isError flag", () => { const errorText = "Error consulting oracle: test error"; const response = { content: [ { type: "text", text: errorText, }, ], isError: true, }; expect(response.isError).toBe(true); expect(response.content[0].text).toContain("Error consulting oracle"); }); it("should handle multiline responses", () => { const stdout = "line 1\nline 2\nline 3"; const response = { content: [ { type: "text", text: stdout, }, ], }; expect(response.content[0].text).toContain("line 1"); expect(response.content[0].text).toContain("line 2"); expect(response.content[0].text).toContain("line 3"); }); }); describe("Unknown tool handling", () => { it("should return error for unknown tool", () => { const toolName = "unknown_tool"; const response = { content: [ { type: "text", text: `Unknown tool: ${toolName}`, }, ], isError: true, }; expect(response.isError).toBe(true); expect(response.content[0].text).toContain("Unknown tool"); expect(response.content[0].text).toContain(toolName); }); }); describe("Fallback behavior", () => { it("should track errors from multiple failed oracles with single models", () => { const errors = [ "Oracle 1, model 1 (gpt-5.1): Connection failed", "Oracle 2, model 1 (gemini-2.5-pro): Timeout", "Oracle 3, model 1 (opus): Rate limited", ]; const response = { content: [ { type: "text", text: `All oracles failed:\n${errors.join("\n")}`, }, ], isError: true, }; expect(response.isError).toBe(true); expect(response.content[0].text).toContain("All oracles failed"); expect(response.content[0].text).toContain("gpt-5.1"); expect(response.content[0].text).toContain("gemini-2.5-pro"); expect(response.content[0].text).toContain("opus"); }); it("should track errors from multiple models within a single oracle", () => { const errors = [ "Oracle 1, model 1 (gpt-5.1): Rate limited", "Oracle 1, model 2 (gpt-5.1-codex-max): Timeout", "Oracle 2, model 1 (gemini-2.5-pro): Connection failed", ]; const response = { content: [ { type: "text", text: `All oracles failed:\n${errors.join("\n")}`, }, ], isError: true, }; expect(response.isError).toBe(true); expect(response.content[0].text).toContain("Oracle 1, model 1"); expect(response.content[0].text).toContain("Oracle 1, model 2"); expect(response.content[0].text).toContain("Oracle 2, model 1"); expect(response.content[0].text).toContain("gpt-5.1"); expect(response.content[0].text).toContain("gpt-5.1-codex-max"); expect(response.content[0].text).toContain("gemini-2.5-pro"); }); it("should format error message with oracle index, model index, and model name", () => { const errorMsg = "Oracle 2, model 3 (gemini-1.5-pro): Connection failed"; expect(errorMsg).toContain("Oracle 2"); expect(errorMsg).toContain("model 3"); expect(errorMsg).toContain("gemini-1.5-pro"); expect(errorMsg).toContain("Connection failed"); }); it("should return success on first successful model", () => { const stdout = "Successfully consulted oracle 1, model 2"; const response = { content: [ { type: "text", text: stdout, }, ], }; expect(response.content[0].text).toBe(stdout); expect((response as Record<string, unknown>).isError).toBeUndefined(); }); it("should accumulate all oracle and model errors when all fail", () => { const errors = [ "Oracle 1, model 1 (gpt-5.1): Error A", "Oracle 1, model 2 (gpt-5.1-codex-max): Error B", "Oracle 2, model 1 (gemini-2.5-pro): Error C", ]; const fullErrorMessage = errors.join("\n"); expect(fullErrorMessage).toContain("Oracle 1, model 1"); expect(fullErrorMessage).toContain("Oracle 1, model 2"); expect(fullErrorMessage).toContain("Oracle 2, model 1"); expect(fullErrorMessage).toContain("Error A"); expect(fullErrorMessage).toContain("Error B"); expect(fullErrorMessage).toContain("Error C"); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/becksclair/oracle-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server