| 1 | import { describe, expect, it } from "@effect/vitest"; |
| 2 | import { mcpToolPresentation } from "./mcpToolPresentation.ts"; |
| 3 | |
| 4 | describe("mcpToolPresentation", () => { |
| 5 | it("uses supplied names and logos while retaining the server identity", () => { |
| 6 | expect( |
| 7 | mcpToolPresentation({ |
| 8 | serverName: "firecrawl-local", |
| 9 | toolName: "firecrawl_scrape", |
| 10 | title: " Scrape\n page ", |
| 11 | serverDisplayName: "Firecrawl", |
| 12 | iconUrl: "https://example.com/icon.png", |
| 13 | iconUrlDark: "https://example.com/dark.png", |
| 14 | }), |
| 15 | ).toEqual({ |
| 16 | title: "Scrape page", |
| 17 | toolIcon: { |
| 18 | _tag: "themed-logo", |
| 19 | logoUrl: "https://example.com/icon.png", |
| 20 | logoUrlDark: "https://example.com/dark.png", |
| 21 | }, |
| 22 | toolSource: { |
| 23 | key: "mcp:firecrawl-local", |
| 24 | name: "Firecrawl", |
| 25 | kind: "integration", |
| 26 | icon: { |
| 27 | _tag: "themed-logo", |
| 28 | logoUrl: "https://example.com/icon.png", |
| 29 | logoUrlDark: "https://example.com/dark.png", |
| 30 | }, |
| 31 | }, |
| 32 | }); |
| 33 | }); |
| 34 | |
| 35 | it("uses MCP result source metadata for the tool and its integration", () => { |
| 36 | const presentation = mcpToolPresentation({ |
| 37 | serverName: "firecrawl-local", |
| 38 | toolName: "firecrawl_scrape", |
| 39 | source: { |
| 40 | name: "Firecrawl", |
| 41 | logoUrl: "https://example.com/firecrawl.png", |
| 42 | logoUrlDark: "https://example.com/firecrawl-dark.png", |
| 43 | }, |
| 44 | }); |
| 45 | expect(presentation.toolSource?.name).toBe("Firecrawl"); |
| 46 | expect(presentation.toolIcon).toEqual({ |
| 47 | _tag: "themed-logo", |
| 48 | logoUrl: "https://example.com/firecrawl.png", |
| 49 | logoUrlDark: "https://example.com/firecrawl-dark.png", |
| 50 | }); |
| 51 | expect(presentation.toolSource?.icon).toEqual(presentation.toolIcon); |
| 52 | }); |
| 53 | |
| 54 | it("uses readable fallback names without a guessed logo", () => { |
| 55 | expect(mcpToolPresentation({ toolName: "mcp__my_server__get_weather" })).toEqual({ |
| 56 | title: "get weather", |
| 57 | toolSource: { key: "mcp:my_server", name: "my server", kind: "integration" }, |
| 58 | }); |
| 59 | expect(mcpToolPresentation({ toolName: "Read" })).toEqual({}); |
| 60 | expect(mcpToolPresentation({ serverName: "t3-code", toolName: "delegate_task" })).toEqual({}); |
| 61 | }); |
| 62 | |
| 63 | it.each([ |
| 64 | "javascript:alert(1)", |
| 65 | "file:///tmp/logo.png", |
| 66 | "not a URL", |
| 67 | "https://example.com/" + "x".repeat(4096), |
| 68 | ])("ignores an invalid logo %s", (iconUrl) => { |
| 69 | expect( |
| 70 | mcpToolPresentation({ |
| 71 | serverName: "weather", |
| 72 | toolName: "get_weather", |
| 73 | iconUrl, |
| 74 | title: "x".repeat(161), |
| 75 | }), |
| 76 | ).toEqual({ |
| 77 | title: "get weather", |
| 78 | toolSource: { key: "mcp:weather", name: "weather", kind: "integration" }, |
| 79 | }); |
| 80 | }); |
| 81 | |
| 82 | it("rejects malformed and overlong names", () => { |
| 83 | expect(mcpToolPresentation({ serverName: {}, toolName: 1, title: [] })).toEqual({}); |
| 84 | expect(mcpToolPresentation({ serverName: "x".repeat(161), toolName: "get_weather" })).toEqual( |
| 85 | {}, |
| 86 | ); |
| 87 | expect(mcpToolPresentation({ title: "Tool title" })).toEqual({ title: "Tool title" }); |
| 88 | }); |
| 89 | |
| 90 | it.each([ |
| 91 | null, |
| 92 | "logo", |
| 93 | { logoUrl: "file:///tmp/logo.png" }, |
| 94 | { logoUrlDark: "https://example.com/dark.png" }, |
| 95 | ])("ignores source metadata without a valid logo: %j", (source) => { |
| 96 | expect(mcpToolPresentation({ serverName: "weather", toolName: "get_weather", source })).toEqual( |
| 97 | { |
| 98 | title: "get weather", |
| 99 | toolSource: { key: "mcp:weather", name: "weather", kind: "integration" }, |
| 100 | }, |
| 101 | ); |
| 102 | }); |
| 103 | }); |