UNPKG

@translated/lara-mcp

Version:

Lara API official MCP server

96 lines (95 loc) 4.35 kB
import { describe, it, expect, beforeEach, vi } from "vitest"; import { Translator } from "@translated/lara"; // Stub the logger before importing `mcp/tools.js`: the real logger pulls in // `src/env.ts`, which parses `process.env` at module load. vi.mock("#logger", () => ({ logger: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() }, })); vi.mock("@translated/lara", async (importOriginal) => { const actual = await importOriginal(); const { createMockTranslator } = await import("../utils/mocks.js"); return { ...actual, Translator: vi.fn(() => createMockTranslator()), }; }); import { CallTool, ListTools } from "../../mcp/tools.js"; import { listMemoriesOutputSchema } from "../../mcp/tools/list_memories.js"; import { createMemoryOutputSchema } from "../../mcp/tools/create_memory.js"; import { getGlossaryOutputSchema } from "../../mcp/tools/get_glossary.js"; function makeRequest(name, args = {}) { return { method: "tools/call", params: { name, arguments: args }, }; } describe("Tool outputSchema declarations", () => { it("every tool advertises a valid outputSchema", async () => { const { tools } = await ListTools(); expect(tools.length).toBeGreaterThan(0); for (const tool of tools) { const schema = tool .outputSchema; expect(schema, `tool "${tool.name}" is missing outputSchema`).toBeDefined(); expect(schema, `tool "${tool.name}" outputSchema`).toBeTypeOf("object"); const s = schema; // MCP SDK's ToolSchema (types.d.ts) requires outputSchema's root to be // type: "object". A bare anyOf/oneOf at the root would be rejected by // strict clients that parse tools/list against the published schema. expect(s.type, `tool "${tool.name}" outputSchema root must be type:"object"`).toBe("object"); } }); }); describe("structuredContent conforms to outputSchema", () => { let mockTranslator; beforeEach(() => { mockTranslator = new Translator(); }); it("list_memories result matches listMemoriesOutputSchema", async () => { const memory = { id: "mem_1", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z", sharedAt: "2026-01-02T00:00:00Z", name: "M1", ownerId: "user_1", collaboratorsCount: 0, isPersonal: true, }; mockTranslator.memories.list.mockResolvedValue([memory]); const res = await CallTool(makeRequest("list_memories"), mockTranslator); expect(() => listMemoriesOutputSchema.parse(res.structuredContent)).not.toThrow(); }); it("create_memory result matches createMemoryOutputSchema", async () => { const memory = { id: "mem_42", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z", sharedAt: "2026-01-02T00:00:00Z", name: "brand_names", ownerId: "user_1", collaboratorsCount: 0, isPersonal: true, }; mockTranslator.memories.create.mockResolvedValue(memory); const res = await CallTool(makeRequest("create_memory", { name: "brand_names" }), mockTranslator); expect(() => createMemoryOutputSchema.parse(res.structuredContent)).not.toThrow(); }); it("get_glossary result matches the schema for both hit and miss", async () => { const glossary = { id: "gls_abc", name: "Brand", ownerId: "user_1", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z", isPersonal: true, }; mockTranslator.glossaries.get.mockResolvedValueOnce(glossary); const hit = await CallTool(makeRequest("get_glossary", { id: "gls_abc" }), mockTranslator); expect(() => getGlossaryOutputSchema.parse(hit.structuredContent)).not.toThrow(); mockTranslator.glossaries.get.mockResolvedValueOnce(null); const miss = await CallTool(makeRequest("get_glossary", { id: "gls_missing" }), mockTranslator); expect(miss.structuredContent).toEqual({ glossary: null }); expect(() => getGlossaryOutputSchema.parse(miss.structuredContent)).not.toThrow(); }); });