Skip to main content
Glama

documcp

by tosin2013
readme-best-practices.ts27 kB
import { readFile, writeFile, mkdir } from 'fs/promises'; import { join } from 'path'; import { z } from 'zod'; import { MCPToolResponse } from '../types/api.js'; // Input validation schema const ReadmeBestPracticesInputSchema = z.object({ readme_path: z.string().describe('Path to the README file to analyze'), project_type: z .enum(['library', 'application', 'tool', 'documentation', 'framework']) .optional() .default('library') .describe('Type of project for tailored analysis'), generate_template: z .boolean() .optional() .default(false) .describe('Generate README templates and community files'), output_directory: z .string() .optional() .describe('Directory to write generated templates and community files'), include_community_files: z .boolean() .optional() .default(true) .describe('Generate community health files (CONTRIBUTING.md, CODE_OF_CONDUCT.md, etc.)'), target_audience: z .enum(['beginner', 'intermediate', 'advanced', 'mixed']) .optional() .default('mixed') .describe('Target audience for recommendations'), }); type ReadmeBestPracticesInput = z.infer<typeof ReadmeBestPracticesInputSchema>; interface ChecklistItem { category: string; item: string; present: boolean; severity: 'critical' | 'important' | 'recommended'; description: string; example?: string; } interface BestPracticesReport { overallScore: number; grade: string; checklist: ChecklistItem[]; recommendations: string[]; templates: Record<string, string>; communityFiles: Record<string, string>; summary: { criticalIssues: number; importantIssues: number; recommendedImprovements: number; sectionsPresent: number; totalSections: number; estimatedImprovementTime: string; }; } export async function readmeBestPractices(input: Partial<ReadmeBestPracticesInput>): Promise< MCPToolResponse<{ bestPracticesReport: BestPracticesReport; recommendations: string[]; nextSteps: string[]; }> > { const startTime = Date.now(); try { // Validate input with defaults const validatedInput = ReadmeBestPracticesInputSchema.parse(input); const { readme_path, project_type, generate_template, output_directory, include_community_files, target_audience, } = validatedInput; // Read README content let readmeContent = ''; try { readmeContent = await readFile(readme_path, 'utf-8'); } catch (error) { if (!generate_template) { return { success: false, error: { code: 'README_NOT_FOUND', message: 'README file not found. Use generate_template: true to create a new README.', details: error instanceof Error ? error.message : 'Unknown error', resolution: 'Set generate_template: true to create a new README from template', }, metadata: { toolVersion: '1.0.0', executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; } } // Generate checklist based on project type and content const checklist = generateChecklist(readmeContent, project_type, target_audience); // Calculate overall score const { score, grade } = calculateOverallScore(checklist); // Generate recommendations const recommendations = generateRecommendations(checklist, project_type, target_audience); // Generate templates if requested const templates = generate_template ? generateTemplates(project_type, generate_template) : {}; // Generate community files if requested const communityFiles = include_community_files ? generateCommunityFiles(project_type) : {}; // Calculate summary metrics const summary = calculateSummaryMetrics(checklist); // Write files if output directory specified if (output_directory && generate_template) { await writeGeneratedFiles(templates, communityFiles, output_directory, readme_path); } const report: BestPracticesReport = { overallScore: score, grade, checklist, recommendations, templates, communityFiles, summary, }; const nextSteps = generateNextSteps(report.checklist, true, output_directory); return { success: true, data: { bestPracticesReport: report, recommendations, nextSteps, }, metadata: { toolVersion: '1.0.0', executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), analysisId: `readme-best-practices-${Date.now()}`, }, }; } catch (error) { return { success: false, error: { code: 'ANALYSIS_FAILED', message: 'Failed to analyze README best practices', details: error instanceof Error ? error.message : 'Unknown error', resolution: 'Check README file path and permissions, ensure valid project type', }, metadata: { toolVersion: '1.0.0', executionTime: Date.now() - startTime, timestamp: new Date().toISOString(), }, }; } } function generateChecklist( content: string, projectType: string, _targetAudience: string, ): ChecklistItem[] { const checklist: ChecklistItem[] = []; const lines = content.split('\n'); const lowerContent = content.toLowerCase(); // Essential Sections checklist.push({ category: 'Essential Sections', item: 'Project Title', present: /^#\s+.+/m.test(content), severity: 'critical', description: 'Clear, descriptive project title as main heading', example: '# My Awesome Project', }); checklist.push({ category: 'Essential Sections', item: 'One-line Description', present: />\s*.+/.test(content) || lines.some( (line) => line.trim().length > 20 && line.trim().length < 100 && !line.startsWith('#'), ), severity: 'critical', description: 'Brief one-line description of what the project does', example: '> A fast, lightweight JavaScript framework for building web applications', }); checklist.push({ category: 'Essential Sections', item: 'Installation Instructions', present: /install/i.test(lowerContent) && /npm|yarn|pip|cargo|go get|git clone/i.test(lowerContent), severity: 'critical', description: 'Clear installation or setup instructions', example: '```bash\nnpm install package-name\n```', }); checklist.push({ category: 'Essential Sections', item: 'Basic Usage Example', present: /usage|example|quick start|getting started/i.test(lowerContent) && /```/.test(content), severity: 'critical', description: 'Working code example showing basic usage', example: '```javascript\nconst lib = require("package-name");\nlib.doSomething();\n```', }); // Important Sections checklist.push({ category: 'Important Sections', item: 'Prerequisites/Requirements', present: /prerequisite|requirement|dependencies|node|python|java|version/i.test(lowerContent), severity: 'important', description: 'Clear system requirements and dependencies', example: '- Node.js 16+\n- Docker (optional)', }); checklist.push({ category: 'Important Sections', item: 'License Information', present: /license/i.test(lowerContent) || /mit|apache|gpl|bsd/i.test(lowerContent), severity: 'important', description: 'Clear license information', example: '## License\n\nMIT License - see [LICENSE](LICENSE) file', }); checklist.push({ category: 'Important Sections', item: 'Contributing Guidelines', present: /contribut/i.test(lowerContent), severity: 'important', description: 'Information on how to contribute to the project', example: 'See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines', }); // Community Health checklist.push({ category: 'Community Health', item: 'Code of Conduct', present: /code of conduct/i.test(lowerContent), severity: 'recommended', description: 'Link to code of conduct for community projects', example: 'Please read our [Code of Conduct](CODE_OF_CONDUCT.md)', }); checklist.push({ category: 'Community Health', item: 'Issue Templates', present: /issue template|bug report|feature request/i.test(lowerContent), severity: 'recommended', description: 'Reference to issue templates for better bug reports', example: 'Use our [issue templates](.github/ISSUE_TEMPLATE/) when reporting bugs', }); // Visual Elements checklist.push({ category: 'Visual Elements', item: 'Badges', present: /\[!\[.*\]\(.*\)\]\(.*\)/.test(content) || /badge/i.test(lowerContent), severity: 'recommended', description: 'Status badges for build, version, license, etc.', example: '[![Build Status](badge-url)](link-url)', }); checklist.push({ category: 'Visual Elements', item: 'Screenshots/Demo', present: /!\[.*\]\(.*\.(png|jpg|jpeg|gif|webp)\)/i.test(content) || /screenshot|demo|gif/i.test(lowerContent), severity: projectType === 'application' || projectType === 'tool' ? 'important' : 'recommended', description: 'Visual demonstration of the project (especially for applications)', example: '![Demo](demo.gif)', }); // Content Quality checklist.push({ category: 'Content Quality', item: 'Appropriate Length', present: lines.length >= 20 && lines.length <= 300, severity: 'important', description: 'README length appropriate for project complexity (20-300 lines)', example: 'Keep main README focused, link to detailed docs', }); checklist.push({ category: 'Content Quality', item: 'Clear Section Headers', present: (content.match(/^##\s+/gm) || []).length >= 3, severity: 'important', description: 'Well-organized content with clear section headers', example: '## Installation\n## Usage\n## Contributing', }); checklist.push({ category: 'Content Quality', item: 'Working Links', present: !/\[.*\]\(\)/.test(content) && !/\[.*\]\(#\)/.test(content), severity: 'important', description: 'All links should be functional (no empty or placeholder links)', example: '[Documentation](https://example.com/docs)', }); // Project-specific checks if (projectType === 'library' || projectType === 'framework') { checklist.push({ category: 'Library Specific', item: 'API Documentation', present: /api|methods|functions|reference/i.test(lowerContent), severity: 'important', description: 'API documentation or link to detailed API reference', example: 'See [API Documentation](docs/api.md) for detailed method reference', }); } if (projectType === 'application' || projectType === 'tool') { checklist.push({ category: 'Application Specific', item: 'Configuration Options', present: /config|settings|options|environment/i.test(lowerContent), severity: 'important', description: 'Configuration and customization options', example: 'See [Configuration Guide](docs/configuration.md)', }); } return checklist; } function calculateOverallScore(checklist: ChecklistItem[]): { score: number; grade: string } { const weights = { critical: 3, important: 2, recommended: 1 }; let totalScore = 0; let maxScore = 0; checklist.forEach((item) => { const weight = weights[item.severity]; maxScore += weight; if (item.present) { totalScore += weight; } }); const percentage = maxScore > 0 ? Math.round((totalScore / maxScore) * 100) : 0; let grade: string; if (percentage >= 90) grade = 'A'; else if (percentage >= 80) grade = 'B'; else if (percentage >= 70) grade = 'C'; else if (percentage >= 60) grade = 'D'; else grade = 'F'; return { score: percentage, grade }; } function generateRecommendations( checklist: ChecklistItem[], projectType: string, targetAudience: string, ): string[] { const recommendations: string[] = []; const missing = checklist.filter((item) => !item.present); // Critical issues first const critical = missing.filter((item) => item.severity === 'critical'); if (critical.length > 0) { recommendations.push( `🚨 Critical: Fix ${critical.length} essential sections: ${critical .map((item) => item.item) .join(', ')}`, ); } // Important issues const important = missing.filter((item) => item.severity === 'important'); if (important.length > 0) { recommendations.push( `⚠️ Important: Add ${important.length} key sections: ${important .map((item) => item.item) .join(', ')}`, ); } // Project-specific recommendations if (projectType === 'library') { recommendations.push( '📚 Library Focus: Emphasize installation, basic usage, and API documentation', ); } else if (projectType === 'application') { recommendations.push( '🖥️ Application Focus: Include screenshots, configuration options, and deployment guides', ); } // Target audience specific recommendations if (targetAudience === 'beginner') { recommendations.push( '👶 Beginner-Friendly: Use simple language, provide detailed examples, include troubleshooting', ); } else if (targetAudience === 'advanced') { recommendations.push( '🎯 Advanced Users: Focus on technical details, performance notes, and extensibility', ); } // General improvements const recommended = missing.filter((item) => item.severity === 'recommended'); if (recommended.length > 0) { recommendations.push( `✨ Enhancement: Consider adding ${recommended.map((item) => item.item).join(', ')}`, ); } return recommendations; } function generateTemplates( projectType: string, _generateTemplate: boolean, ): Record<string, string> { const templates: Record<string, string> = {}; if (projectType === 'library') { templates['README-library.md'] = `# Project Name > One-line description of what this library does [![Build Status][build-badge]][build-link] [![npm version][npm-badge]][npm-link] [![License][license-badge]][license-link] ## TL;DR What it does in 2-3 sentences. Who should use it. ## Quick Start ### Install \`\`\`bash npm install package-name \`\`\` ### Use \`\`\`javascript const lib = require('package-name'); // Basic usage example const result = lib.doSomething(); console.log(result); \`\`\` ## When to Use This - ✅ When you need X functionality - ✅ When you want Y capability - ❌ When you need Z (use [alternative] instead) ## API Reference ### \`doSomething(options)\` Description of the main method. **Parameters:** - \`options\` (Object): Configuration options - \`param1\` (string): Description of parameter - \`param2\` (boolean, optional): Description of optional parameter **Returns:** Description of return value **Example:** \`\`\`javascript const result = lib.doSomething({ param1: 'value', param2: true }); \`\`\` ## Full Documentation [Link to full documentation](docs/) ## Contributing We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. ## License MIT License - see [LICENSE](LICENSE) file for details. [build-badge]: https://github.com/username/repo/workflows/CI/badge.svg [build-link]: https://github.com/username/repo/actions [npm-badge]: https://img.shields.io/npm/v/package-name.svg [npm-link]: https://www.npmjs.com/package/package-name [license-badge]: https://img.shields.io/badge/license-MIT-blue.svg [license-link]: LICENSE `; } if (projectType === 'application' || projectType === 'tool') { templates['README-application.md'] = `# Project Name > One-line description of what this application does ![Demo](demo.gif) ## What This Does Brief explanation of the application's purpose and key features: - 🚀 Feature 1: Description - 📊 Feature 2: Description - 🔧 Feature 3: Description ## Quick Start ### Prerequisites - Node.js 16+ - Docker (optional) - Other requirements ### Install & Run \`\`\`bash git clone https://github.com/username/repo.git cd project-name npm install npm start \`\`\` Visit \`http://localhost:3000\` to see the application. ## Configuration ### Environment Variables \`\`\`bash # Copy example config cp .env.example .env # Edit configuration nano .env \`\`\` ### Key Settings - \`PORT\`: Server port (default: 3000) - \`DATABASE_URL\`: Database connection string - \`API_KEY\`: External service API key ## Usage Examples ### Basic Usage \`\`\`bash npm run command -- --option value \`\`\` ### Advanced Usage \`\`\`bash npm run command -- --config custom.json --verbose \`\`\` ## Deployment See [Deployment Guide](docs/deployment.md) for production setup. ## Troubleshooting ### Common Issues **Issue 1: Error message** - Solution: Steps to resolve **Issue 2: Another error** - Solution: Steps to resolve See [FAQ](docs/FAQ.md) for more help. ## Contributing We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. ## License MIT License - see [LICENSE](LICENSE) file for details. `; } return templates; } function generateCommunityFiles(_projectType: string): Record<string, string> { const files: Record<string, string> = {}; files['CONTRIBUTING.md'] = `# Contributing to Project Name Thank you for your interest in contributing! This document provides guidelines for contributing to this project. ## Getting Started 1. Fork the repository 2. Clone your fork: \`git clone https://github.com/yourusername/repo.git\` 3. Create a feature branch: \`git checkout -b feature-name\` 4. Make your changes 5. Test your changes: \`npm test\` 6. Commit your changes: \`git commit -m "Description of changes"\` 7. Push to your fork: \`git push origin feature-name\` 8. Create a Pull Request ## Development Setup \`\`\`bash npm install npm run dev \`\`\` ## Code Style - Use TypeScript for new code - Follow existing code formatting - Run \`npm run lint\` before committing - Add tests for new features ## Pull Request Guidelines - Keep PRs focused and small - Include tests for new functionality - Update documentation as needed - Ensure CI passes - Link to relevant issues ## Reporting Issues Use our [issue templates](.github/ISSUE_TEMPLATE/) when reporting bugs or requesting features. ## Code of Conduct Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md). `; files['CODE_OF_CONDUCT.md'] = `# Code of Conduct ## Our Pledge We pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4. `; files['SECURITY.md'] = `# Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | 1.x.x | :white_check_mark: | | < 1.0 | :x: | ## Reporting a Vulnerability If you discover a security vulnerability, please report it privately: 1. **Do not** create a public issue 2. Email security@example.com with details 3. Include steps to reproduce if possible 4. We will respond within 48 hours ## Security Best Practices When using this project: - Keep dependencies updated - Use environment variables for secrets - Follow principle of least privilege - Regularly audit your setup Thank you for helping keep our project secure! `; return files; } async function writeGeneratedFiles( templates: Record<string, string>, communityFiles: Record<string, string>, outputDirectory: string, _originalReadmePath: string, ): Promise<void> { try { // Create output directory await mkdir(outputDirectory, { recursive: true }); // Write templates for (const [filename, content] of Object.entries(templates)) { const filePath = join(outputDirectory, filename); await writeFile(filePath, content, 'utf-8'); } // Write community files for (const [filename, content] of Object.entries(communityFiles)) { const filePath = join(outputDirectory, filename); await writeFile(filePath, content, 'utf-8'); } // Create .github directory structure const githubDir = join(outputDirectory, '.github'); await mkdir(githubDir, { recursive: true }); const issueTemplateDir = join(githubDir, 'ISSUE_TEMPLATE'); await mkdir(issueTemplateDir, { recursive: true }); // Bug report template const bugReportTemplate = `--- name: Bug report about: Create a report to help us improve title: '[BUG] ' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Environment:** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. `; await writeFile(join(issueTemplateDir, 'bug_report.yml'), bugReportTemplate, 'utf-8'); // Feature request template const featureRequestTemplate = `--- name: Feature request about: Suggest an idea for this project title: '[FEATURE] ' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. `; await writeFile(join(issueTemplateDir, 'feature_request.yml'), featureRequestTemplate, 'utf-8'); // Pull request template const prTemplate = `## Description Brief description of changes made. ## Type of Change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update ## Testing - [ ] Tests pass locally - [ ] New tests added for new functionality - [ ] Manual testing completed ## Checklist - [ ] Code follows project style guidelines - [ ] Self-review completed - [ ] Documentation updated - [ ] No new warnings introduced `; await writeFile(join(githubDir, 'PULL_REQUEST_TEMPLATE.md'), prTemplate, 'utf-8'); } catch (error) { throw new Error( `Failed to write generated files: ${ error instanceof Error ? error.message : 'Unknown error' }`, ); } } function calculateSummaryMetrics(checklist: ChecklistItem[]) { const criticalIssues = checklist.filter( (item) => !item.present && item.severity === 'critical', ).length; const importantIssues = checklist.filter( (item) => !item.present && item.severity === 'important', ).length; const recommendedImprovements = checklist.filter( (item) => !item.present && item.severity === 'recommended', ).length; const sectionsPresent = checklist.filter((item) => item.present).length; const totalSections = checklist.length; // Estimate improvement time based on missing items const totalMissing = criticalIssues + importantIssues + recommendedImprovements; let estimatedTime = ''; if (totalMissing === 0) { estimatedTime = 'No improvements needed'; } else if (totalMissing <= 3) { estimatedTime = '30 minutes - 1 hour'; } else if (totalMissing <= 6) { estimatedTime = '1-2 hours'; } else if (totalMissing <= 10) { estimatedTime = '2-4 hours'; } else { estimatedTime = '4+ hours (consider phased approach)'; } return { criticalIssues, importantIssues, recommendedImprovements, sectionsPresent, totalSections, estimatedImprovementTime: estimatedTime, }; } function generateNextSteps( checklist: ChecklistItem[], generateTemplate: boolean, outputDirectory?: string, ): string[] { const nextSteps: string[] = []; const missing = checklist.filter((item) => !item.present); if (missing.length === 0) { nextSteps.push('✅ README follows all best practices - no immediate action needed'); nextSteps.push('📊 Consider periodic reviews to maintain quality as project evolves'); return nextSteps; } // Critical issues first const critical = missing.filter((item) => item.severity === 'critical'); if (critical.length > 0) { nextSteps.push(`🚨 Priority 1: Address ${critical.length} critical issues immediately`); critical.forEach((item) => { nextSteps.push(` • Add ${item.item}: ${item.description}`); }); } // Important issues const important = missing.filter((item) => item.severity === 'important'); if (important.length > 0) { nextSteps.push(`⚠️ Priority 2: Address ${important.length} important sections within 1 week`); } // Template usage if (generateTemplate && outputDirectory) { nextSteps.push(`📝 Review generated templates in ${outputDirectory}/`); nextSteps.push('🔄 Customize templates to match your project specifics'); nextSteps.push( '📋 Use community files (.github templates, CONTRIBUTING.md) to improve project health', ); } // General improvements nextSteps.push('🔍 Run this analysis periodically to maintain README quality'); nextSteps.push('👥 Consider getting feedback from new users on README clarity'); return nextSteps; }

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/tosin2013/documcp'

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