File Rank MCP Server
by admica
Verified
import * as fs from "fs/promises";
import * as path from "path";
import { FileNode } from "./types.js";
export function normalizePath(filepath: string): string {
return filepath.split(path.sep).join('/');
}
export function toPlatformPath(normalizedPath: string): string {
return normalizedPath.split('/').join(path.sep);
}
const SUPPORTED_EXTENSIONS = [".py", ".c", ".cpp", ".h", ".rs", ".lua", ".js", ".jsx", ".ts", ".tsx"];
const IMPORT_PATTERNS: { [key: string]: RegExp } = {
".py": /import\s+[\w.]+|from\s+[\w.]+\s+import\s+[\w*]+/g,
".c": /#include\s+["<][^">]+[">]/g,
".cpp": /#include\s+["<][^">]+[">]/g,
".h": /#include\s+["<][^">]+[">]/g,
".rs": /use\s+[\w:]+|mod\s+\w+/g,
".lua": /require\s*\(['"][^'"]+['"]\)/g,
".js": /import\s+.*from\s+['"][^'"]+['"]/g,
".jsx": /import\s+.*from\s+['"][^'"]+['"]/g,
".ts": /import\s+.*from\s+['"][^'"]+['"]/g,
".tsx": /import\s+.*from\s+['"][^'"]+['"]/g,
};
export async function scanDirectory(dirPath: string): Promise<FileNode> {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
const normalizedDirPath = normalizePath(dirPath);
const node: FileNode = {
path: normalizedDirPath,
name: path.basename(dirPath),
isDirectory: true,
children: [],
};
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
const normalizedFullPath = normalizePath(fullPath);
const childNode: FileNode = {
path: normalizedFullPath,
name: entry.name,
isDirectory: entry.isDirectory(),
};
if (entry.isDirectory()) {
if (!fullPath.includes("node_modules") && !fullPath.includes(".git")) {
childNode.children = (await scanDirectory(fullPath)).children;
}
} else if (SUPPORTED_EXTENSIONS.includes(path.extname(entry.name))) {
try {
const content = await fs.readFile(fullPath, "utf-8");
const ext = path.extname(entry.name);
const matches = content.match(IMPORT_PATTERNS[ext]) || [];
childNode.dependencies = await Promise.all(
matches
.map(match => {
const importPath = match.match(/['"]([^'"]+)['"]/)?.[1] || match.split(/\s+/)[1];
const resolvedPath = path.resolve(dirPath, importPath + (ext === ".py" ? ".py" : ""));
return normalizePath(resolvedPath);
})
.map(async dep => {
try {
await fs.access(toPlatformPath(dep));
return dep;
} catch {
return null;
}
})
).then(results => results.filter(dep => dep !== null) as string[]);
} catch (e) {
console.warn(`Failed to read ${fullPath}: ${e}`);
}
}
node.children!.push(childNode);
}
return node;
}
export function calculateImportance(node: FileNode) {
if (!node.isDirectory && node.dependencies) {
const depCount = node.dependencies.length;
node.importance = Math.min(10, Math.max(0, Math.floor(depCount / 2)));
}
if (node.children) {
node.children.forEach(calculateImportance);
}
}