import { describe, test, expect, mock, afterEach } from "bun:test";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerAllTools } from "../src/tools/index.js";
// Helper to create server and extract tool handlers
function createTestServer() {
const server = new McpServer({ name: "test", version: "1.0.0" });
registerAllTools(server);
return server;
}
describe("Tool Registration", () => {
test("registers all 9 tools", () => {
const server = createTestServer();
// Server should have tools registered (checking internal state)
expect(server).toBeDefined();
});
});
describe("Wikipedia Tools Integration", () => {
const originalFetch = global.fetch;
afterEach(() => {
global.fetch = originalFetch;
});
describe("wiki_search", () => {
test("returns search results", async () => {
const mockResponse = {
query: {
search: [
{ title: "JavaScript", snippet: "Programming <span>language</span>", pageid: 123 },
{ title: "TypeScript", snippet: "Typed superset", pageid: 456 },
],
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
// Import and test the tool directly
const { queryWikipedia, jsonResponse } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
list: "search",
srsearch: "JavaScript",
srlimit: "10",
});
const results = data.query?.search?.map((result) => ({
title: result.title,
snippet: result.snippet.replace(/<[^>]*>/g, ""),
pageid: result.pageid,
})) ?? [];
expect(results).toHaveLength(2);
expect(results[0].title).toBe("JavaScript");
expect(results[0].snippet).toBe("Programming language"); // HTML stripped
expect(results[0].pageid).toBe(123);
});
});
describe("wiki_get_article", () => {
test("returns article content", async () => {
const mockResponse = {
query: {
pages: {
"123": {
pageid: 123,
title: "JavaScript",
extract: "JavaScript is a programming language.",
},
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "JavaScript",
prop: "extracts",
explaintext: "true",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
expect(page?.extract).toBe("JavaScript is a programming language.");
});
test("handles missing article", async () => {
const mockResponse = {
query: {
pages: {
"-1": { missing: true },
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "NonexistentArticle12345",
prop: "extracts",
explaintext: "true",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
expect(page?.missing).toBe(true);
});
});
describe("wiki_get_categories", () => {
test("returns categories", async () => {
const mockResponse = {
query: {
pages: {
"123": {
pageid: 123,
title: "JavaScript",
categories: [
{ title: "Category:Programming languages" },
{ title: "Category:Web development" },
],
},
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "JavaScript",
prop: "categories",
cllimit: "50",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
const categories = page?.categories?.map((cat) =>
cat.title.replace(/^Category:/, "")
) ?? [];
expect(categories).toHaveLength(2);
expect(categories[0]).toBe("Programming languages");
});
});
describe("wiki_get_links", () => {
test("returns internal links", async () => {
const mockResponse = {
query: {
pages: {
"123": {
pageid: 123,
title: "JavaScript",
links: [
{ title: "ECMAScript", ns: 0 },
{ title: "Web browser", ns: 0 },
],
},
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "JavaScript",
prop: "links",
pllimit: "100",
plnamespace: "0",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
const links = page?.links?.map((link) => link.title) ?? [];
expect(links).toHaveLength(2);
expect(links).toContain("ECMAScript");
});
});
describe("wiki_get_images", () => {
test("returns images", async () => {
const mockResponse = {
query: {
pages: {
"123": {
pageid: 123,
title: "JavaScript",
images: [
{ title: "File:JavaScript-logo.png" },
{ title: "File:Example.svg" },
],
},
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "JavaScript",
prop: "images",
imlimit: "50",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
const images = page?.images?.map((img) => ({
title: img.title.replace(/^File:/, ""),
})) ?? [];
expect(images).toHaveLength(2);
expect(images[0].title).toBe("JavaScript-logo.png");
});
});
describe("wiki_get_languages", () => {
test("returns language links", async () => {
const mockResponse = {
query: {
pages: {
"123": {
pageid: 123,
title: "JavaScript",
langlinks: [
{ lang: "de", url: "https://de.wikipedia.org/wiki/JavaScript", autonym: "Deutsch", title: "JavaScript" },
{ lang: "fr", url: "https://fr.wikipedia.org/wiki/JavaScript", autonym: "Français", title: "JavaScript" },
],
},
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "JavaScript",
prop: "langlinks",
lllimit: "500",
llprop: "url|autonym",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
const languages = page?.langlinks ?? [];
expect(languages).toHaveLength(2);
expect(languages[0].lang).toBe("de");
expect(languages[0].autonym).toBe("Deutsch");
});
});
describe("wiki_get_references", () => {
test("returns external links", async () => {
const mockResponse = {
query: {
pages: {
"123": {
pageid: 123,
title: "JavaScript",
extlinks: [
{ "*": "https://developer.mozilla.org/" },
{ "*": "https://ecma-international.org/" },
],
},
},
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
titles: "JavaScript",
prop: "extlinks",
ellimit: "50",
});
const pages = data.query?.pages ?? {};
const page = Object.values(pages)[0];
const references = page?.extlinks?.map((link) => link["*"]) ?? [];
expect(references).toHaveLength(2);
expect(references).toContain("https://developer.mozilla.org/");
});
});
describe("wiki_random", () => {
test("returns random articles", async () => {
const mockResponse = {
query: {
random: [
{ id: 111, title: "Random Article 1" },
{ id: 222, title: "Random Article 2" },
],
},
};
global.fetch = mock(async () => new Response(JSON.stringify(mockResponse)));
const { queryWikipedia } = await import("../src/api.js");
const data = await queryWikipedia<typeof mockResponse>({
action: "query",
list: "random",
rnnamespace: "0",
rnlimit: "5",
});
const articles = data.query?.random ?? [];
expect(articles).toHaveLength(2);
expect(articles[0].title).toBe("Random Article 1");
});
});
});