Skip to main content
Glama

MCP Probe Kit

by mybolide
analyze_project.ts15 kB
import { readFileSync, readdirSync, statSync, existsSync } from 'fs'; import { join, extname, basename } from 'path'; interface ProjectAnalysis { projectStructure: { name: string; type: string; framework: string; language: string; packageManager: string; }; directoryTree: string; keyFiles: Array<{ path: string; purpose: string; content: string; }>; dependencies: { production: string[]; development: string[]; total: number; }; codeMetrics: { totalFiles: number; totalLines: number; fileTypes: Record<string, number>; largestFiles: Array<{ path: string; lines: number; }>; }; architecture: { patterns: string[]; entryPoints: string[]; mainModules: string[]; }; summary: { purpose: string; complexity: 'low' | 'medium' | 'high'; recommendations: string[]; }; } export async function analyzeProject(args: any): Promise<any> { const projectPath = args.project_path || process.cwd(); const maxDepth = args.max_depth || 5; const includeContent = args.include_content !== false; try { const analysis = await performProjectAnalysis(projectPath, maxDepth, includeContent); return { content: [ { type: "text", text: `# 📊 项目分析报告 ## 🏗️ 项目概览 - **项目名称**: ${analysis.projectStructure.name} - **项目类型**: ${analysis.projectStructure.type} - **技术栈**: ${analysis.projectStructure.framework} - **主要语言**: ${analysis.projectStructure.language} - **包管理器**: ${analysis.projectStructure.packageManager} ## 📁 目录结构 \`\`\` ${analysis.directoryTree} \`\`\` ## 🔑 关键文件 ${analysis.keyFiles.map(file => `### ${file.path} **用途**: ${file.purpose} ${includeContent ? `\`\`\`${getFileExtension(file.path)} ${file.content.substring(0, 500)}${file.content.length > 500 ? '\n...' : ''} \`\`\`` : ''}`).join('\n\n')} ## 📦 依赖分析 - **生产依赖**: ${analysis.dependencies.production.length} 个 - **开发依赖**: ${analysis.dependencies.development.length} 个 - **总依赖数**: ${analysis.dependencies.total} 个 ### 主要依赖 ${analysis.dependencies.production.slice(0, 10).map(dep => `- ${dep}`).join('\n')} ## 📈 代码指标 - **总文件数**: ${analysis.codeMetrics.totalFiles} - **总行数**: ${analysis.codeMetrics.totalLines} - **文件类型分布**: ${Object.entries(analysis.codeMetrics.fileTypes).map(([type, count]) => ` - ${type}: ${count} 个文件` ).join('\n')} ### 最大文件 ${analysis.codeMetrics.largestFiles.slice(0, 5).map(file => `- ${file.path} (${file.lines} 行)` ).join('\n')} ## 🏛️ 架构分析 - **设计模式**: ${analysis.architecture.patterns.join(', ')} - **入口文件**: ${analysis.architecture.entryPoints.join(', ')} - **核心模块**: ${analysis.architecture.mainModules.join(', ')} ## 📋 项目总结 **项目目的**: ${analysis.summary.purpose} **复杂度**: ${analysis.summary.complexity} **建议**: ${analysis.summary.recommendations.map(rec => `- ${rec}`).join('\n')} --- *分析完成时间: ${new Date().toLocaleString('zh-CN')}* *分析工具: MCP Probe Kit v1.2.0*`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `❌ 项目分析失败: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } async function performProjectAnalysis( projectPath: string, maxDepth: number, includeContent: boolean ): Promise<ProjectAnalysis> { // 读取 package.json const packageJson = await readPackageJson(projectPath); // 分析项目结构 const projectStructure = analyzeProjectStructure(packageJson); // 生成目录树 const directoryTree = generateDirectoryTree(projectPath, maxDepth); // 识别关键文件 const keyFiles = await identifyKeyFiles(projectPath, includeContent); // 分析依赖 const dependencies = analyzeDependencies(packageJson); // 代码指标 const codeMetrics = await calculateCodeMetrics(projectPath); // 架构分析 const architecture = await analyzeArchitecture(projectPath, keyFiles); // 生成总结 const summary = generateSummary(projectStructure, codeMetrics, architecture); return { projectStructure, directoryTree, keyFiles, dependencies, codeMetrics, architecture, summary, }; } async function readPackageJson(projectPath: string): Promise<any> { try { const packageJsonPath = join(projectPath, 'package.json'); const content = readFileSync(packageJsonPath, 'utf-8'); return JSON.parse(content); } catch { return {}; } } function analyzeProjectStructure(packageJson: any) { const name = packageJson.name || 'unknown'; const type = detectProjectType(packageJson); const framework = detectFramework(packageJson); const language = detectLanguage(packageJson); const packageManager = detectPackageManager(); return { name, type, framework, language, packageManager }; } function detectProjectType(packageJson: any): string { if (packageJson.dependencies?.react) return 'React 应用'; if (packageJson.dependencies?.vue) return 'Vue 应用'; if (packageJson.dependencies?.angular) return 'Angular 应用'; if (packageJson.dependencies?.express) return 'Node.js 后端'; if (packageJson.dependencies?.next) return 'Next.js 应用'; if (packageJson.dependencies?.nuxt) return 'Nuxt.js 应用'; if (packageJson.dependencies?.svelte) return 'Svelte 应用'; if (packageJson.dependencies?.electron) return 'Electron 应用'; if (packageJson.dependencies?.jest || packageJson.devDependencies?.jest) return '测试项目'; if (packageJson.dependencies?.webpack || packageJson.devDependencies?.webpack) return '构建工具'; return '通用项目'; } function detectFramework(packageJson: any): string { const frameworks = []; if (packageJson.dependencies?.react) frameworks.push('React'); if (packageJson.dependencies?.vue) frameworks.push('Vue'); if (packageJson.dependencies?.angular) frameworks.push('Angular'); if (packageJson.dependencies?.express) frameworks.push('Express'); if (packageJson.dependencies?.next) frameworks.push('Next.js'); if (packageJson.dependencies?.nuxt) frameworks.push('Nuxt.js'); if (packageJson.dependencies?.svelte) frameworks.push('Svelte'); if (packageJson.dependencies?.electron) frameworks.push('Electron'); return frameworks.join(', ') || '无框架'; } function detectLanguage(packageJson: any): string { if (packageJson.devDependencies?.typescript) return 'TypeScript'; if (packageJson.dependencies?.typescript) return 'TypeScript'; return 'JavaScript'; } function detectPackageManager(): string { if (existsSync('yarn.lock')) return 'Yarn'; if (existsSync('pnpm-lock.yaml')) return 'pnpm'; return 'npm'; } function generateDirectoryTree(projectPath: string, maxDepth: number): string { const ignoreDirs = ['node_modules', '.git', 'dist', 'build', '.next', '.nuxt']; function buildTree(dir: string, prefix: string = '', depth: number = 0): string { if (depth >= maxDepth) return ''; try { const items = readdirSync(dir) .filter(item => !ignoreDirs.includes(item) && !item.startsWith('.')) .map(item => { const fullPath = join(dir, item); const stat = statSync(fullPath); return { name: item, isDir: stat.isDirectory(), path: fullPath }; }) .sort((a, b) => { if (a.isDir && !b.isDir) return -1; if (!a.isDir && b.isDir) return 1; return a.name.localeCompare(b.name); }); let result = ''; items.forEach((item, index) => { const isLast = index === items.length - 1; const currentPrefix = isLast ? '└── ' : '├── '; const nextPrefix = isLast ? ' ' : '│ '; result += `${prefix}${currentPrefix}${item.name}\n`; if (item.isDir) { result += buildTree(item.path, prefix + nextPrefix, depth + 1); } }); return result; } catch { return ''; } } return buildTree(projectPath); } async function identifyKeyFiles(projectPath: string, includeContent: boolean) { const keyFilePatterns = [ 'package.json', 'README.md', 'index.js', 'index.ts', 'main.js', 'main.ts', 'app.js', 'app.ts', 'server.js', 'server.ts', 'config.js', 'config.ts', 'webpack.config.js', 'vite.config.js', 'next.config.js', 'nuxt.config.js', 'tsconfig.json', 'babel.config.js', '.env', '.env.example' ]; const keyFiles = []; for (const pattern of keyFilePatterns) { try { const filePath = join(projectPath, pattern); const content = readFileSync(filePath, 'utf-8'); const purpose = getFilePurpose(pattern, content); keyFiles.push({ path: pattern, purpose, content: includeContent ? content : '' }); } catch { // 文件不存在,跳过 } } return keyFiles; } function getFilePurpose(filename: string, content: string): string { const purposes: Record<string, string> = { 'package.json': '项目配置和依赖管理', 'README.md': '项目说明文档', 'index.js': '项目入口文件', 'index.ts': 'TypeScript 项目入口文件', 'main.js': '主程序文件', 'main.ts': 'TypeScript 主程序文件', 'app.js': '应用程序主文件', 'app.ts': 'TypeScript 应用程序主文件', 'server.js': '服务器文件', 'server.ts': 'TypeScript 服务器文件', 'config.js': '配置文件', 'config.ts': 'TypeScript 配置文件', 'webpack.config.js': 'Webpack 构建配置', 'vite.config.js': 'Vite 构建配置', 'next.config.js': 'Next.js 配置', 'nuxt.config.js': 'Nuxt.js 配置', 'tsconfig.json': 'TypeScript 配置', 'babel.config.js': 'Babel 配置', '.env': '环境变量配置', '.env.example': '环境变量示例' }; return purposes[filename] || '配置文件'; } function analyzeDependencies(packageJson: any) { const production = Object.keys(packageJson.dependencies || {}); const development = Object.keys(packageJson.devDependencies || {}); return { production, development, total: production.length + development.length }; } async function calculateCodeMetrics(projectPath: string) { const fileTypes: Record<string, number> = {}; const largestFiles: Array<{ path: string; lines: number }> = []; let totalFiles = 0; let totalLines = 0; function scanDirectory(dir: string) { try { const items = readdirSync(dir); for (const item of items) { const fullPath = join(dir, item); const stat = statSync(fullPath); if (stat.isDirectory()) { if (!['node_modules', '.git', 'dist', 'build'].includes(item)) { scanDirectory(fullPath); } } else { const ext = extname(item); const fileType = ext || 'no-extension'; fileTypes[fileType] = (fileTypes[fileType] || 0) + 1; totalFiles++; try { const content = readFileSync(fullPath, 'utf-8'); const lines = content.split('\n').length; totalLines += lines; largestFiles.push({ path: fullPath, lines }); } catch { // 忽略无法读取的文件 } } } } catch { // 忽略无法访问的目录 } } scanDirectory(projectPath); // 按行数排序,取前10个 largestFiles.sort((a, b) => b.lines - a.lines); return { totalFiles, totalLines, fileTypes, largestFiles: largestFiles.slice(0, 10) }; } async function analyzeArchitecture(projectPath: string, keyFiles: any[]) { const patterns: string[] = []; const entryPoints: string[] = []; const mainModules: string[] = []; // 分析入口文件 keyFiles.forEach(file => { if (file.path.includes('index') || file.path.includes('main') || file.path.includes('app')) { entryPoints.push(file.path); } }); // 检测设计模式 if (keyFiles.some(f => f.path.includes('component'))) patterns.push('组件化'); if (keyFiles.some(f => f.path.includes('service'))) patterns.push('服务层'); if (keyFiles.some(f => f.path.includes('controller'))) patterns.push('MVC'); if (keyFiles.some(f => f.path.includes('middleware'))) patterns.push('中间件'); if (keyFiles.some(f => f.path.includes('hook'))) patterns.push('Hooks'); if (keyFiles.some(f => f.path.includes('store'))) patterns.push('状态管理'); // 识别核心模块 const srcDir = join(projectPath, 'src'); try { const srcItems = readdirSync(srcDir); mainModules.push(...srcItems.filter(item => statSync(join(srcDir, item)).isDirectory() )); } catch { // src 目录不存在 } return { patterns, entryPoints, mainModules }; } function generateSummary( projectStructure: any, codeMetrics: any, architecture: any ) { let complexity: 'low' | 'medium' | 'high' = 'low'; if (codeMetrics.totalFiles > 100 || codeMetrics.totalLines > 10000) { complexity = 'high'; } else if (codeMetrics.totalFiles > 20 || codeMetrics.totalLines > 1000) { complexity = 'medium'; } const purpose = `${projectStructure.type},使用 ${projectStructure.framework} 框架,主要语言为 ${projectStructure.language}`; const recommendations: string[] = []; if (codeMetrics.totalFiles > 50) { recommendations.push('考虑模块化重构,减少文件数量'); } if (codeMetrics.largestFiles[0]?.lines > 500) { recommendations.push('拆分大文件,提高代码可维护性'); } if (architecture.patterns.length === 0) { recommendations.push('引入设计模式,提高代码组织性'); } if (!architecture.entryPoints.length) { recommendations.push('明确项目入口文件'); } return { purpose, complexity, recommendations }; } function getFileExtension(filename: string): string { const ext = extname(filename).slice(1); const extMap: Record<string, string> = { 'js': 'javascript', 'ts': 'typescript', 'jsx': 'jsx', 'tsx': 'tsx', 'vue': 'vue', 'py': 'python', 'java': 'java', 'cpp': 'cpp', 'c': 'c', 'cs': 'csharp', 'php': 'php', 'rb': 'ruby', 'go': 'go', 'rs': 'rust', 'swift': 'swift', 'kt': 'kotlin', 'scala': 'scala', 'html': 'html', 'css': 'css', 'scss': 'scss', 'sass': 'sass', 'less': 'less', 'json': 'json', 'xml': 'xml', 'yaml': 'yaml', 'yml': 'yaml', 'md': 'markdown', 'txt': 'text', 'sql': 'sql', 'sh': 'bash', 'bat': 'batch', 'ps1': 'powershell' }; return extMap[ext] || ext || 'text'; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mybolide/mcp-probe-kit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server