Forked from dirty-data/rag-v2
Project Files
scripts / smoke-mcp-filesystem.ts
import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { createDefaultMcpRuntime } from "../packages/mcp-server/src/defaultRuntime";
import { createMcpToolHandlers } from "../packages/mcp-server/src/handlers";
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
async function main() {
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "rag-v2-mcp-"));
const homeTempRoot = await mkdtemp(path.join(os.homedir(), "rag-v2-home-"));
try {
await writeFile(
path.join(tempRoot, "architecture.md"),
"# Architecture\nThe session service uses PostgreSQL for durable state.\n\n## Analytics\nAnalytics uses ClickHouse for dashboards.\n",
"utf8"
);
await mkdir(path.join(tempRoot, "docs"));
await writeFile(
path.join(tempRoot, "docs", "tradeoffs.txt"),
"Tradeoff summary\nHigher write latency is accepted to preserve failover consistency.\n",
"utf8"
);
await mkdir(path.join(tempRoot, "node_modules"));
await writeFile(
path.join(tempRoot, "node_modules", "ignored.txt"),
"Ignored dependency text\nThis file should not be ingested by corpus loading.\n",
"utf8"
);
await mkdir(path.join(tempRoot, ".git"));
await writeFile(
path.join(tempRoot, ".git", "ignored.md"),
"Ignored git metadata\nThis file should not be ingested by corpus loading.\n",
"utf8"
);
await writeFile(
path.join(homeTempRoot, "tilde-path.md"),
"Path expansion proof\nThe deployment lives under the user's home projects directory.\n",
"utf8"
);
const handlers = createMcpToolHandlers(createDefaultMcpRuntime());
const browse = await handlers.filesystemBrowse({
path: tempRoot,
recursive: true,
maxDepth: 2,
maxEntries: 20,
});
assert(browse.exists, "Expected filesystem_browse to resolve the temporary root.");
assert(browse.type === "directory", "Expected filesystem_browse to report a directory.");
assert(
browse.entries.some((entry) => entry.name === "architecture.md"),
"Expected filesystem_browse to include the architecture file."
);
assert(
browse.entries.some((entry) => entry.name === "docs" && entry.type === "directory"),
"Expected filesystem_browse to include the docs directory."
);
assert(
browse.entries.some((entry) => entry.name === "node_modules" && entry.type === "directory"),
"Expected filesystem_browse to include node_modules when browsing."
);
assert(browse.directoryCount === 2, "Expected filesystem_browse to summarize two visible directories.");
assert(browse.fileCount === 1, "Expected filesystem_browse to summarize one visible file at the root.");
assert(
browse.topExtensions?.some((entry) => entry.extension === ".md" && entry.count === 1),
"Expected filesystem_browse to summarize the markdown extension count."
);
const info = await handlers.fileInfo({
path: path.join(tempRoot, "architecture.md"),
});
assert(info.exists, "Expected file_info to resolve the architecture file.");
assert(info.type === "file", "Expected file_info to report a file.");
assert(info.extension === ".md", "Expected file_info to report the markdown extension.");
assert(info.textLike === true, "Expected file_info to mark architecture.md as text-like.");
const dirInfo = await handlers.fileInfo({
path: tempRoot,
});
assert(dirInfo.exists, "Expected file_info to resolve the temporary root directory.");
assert(dirInfo.type === "directory", "Expected file_info to report a directory.");
assert(dirInfo.childCount === 3, "Expected file_info to report three visible children at the root.");
assert(dirInfo.directoryCount === 2, "Expected file_info to report two visible directories.");
assert(dirInfo.fileCount === 1, "Expected file_info to report one visible file.");
const excerpt = await handlers.readFile({
path: path.join(tempRoot, "architecture.md"),
startLine: 0,
maxLines: 2,
maxChars: 200,
});
assert(excerpt.exists, "Expected read_file to resolve the architecture file.");
assert(
excerpt.content?.includes("session service uses PostgreSQL"),
"Expected read_file to return the requested text excerpt."
);
const inspect = await handlers.corpusInspect({
paths: [tempRoot],
});
assert(inspect.fileCount === 2, "Expected filesystem corpus inspection to load two text files.");
const search = await handlers.ragSearch({
query: "failover consistency tradeoff",
paths: [tempRoot],
});
assert(search.candidates.length > 0, "Expected filesystem rag_search to return candidates.");
assert(
search.candidates.some((candidate) => candidate.sourceName.includes("tradeoffs.txt")),
"Expected filesystem rag_search to include the matching tradeoffs file."
);
assert(
!search.candidates.some((candidate) => candidate.sourceName.includes("ignored.txt")),
"Expected filesystem rag_search to ignore files under node_modules during corpus loading."
);
const answer = await handlers.ragAnswer({
query: "What database does the session service use?",
paths: [tempRoot],
});
assert(answer.evidence.length > 0, "Expected filesystem rag_answer to produce evidence.");
assert(
answer.evidence.some((block) => block.fileName.includes("architecture.md")),
"Expected filesystem rag_answer to cite the architecture file."
);
const tildePath = homeTempRoot.replace(os.homedir(), "~");
const tildeBrowse = await handlers.filesystemBrowse({
path: tildePath,
recursive: false,
maxEntries: 10,
});
assert(tildeBrowse.exists, "Expected tilde-expanded filesystem_browse to resolve.");
assert(
tildeBrowse.resolvedPath === homeTempRoot,
"Expected tilde-expanded filesystem_browse to report the resolved home path."
);
const tildeInspect = await handlers.corpusInspect({
paths: [tildePath],
});
assert(tildeInspect.fileCount === 1, "Expected tilde-expanded corpus inspection to load one text file.");
const tildeSearch = await handlers.ragSearch({
query: "user's home projects directory",
paths: [tildePath],
});
assert(tildeSearch.candidates.length > 0, "Expected tilde-expanded rag_search to return candidates.");
assert(
tildeSearch.candidates.some((candidate) => candidate.sourceName.includes("tilde-path.md")),
"Expected tilde-expanded rag_search to include the tilde-path file."
);
console.log("MCP filesystem smoke test passed.");
} finally {
await rm(tempRoot, { recursive: true, force: true });
await rm(homeTempRoot, { recursive: true, force: true });
}
}
main().catch((error) => {
const message = error instanceof Error ? error.message : String(error);
console.error(`MCP filesystem smoke test failed: ${message}`);
process.exit(1);
});