Copies source (no node_modules, dist, .tasks, .wiki, __pycache__) for: - projects-meta-mcp v2.25.0 (TypeScript/Node) - wiki-graph v0.3.1 (TypeScript/Node) - interns-mcp v0.3.3 (Python/FastMCP) .gitignore: exclude lib build artefacts (node_modules, dist, .venv, __pycache__, *.pyc) bootstrap.ps1: add MCP build step — npm install+build for TS servers, venv+pip for Python Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
import { describe, test, expect } from 'vitest';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
import { makeWikiGraphTools } from '../src/tools.js';
|
|
import { GraphCache } from '../src/cache.js';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const FIXTURE = join(here, 'fixtures', 'wiki');
|
|
|
|
function toolMap() {
|
|
const tools = makeWikiGraphTools(new GraphCache());
|
|
return new Map(tools.map((t) => [t.name, t]));
|
|
}
|
|
|
|
function textOf(res: { content: Array<{ type: string; text: string }>; isError?: boolean }) {
|
|
return res.content.map((c) => c.text).join('\n');
|
|
}
|
|
|
|
describe('wiki-graph MCP tools', () => {
|
|
const tools = toolMap();
|
|
|
|
test('exposes exactly the five v1 tools', () => {
|
|
expect([...tools.keys()].sort()).toEqual([
|
|
'backlinks',
|
|
'neighbors',
|
|
'orphans',
|
|
'path',
|
|
'stats',
|
|
]);
|
|
});
|
|
|
|
test('every tool requires a corpus argument', () => {
|
|
for (const t of tools.values()) {
|
|
expect(t.inputSchema.required).toContain('corpus');
|
|
}
|
|
});
|
|
|
|
test('path: returns the chain', () => {
|
|
const res = tools.get('path')!.handler({ corpus: FIXTURE, from: 'a', to: 'c' });
|
|
expect(textOf(res)).toContain('a → b → c');
|
|
});
|
|
|
|
test('path: reports absence', () => {
|
|
const res = tools.get('path')!.handler({ corpus: FIXTURE, from: 'a', to: 'e' });
|
|
expect(textOf(res).toLowerCase()).toContain('no path');
|
|
});
|
|
|
|
test('neighbors: honors depth', () => {
|
|
const res = tools.get('neighbors')!.handler({ corpus: FIXTURE, node: 'a', depth: 2 });
|
|
const txt = textOf(res);
|
|
expect(txt).toContain('b');
|
|
expect(txt).toContain('c');
|
|
});
|
|
|
|
test('backlinks: lists incoming', () => {
|
|
const res = tools.get('backlinks')!.handler({ corpus: FIXTURE, node: 'c' });
|
|
const txt = textOf(res);
|
|
expect(txt).toContain('b');
|
|
expect(txt).toContain('d');
|
|
});
|
|
|
|
test('orphans: lists unlinked pages and dangling targets', () => {
|
|
const res = tools.get('orphans')!.handler({ corpus: FIXTURE });
|
|
const txt = textOf(res);
|
|
expect(txt).toContain('e');
|
|
expect(txt).toContain('<todo-module>');
|
|
});
|
|
|
|
test('stats: reports counts', () => {
|
|
const res = tools.get('stats')!.handler({ corpus: FIXTURE });
|
|
const txt = textOf(res);
|
|
expect(txt).toContain('5');
|
|
expect(txt).toContain('nodes');
|
|
});
|
|
|
|
test('missing corpus is a clean error, not a throw', () => {
|
|
const res = tools.get('stats')!.handler({});
|
|
expect(res.isError).toBe(true);
|
|
expect(textOf(res).toLowerCase()).toContain('corpus');
|
|
});
|
|
});
|