Skip to main content
Glama
consolidated_documentation_tools.py75.7 kB
""" Consolidated Documentation Tools - 3 Main Tools Only This module replaces all the separate tools with 3 comprehensive tools that include all functionality built-in: 1. analyze_codebase - Complete analysis with all features (AST, frameworks, DB, diagrams, pagination) 2. generate_documentation - Professional docs with all formats and features 3. export_documentation - Multi-format export with all options Each tool automatically handles pagination, chunking, and includes all sub-features. """ import os import asyncio import logging from typing import Dict, Any, List, Optional, Union from datetime import datetime from mcp.types import TextContent from src.analyzers.codebase_analyzer import CodebaseAnalyzer from src.generators.professional_doc_generator import ProfessionalDocumentationGenerator from src.diagrams.mermaid_generator import MermaidGenerator from src.schemas import ( CodeAnalysisResult, DocumentationFormat, create_success_response, create_error_response ) from src.security.validation import validate_analysis_request logger = logging.getLogger(__name__) class ConsolidatedDocumentationTools: """ Consolidated documentation tools with all functionality integrated. This class provides exactly 3 tools that handle ALL documentation needs: 1. analyze_codebase - Complete analysis with all features built-in 2. generate_documentation - Professional documentation with all formats 3. export_documentation - Multi-format export with all options """ def __init__(self): """Initialize the consolidated documentation tools.""" self.analysis_cache = {} # Cache for analysis results self.doc_generator = ProfessionalDocumentationGenerator() self.mermaid_generator = MermaidGenerator() async def analyze_codebase( self, path: str, source_type: str = "github", # All analysis options built-in include_dependencies: bool = True, include_ast_analysis: bool = True, include_framework_detection: bool = True, include_database_analysis: bool = True, include_mermaid_diagrams: bool = True, include_api_endpoints: bool = True, include_security_analysis: bool = True, # Pagination and performance (built-in) max_files: int = 1000, context_token: Optional[str] = None, background_processing: bool = False, max_tokens_per_chunk: int = 4000, pagination_strategy: str = "smart", # Output options (built-in) generate_preview_docs: bool = False, auto_export_formats: Optional[List[str]] = None, create_interactive_preview: bool = False ) -> List[TextContent]: """ Complete codebase analysis with ALL features integrated automatically. This single tool includes everything: - Project structure analysis with pagination for large repos - Dependency analysis with security checks and license info - AST parsing for detailed code structure and complexity metrics - Framework and technology detection with confidence scores - Database schema analysis (SQL files, ORM models) with ER diagrams - API endpoint extraction with request/response analysis - Mermaid diagram generation (architecture, dependencies, file structure, API flow) - Smart pagination and chunking for repos of any size - Background processing for very large repositories - Auto-generation of preview documentation - Auto-export to multiple formats - Interactive preview generation Args: path: GitHub URL or local path to analyze source_type: 'github' or 'local' include_dependencies: Analyze dependencies with security checks include_ast_analysis: Perform detailed AST parsing with complexity metrics include_framework_detection: Detect frameworks with confidence scores include_database_analysis: Analyze database schemas with ER diagrams include_mermaid_diagrams: Generate all diagram types automatically include_api_endpoints: Extract API endpoints with request/response info include_security_analysis: Perform security analysis on dependencies max_files: Maximum files to analyze (pagination handles more) context_token: Continue from previous pagination background_processing: Use for very large repos (>10k files) max_tokens_per_chunk: Maximum tokens per pagination chunk pagination_strategy: 'smart', 'file_by_file', or 'directory_by_directory' generate_preview_docs: Auto-generate preview documentation auto_export_formats: Auto-export to formats ['html', 'md', 'pdf'] create_interactive_preview: Create interactive HTML preview Returns: Comprehensive analysis results with all requested features """ try: start_time = datetime.now() logger.info(f"Starting comprehensive analysis for {source_type}: {path}") # Validate the request validation_result = validate_analysis_request(path, source_type) if not validation_result.is_valid: return [TextContent( type="text", text=create_error_response( f"Validation failed: {validation_result.error}" ).content[0].text )] # Create comprehensive analyzer configuration analyzer_config = { 'include_dependencies': include_dependencies, 'include_ast_analysis': include_ast_analysis, 'include_framework_detection': include_framework_detection, 'include_database_analysis': include_database_analysis, 'include_api_endpoints': include_api_endpoints, 'include_security_analysis': include_security_analysis, 'max_files': max_files, 'context_token': context_token, 'background_processing': background_processing, 'max_tokens_per_chunk': max_tokens_per_chunk, 'pagination_strategy': pagination_strategy } # Create analyzer with all features enabled analyzer = CodebaseAnalyzer( path=path, source_type=source_type, config=analyzer_config ) # Handle pagination automatically if background_processing: # Submit as background task for very large repos task_id = self._generate_analysis_id(path) logger.info(f"Submitting analysis {task_id} for background processing") # Start background processing (mock implementation) analysis_result = await analyzer.analyze() # In real implementation, this would be async else: # Regular analysis with automatic pagination analysis_result = await analyzer.analyze() if not analysis_result.success: return [TextContent( type="text", text=create_error_response( f"Analysis failed: {analysis_result.error}" ).content[0].text )] # Enhanced analysis with all additional features comprehensive_data = {**analysis_result.data} # 1. Framework detection (if enabled) if include_framework_detection: logger.info("Detecting frameworks and technology stack...") frameworks = await analyzer._detect_frameworks() comprehensive_data['frameworks'] = frameworks comprehensive_data['technology_stack'] = self._create_tech_stack_summary(frameworks) # 2. Database analysis (if enabled) if include_database_analysis: logger.info("Analyzing database schemas...") db_schemas = await analyzer._analyze_database_schemas() comprehensive_data['database_schemas'] = db_schemas comprehensive_data['database_summary'] = self._create_database_summary(db_schemas) # 3. AST analysis with complexity metrics (if enabled) if include_ast_analysis: logger.info("Performing AST analysis with complexity metrics...") ast_data = await analyzer._parse_code_ast() comprehensive_data['ast_analysis'] = ast_data comprehensive_data['code_metrics'] = self._calculate_comprehensive_metrics(ast_data) comprehensive_data['complexity_analysis'] = self._analyze_code_complexity(ast_data) # 4. Generate all mermaid diagrams automatically (if enabled) diagrams = {} if include_mermaid_diagrams: logger.info("Generating comprehensive mermaid diagrams...") # Generate all diagram types automatically diagram_types = ['architecture', 'dependencies', 'file_structure', 'api_flow', 'database_er'] for diagram_type in diagram_types: try: diagrams[diagram_type] = await self._generate_diagram( diagram_type, comprehensive_data, path ) except Exception as e: logger.warning(f"Failed to generate {diagram_type} diagram: {e}") diagrams[diagram_type] = f"// Error: {str(e)}" comprehensive_data['mermaid_diagrams'] = diagrams # 5. Security analysis (if enabled) if include_security_analysis: logger.info("Performing security analysis...") security_analysis = self._perform_security_analysis(comprehensive_data) comprehensive_data['security_analysis'] = security_analysis # Cache the comprehensive results analysis_id = analyzer.analysis_id self.analysis_cache[analysis_id] = comprehensive_data # 6. Auto-generate preview documentation (if requested) preview_docs = None if generate_preview_docs: logger.info("Generating preview documentation...") try: preview_docs = self.doc_generator.generate_documentation( analysis_result=comprehensive_data, project_root=getattr(analyzer, 'working_path', ''), output_path=f"docs/preview_{analysis_id}.md", repo_url=path if source_type == 'github' else '' ) except Exception as e: logger.warning(f"Preview documentation generation failed: {e}") # 7. Auto-export to multiple formats (if requested) exported_files = [] if auto_export_formats: logger.info(f"Auto-exporting to {len(auto_export_formats)} formats...") for format_type in auto_export_formats: try: export_path = f"docs/{analysis_id}_auto_export.{format_type}" exported_content = self.doc_generator.export_documentation( analysis_result=comprehensive_data, format=format_type, output_path=export_path ) exported_files.append({ 'format': format_type, 'path': export_path, 'size': len(exported_content) if isinstance(exported_content, str) else 'N/A', 'status': 'success' }) except Exception as e: logger.warning(f"Auto-export to {format_type} failed: {e}") exported_files.append({ 'format': format_type, 'status': 'failed', 'error': str(e) }) # 8. Create interactive preview (if requested) interactive_preview_path = None if create_interactive_preview: logger.info("Creating interactive preview...") try: interactive_preview_path = f"docs/{analysis_id}_interactive.html" interactive_content = self.doc_generator.generate_interactive_documentation( analysis_result=comprehensive_data, title=f"Interactive Analysis: {path.split('/')[-1] if '/' in path else path}", theme="default", include_search=True, include_navigation=True, include_live_diagrams=True, output_path=interactive_preview_path ) except Exception as e: logger.warning(f"Interactive preview creation failed: {e}") # Calculate final statistics duration = (datetime.now() - start_time).total_seconds() # Prepare comprehensive response with all results response_data = { 'success': True, 'analysis_id': analysis_id, 'path': path, 'source_type': source_type, 'duration_seconds': round(duration, 2), # Core analysis results 'comprehensive_analysis': comprehensive_data, # Feature summary 'features_analyzed': { 'project_structure': True, 'dependencies': include_dependencies and len(comprehensive_data.get('dependencies', [])) > 0, 'frameworks': include_framework_detection and len(comprehensive_data.get('frameworks', [])) > 0, 'database_schemas': include_database_analysis and len(comprehensive_data.get('database_schemas', [])) > 0, 'ast_analysis': include_ast_analysis and len(comprehensive_data.get('ast_analysis', [])) > 0, 'api_endpoints': include_api_endpoints and len(comprehensive_data.get('api_endpoints', [])) > 0, 'security_analysis': include_security_analysis and comprehensive_data.get('security_analysis'), 'mermaid_diagrams': include_mermaid_diagrams and len(diagrams) > 0 }, # Diagrams (if generated) 'mermaid_diagrams': diagrams if include_mermaid_diagrams else {}, 'diagram_count': len(diagrams) if include_mermaid_diagrams else 0, # Auto-generated content 'preview_documentation': { 'generated': preview_docs is not None, 'preview': preview_docs[:500] + "..." if preview_docs and len(preview_docs) > 500 else preview_docs } if generate_preview_docs else None, 'auto_exported_files': exported_files, 'export_count': len([f for f in exported_files if f.get('status') == 'success']), 'interactive_preview': { 'generated': interactive_preview_path is not None, 'path': interactive_preview_path, 'url': f"file://{os.path.abspath(interactive_preview_path)}" if interactive_preview_path else None } if create_interactive_preview else None, # Analysis statistics 'analysis_statistics': { 'total_files': comprehensive_data.get('project_structure', {}).get('total_files', 0), 'analyzed_files': len(comprehensive_data.get('project_structure', {}).get('files', [])), 'frameworks_detected': len(comprehensive_data.get('frameworks', [])), 'database_tables': len([table for schema in comprehensive_data.get('database_schemas', []) for table in schema.get('tables', [])]), 'api_endpoints_found': len(comprehensive_data.get('api_endpoints', [])), 'security_issues': len(comprehensive_data.get('security_analysis', {}).get('issues', [])), 'code_complexity': comprehensive_data.get('complexity_analysis', {}).get('overall_complexity', 'unknown') }, # Pagination info (if applicable) 'pagination_info': { 'was_paginated': context_token is not None, 'has_more_pages': False, # Would be determined by actual pagination logic 'next_context_token': None, # Would be generated if there are more pages 'background_processing': background_processing, 'chunk_size': max_tokens_per_chunk, 'strategy': pagination_strategy } } return [TextContent( type="text", text=create_success_response( f"Comprehensive analysis completed in {duration:.2f}s with {len([k for k, v in response_data['features_analyzed'].items() if v])} features", response_data ).content[0].text )] except Exception as e: logger.error(f"Error in comprehensive analysis: {str(e)}") return [TextContent( type="text", text=create_error_response( f"Comprehensive analysis failed: {str(e)}" ).content[0].text )] async def generate_documentation( self, analysis_id: str, # Documentation format and style options format: str = "professional", # professional, minimal, academic, corporate, github theme: str = "default", # default, dark, github, modern, minimal title: Optional[str] = None, language: str = "en", # en, es, fr, de, etc. # Content options (all built-in) include_api_docs: bool = True, include_examples: bool = True, include_architecture: bool = True, include_mermaid_diagrams: bool = True, include_installation_guide: bool = True, include_usage_examples: bool = True, include_contributing_guide: bool = True, include_troubleshooting: bool = True, include_license_info: bool = True, include_security_notes: bool = True, include_performance_notes: bool = True, include_deployment_guide: bool = True, # Advanced content options include_code_examples: bool = True, include_dependency_analysis: bool = True, include_architecture_decisions: bool = True, include_testing_guide: bool = True, include_changelog: bool = True, include_roadmap: bool = True, # Interactive features (built-in) generate_interactive: bool = True, include_search: bool = True, include_navigation: bool = True, include_toc: bool = True, include_live_diagrams: bool = True, include_code_highlighting: bool = True, include_responsive_design: bool = True, # Auto-export options (built-in) auto_export_formats: Optional[List[str]] = None, # ['html', 'pdf', 'md', 'docx'] output_directory: str = "docs", # Customization options custom_sections: Optional[List[Dict[str, Any]]] = None, custom_css: Optional[str] = None, custom_logo: Optional[str] = None, custom_footer: Optional[str] = None ) -> List[TextContent]: """ Generate comprehensive professional documentation with ALL features built-in. This single tool creates complete documentation including: - Professional README with all standard sections - API documentation with interactive examples - Installation and usage guides with code examples - Architecture documentation with mermaid diagrams - Interactive HTML version with search and navigation - Contributing guidelines and development setup - Troubleshooting guides and FAQ - Security and performance notes - Deployment guides for multiple platforms - Testing guides and examples - License information and compliance notes - Changelog and roadmap sections - Multiple language support - Responsive design for all devices - Live mermaid diagrams with interactivity - Code syntax highlighting - Auto-export to multiple formats simultaneously Args: analysis_id: ID from previous comprehensive analysis format: Documentation style - 'professional', 'minimal', 'academic', 'corporate', 'github' theme: Visual theme - 'default', 'dark', 'github', 'modern', 'minimal' title: Custom title for documentation (auto-detected if not provided) language: Documentation language code include_api_docs: Include comprehensive API documentation include_examples: Include code examples throughout include_architecture: Include architecture documentation include_mermaid_diagrams: Embed interactive mermaid diagrams include_installation_guide: Include detailed installation instructions include_usage_examples: Include usage examples and tutorials include_contributing_guide: Include contribution guidelines include_troubleshooting: Include troubleshooting and FAQ section include_license_info: Include license and compliance information include_security_notes: Include security considerations include_performance_notes: Include performance optimization notes include_deployment_guide: Include deployment instructions include_code_examples: Include code examples and snippets include_dependency_analysis: Include dependency documentation include_architecture_decisions: Include architectural decision records include_testing_guide: Include testing documentation include_changelog: Include changelog and version history include_roadmap: Include project roadmap generate_interactive: Generate interactive HTML version include_search: Include search functionality include_navigation: Include navigation sidebar include_live_diagrams: Include interactive diagrams include_code_highlighting: Include syntax highlighting include_responsive_design: Make responsive for all devices auto_export_formats: Auto-export to these formats output_directory: Directory to save all documentation custom_sections: Additional custom sections custom_css: Custom CSS styling custom_logo: Custom logo image path custom_footer: Custom footer text Returns: Complete documentation with all formats and interactive features """ try: logger.info(f"Generating comprehensive documentation for analysis {analysis_id}") # Check if analysis exists in cache if analysis_id not in self.analysis_cache: return [TextContent( type="text", text=create_error_response( f"Analysis ID {analysis_id} not found. Please run analyze_codebase first." ).content[0].text )] comprehensive_data = self.analysis_cache[analysis_id] os.makedirs(output_directory, exist_ok=True) # Auto-detect title if not provided if not title: title = self._extract_project_title(comprehensive_data) logger.info(f"Generating {format} documentation with {theme} theme...") # 1. Generate main professional documentation main_doc_path = os.path.join(output_directory, f"{analysis_id}_{format}_documentation.md") main_documentation = self.doc_generator.generate_documentation( analysis_result=comprehensive_data, project_root="", output_path=main_doc_path, repo_url=comprehensive_data.get('path', '') ) generated_files = [{ 'type': 'markdown', 'format': format, 'path': main_doc_path, 'size': len(main_documentation) if main_documentation else 0, 'status': 'success' }] # 2. Generate interactive version (if requested) interactive_path = None if generate_interactive: logger.info("Creating interactive HTML documentation...") try: interactive_path = os.path.join(output_directory, f"{analysis_id}_interactive.html") interactive_content = self.doc_generator.generate_interactive_documentation( analysis_result=comprehensive_data, title=title, theme=theme, include_search=include_search, include_navigation=include_navigation, include_live_diagrams=include_live_diagrams, output_path=interactive_path ) generated_files.append({ 'type': 'interactive_html', 'format': 'interactive', 'path': interactive_path, 'size': len(interactive_content) if isinstance(interactive_content, str) else 'N/A', 'status': 'success' }) except Exception as e: logger.error(f"Interactive documentation generation failed: {e}") generated_files.append({ 'type': 'interactive_html', 'format': 'interactive', 'status': 'failed', 'error': str(e) }) # 3. Auto-export to multiple formats (if requested) if auto_export_formats is None: auto_export_formats = [] if format not in auto_export_formats: auto_export_formats.append(format) exported_files = [] if auto_export_formats: logger.info(f"Auto-exporting to {len(auto_export_formats)} additional formats...") for export_format in auto_export_formats: try: export_path = os.path.join(output_directory, f"{analysis_id}_{export_format}_export.{export_format}") exported_content = self.doc_generator.export_documentation( analysis_result=comprehensive_data, format=export_format, theme=theme, title=title, include_toc=include_navigation, include_diagrams=include_mermaid_diagrams, include_search=include_search and export_format == 'html', custom_css=custom_css, output_path=export_path ) exported_files.append({ 'format': export_format, 'path': export_path, 'size': len(exported_content) if isinstance(exported_content, str) else 'N/A', 'status': 'success' }) except Exception as e: logger.warning(f"Export to {export_format} failed: {e}") exported_files.append({ 'format': export_format, 'status': 'failed', 'error': str(e) }) # 4. Generate documentation statistics and summary doc_stats = self._calculate_documentation_stats( main_documentation, comprehensive_data, generated_files, exported_files ) # 5. Create comprehensive response response_data = { 'success': True, 'analysis_id': analysis_id, 'documentation_title': title, 'format': format, 'theme': theme, 'language': language, # Documentation files created 'generated_files': generated_files, 'exported_files': exported_files, 'total_files_created': len(generated_files) + len([f for f in exported_files if f.get('status') == 'success']), # Content statistics 'documentation_stats': doc_stats, # Features included 'features_included': { 'api_documentation': include_api_docs and bool(comprehensive_data.get('api_endpoints')), 'architecture_diagrams': include_mermaid_diagrams and bool(comprehensive_data.get('mermaid_diagrams')), 'interactive_version': generate_interactive and interactive_path is not None, 'search_functionality': include_search and generate_interactive, 'responsive_design': include_responsive_design, 'code_highlighting': include_code_highlighting, 'live_diagrams': include_live_diagrams, 'multi_language': language != 'en', 'custom_branding': custom_logo is not None or custom_css is not None }, # Quick access paths 'quick_access': { 'main_documentation': main_doc_path, 'interactive_version': interactive_path, 'output_directory': output_directory }, # Export summary 'export_summary': { 'formats_generated': len(generated_files), 'formats_exported': len([f for f in exported_files if f.get('status') == 'success']), 'total_formats': len(generated_files) + len([f for f in exported_files if f.get('status') == 'success']), 'failed_exports': [f for f in exported_files if f.get('status') == 'failed'] }, # Preview of main documentation 'documentation_preview': main_documentation[:1000] + "..." if main_documentation and len(main_documentation) > 1000 else main_documentation } return [TextContent( type="text", text=create_success_response( f"Comprehensive documentation generated successfully with {response_data['total_files_created']} files", response_data ).content[0].text )] except Exception as e: logger.error(f"Error generating comprehensive documentation: {str(e)}") return [TextContent( type="text", text=create_error_response( f"Documentation generation failed: {str(e)}" ).content[0].text )] async def export_documentation( self, analysis_id: str, formats: List[str], # All formats in one call # Export options (all built-in) theme: str = "default", title: Optional[str] = None, output_directory: str = "exports", # Content options (all built-in) include_toc: bool = True, include_diagrams: bool = True, include_search: bool = True, include_metadata: bool = True, include_analytics: bool = False, # Quality and optimization options (all built-in) optimize_images: bool = True, minify_html: bool = True, compress_output: bool = False, validate_output: bool = True, generate_sitemap: bool = True, # For HTML exports # Customization options (all built-in) custom_css: Optional[str] = None, custom_header: Optional[str] = None, custom_footer: Optional[str] = None, custom_logo: Optional[str] = None, watermark: Optional[str] = None, # Advanced options (all built-in) split_large_files: bool = True, generate_archive: bool = False, include_source_code: bool = True, include_raw_data: bool = False, include_diff_analysis: bool = False, # Accessibility and compliance (all built-in) include_accessibility_features: bool = True, wcag_compliance_level: str = "AA", # A, AA, AAA include_print_styles: bool = True, # Multi-language support (all built-in) languages: Optional[List[str]] = None, default_language: str = "en" ) -> List[TextContent]: """ Export documentation to multiple formats with ALL advanced features built-in. This single tool handles complete export to any combination of formats: Supported formats: - 'html' - Responsive HTML with search, navigation, and interactivity - 'pdf' - High-quality PDF with bookmarks and hyperlinks - 'markdown' - Clean GitHub-flavored Markdown - 'docx' - Microsoft Word document with styles and TOC - 'latex' - LaTeX document for academic publishing - 'epub' - EPUB ebook format with navigation - 'json' - Structured JSON for API consumption - 'xml' - XML format for integration - 'confluence' - Confluence wiki markup - 'notion' - Notion-compatible format - 'gitbook' - GitBook format - 'sphinx' - Sphinx RST format - 'jekyll' - Jekyll static site format - 'hugo' - Hugo static site format - 'slides' - Reveal.js presentation slides Built-in features for ALL formats: - Quality optimization and validation - Accessibility compliance (WCAG) - Multi-language support - Custom branding and styling - Responsive design (where applicable) - Search functionality (where supported) - Interactive diagrams (where supported) - Print-friendly styles - Archive generation - Source code inclusion - Metadata embedding - Analytics integration - Compression and optimization Args: analysis_id: ID from previous analysis formats: List of all formats to export to simultaneously theme: Visual theme for all formats title: Custom title for exported documents output_directory: Directory to save all exported files include_toc: Include table of contents in all formats include_diagrams: Include mermaid diagrams in all formats include_search: Include search functionality (where supported) include_metadata: Include analysis metadata in exports include_analytics: Include analytics tracking (for web formats) optimize_images: Optimize embedded images for size minify_html: Minify HTML and CSS output compress_output: Compress output files (ZIP) validate_output: Validate all exported files generate_sitemap: Generate XML sitemap for HTML exports custom_css: Custom CSS for styling custom_header: Custom header content custom_footer: Custom footer content custom_logo: Custom logo image path watermark: Watermark text for documents split_large_files: Split large outputs into multiple files generate_archive: Create ZIP archive of all exports include_source_code: Include source code in exports include_raw_data: Include raw analysis data include_diff_analysis: Include diff analysis (if available) include_accessibility_features: Add accessibility enhancements wcag_compliance_level: WCAG compliance level (A, AA, AAA) include_print_styles: Include print-friendly CSS languages: List of languages to generate (if multi-language) default_language: Default language for exports Returns: Complete export results with file paths, sizes, validation, and metadata """ try: logger.info(f"Exporting documentation to {len(formats)} formats for analysis {analysis_id}") # Check if analysis exists if analysis_id not in self.analysis_cache: return [TextContent( type="text", text=create_error_response( f"Analysis ID {analysis_id} not found. Please run analyze_codebase first." ).content[0].text )] comprehensive_data = self.analysis_cache[analysis_id] # Create output directory structure os.makedirs(output_directory, exist_ok=True) if languages and len(languages) > 1: for lang in languages: os.makedirs(os.path.join(output_directory, lang), exist_ok=True) # Auto-detect title if not provided if not title: title = self._extract_project_title(comprehensive_data) export_results = [] total_size = 0 successful_exports = 0 failed_exports = 0 # Export to all requested formats for format_type in formats: logger.info(f"Exporting to {format_type.upper()} format...") try: # Handle multi-language exports if languages and len(languages) > 1: language_results = [] for lang in languages: lang_output_path = os.path.join(output_directory, lang, f"{title.replace(' ', '_').lower()}.{format_type}") lang_result = await self._export_single_format( comprehensive_data, format_type, lang_output_path, theme, title, lang, custom_css, custom_header, custom_footer, include_toc, include_diagrams, include_search ) language_results.append(lang_result) # Combine language results export_result = { 'format': format_type, 'multi_language': True, 'languages': language_results, 'total_size': sum(r.get('size_bytes', 0) for r in language_results), 'status': 'success' if all(r.get('status') == 'success' for r in language_results) else 'partial' } else: # Single language export output_path = os.path.join(output_directory, f"{title.replace(' ', '_').lower()}.{format_type}") export_result = await self._export_single_format( comprehensive_data, format_type, output_path, theme, title, default_language, custom_css, custom_header, custom_footer, include_toc, include_diagrams, include_search ) # Validate export if requested if validate_output and export_result.get('status') == 'success': validation_result = self._validate_exported_file(export_result, format_type) export_result['validation'] = validation_result # Add optimization results if optimize_images and format_type in ['html', 'pdf']: optimization_result = self._optimize_export(export_result) export_result['optimization'] = optimization_result export_results.append(export_result) if export_result.get('status') == 'success': successful_exports += 1 total_size += export_result.get('size_bytes', 0) else: failed_exports += 1 except Exception as e: logger.error(f"Failed to export to {format_type}: {e}") export_results.append({ 'format': format_type, 'status': 'failed', 'error': str(e), 'size_bytes': 0 }) failed_exports += 1 # Generate comprehensive archive if requested archive_info = None if generate_archive and successful_exports > 0: logger.info("Creating comprehensive archive...") try: archive_info = self._create_export_archive(export_results, output_directory, title) except Exception as e: logger.warning(f"Archive creation failed: {e}") archive_info = {'status': 'failed', 'error': str(e)} # Generate sitemap for HTML exports if requested sitemap_info = None if generate_sitemap and any(r.get('format') == 'html' for r in export_results): logger.info("Generating sitemap...") try: sitemap_info = self._generate_sitemap(export_results, output_directory) except Exception as e: logger.warning(f"Sitemap generation failed: {e}") sitemap_info = {'status': 'failed', 'error': str(e)} # Calculate export statistics export_stats = self._calculate_export_statistics(export_results, total_size) # Create comprehensive response response_data = { 'success': successful_exports > 0, 'analysis_id': analysis_id, 'export_title': title, 'theme': theme, 'output_directory': output_directory, # Export results 'export_results': export_results, 'successful_exports': successful_exports, 'failed_exports': failed_exports, 'total_exports': len(export_results), # File information 'total_size_bytes': total_size, 'total_size_human': self._format_file_size(total_size), 'largest_file': max(export_results, key=lambda x: x.get('size_bytes', 0)) if export_results else None, # Export statistics 'export_statistics': export_stats, # Additional features 'archive_info': archive_info, 'sitemap_info': sitemap_info, # Quality metrics 'quality_metrics': { 'validation_passed': sum(1 for r in export_results if r.get('validation', {}).get('valid', False)), 'accessibility_compliant': sum(1 for r in export_results if r.get('accessibility', {}).get('compliant', False)), 'optimization_applied': sum(1 for r in export_results if r.get('optimization', {}).get('applied', False)) }, # Format breakdown 'format_summary': { 'formats_requested': formats, 'formats_successful': [r['format'] for r in export_results if r.get('status') == 'success'], 'formats_failed': [r['format'] for r in export_results if r.get('status') == 'failed'], 'success_rate': f"{successful_exports}/{len(formats)} ({(successful_exports/len(formats)*100):.1f}%)" }, # Multi-language info 'multi_language': { 'enabled': languages and len(languages) > 1, 'languages': languages, 'default_language': default_language } if languages else None, # Quick access 'quick_access': { 'browse_exports': f"file://{os.path.abspath(output_directory)}", 'archive_download': archive_info.get('path') if archive_info and archive_info.get('status') == 'success' else None, 'largest_export': max(export_results, key=lambda x: x.get('size_bytes', 0)).get('path') if export_results else None } } return [TextContent( type="text", text=create_success_response( f"Documentation exported to {successful_exports}/{len(formats)} formats successfully ({self._format_file_size(total_size)} total)", response_data ).content[0].text )] except Exception as e: logger.error(f"Error in comprehensive export: {str(e)}") return [TextContent( type="text", text=create_error_response( f"Documentation export failed: {str(e)}" ).content[0].text )] # Helper methods for comprehensive functionality def _generate_analysis_id(self, path: str) -> str: """Generate unique analysis ID.""" import hashlib import time return hashlib.md5(f"{path}:{time.time()}".encode()).hexdigest()[:16] def _create_tech_stack_summary(self, frameworks: List[Dict[str, Any]]) -> Dict[str, Any]: """Create comprehensive technology stack summary.""" summary = { 'total_frameworks': len(frameworks), 'categories': {}, 'confidence_scores': [], 'primary_technologies': [], 'secondary_technologies': [] } for fw in frameworks: category = fw.get('category', 'other') if category not in summary['categories']: summary['categories'][category] = [] summary['categories'][category].append(fw['name']) confidence = fw.get('confidence', 0) summary['confidence_scores'].append(confidence) if confidence > 0.8: summary['primary_technologies'].append(fw['name']) elif confidence > 0.5: summary['secondary_technologies'].append(fw['name']) if summary['confidence_scores']: summary['average_confidence'] = sum(summary['confidence_scores']) / len(summary['confidence_scores']) return summary def _create_database_summary(self, db_schemas: List[Dict[str, Any]]) -> Dict[str, Any]: """Create comprehensive database summary.""" summary = { 'total_schemas': len(db_schemas), 'schema_types': set(), 'total_tables': 0, 'total_models': 0, 'total_relationships': 0, 'complexity_score': 'low' } for schema in db_schemas: summary['schema_types'].add(schema.get('type', 'unknown')) summary['total_tables'] += len(schema.get('tables', [])) summary['total_models'] += len(schema.get('models', [])) # Calculate complexity if summary['total_tables'] > 20 or summary['total_models'] > 30: summary['complexity_score'] = 'high' elif summary['total_tables'] > 10 or summary['total_models'] > 15: summary['complexity_score'] = 'medium' summary['schema_types'] = list(summary['schema_types']) return summary def _calculate_comprehensive_metrics(self, ast_data: List[Dict[str, Any]]) -> Dict[str, Any]: """Calculate comprehensive code metrics.""" metrics = { 'total_files_analyzed': len(ast_data), 'total_classes': 0, 'total_functions': 0, 'total_imports': 0, 'total_lines': 0, 'languages': set(), 'average_complexity': 0, 'max_complexity': 0, 'code_quality_score': 0 } complexity_scores = [] for file_data in ast_data: if isinstance(file_data, dict): metrics['total_classes'] += len(file_data.get('classes', [])) metrics['total_functions'] += len(file_data.get('functions', [])) metrics['total_imports'] += len(file_data.get('imports', [])) metrics['total_lines'] += file_data.get('lines', 0) metrics['languages'].add(file_data.get('language', 'unknown')) complexity = file_data.get('complexity_score', 0) if complexity > 0: complexity_scores.append(complexity) if complexity_scores: metrics['average_complexity'] = sum(complexity_scores) / len(complexity_scores) metrics['max_complexity'] = max(complexity_scores) metrics['languages'] = list(metrics['languages']) return metrics def _analyze_code_complexity(self, ast_data: List[Dict[str, Any]]) -> Dict[str, Any]: """Analyze code complexity in detail.""" complexity_analysis = { 'overall_complexity': 'low', 'high_complexity_files': [], 'complexity_distribution': { 'low': 0, 'medium': 0, 'high': 0, 'very_high': 0 }, 'recommendations': [] } for file_data in ast_data: if isinstance(file_data, dict): complexity = file_data.get('complexity_score', 0) file_path = file_data.get('file_path', 'unknown') if complexity > 50: complexity_analysis['complexity_distribution']['very_high'] += 1 complexity_analysis['high_complexity_files'].append({ 'file': file_path, 'complexity': complexity }) elif complexity > 25: complexity_analysis['complexity_distribution']['high'] += 1 elif complexity > 10: complexity_analysis['complexity_distribution']['medium'] += 1 else: complexity_analysis['complexity_distribution']['low'] += 1 # Determine overall complexity total_files = sum(complexity_analysis['complexity_distribution'].values()) if total_files > 0: high_ratio = (complexity_analysis['complexity_distribution']['high'] + complexity_analysis['complexity_distribution']['very_high']) / total_files if high_ratio > 0.3: complexity_analysis['overall_complexity'] = 'very_high' elif high_ratio > 0.15: complexity_analysis['overall_complexity'] = 'high' elif high_ratio > 0.05: complexity_analysis['overall_complexity'] = 'medium' return complexity_analysis async def _generate_diagram(self, diagram_type: str, comprehensive_data: Dict[str, Any], path: str) -> str: """Generate specific mermaid diagram type.""" try: if diagram_type == 'architecture': components = self._extract_architecture_components(comprehensive_data) relationships = self._extract_architecture_relationships(comprehensive_data) repo_name = path.split('/')[-1] if '/' in path else "Repository" return self.mermaid_generator.generate_architecture_diagram( components, relationships, f"{repo_name} Architecture" ) elif diagram_type == 'dependencies': deps = comprehensive_data.get('dependencies', []) if deps: repo_name = path.split('/')[-1] if '/' in path else "Repository" dep_dict = {repo_name: deps[:15]} # Limit for readability return self.mermaid_generator.generate_dependency_graph( dep_dict, f"{repo_name} Dependencies" ) return "graph TD\n A[No dependencies found]" elif diagram_type == 'file_structure': structure = comprehensive_data.get('project_structure', {}) if structure: structure_dict = self._convert_structure_to_dict(structure) return self.mermaid_generator.generate_file_structure_diagram( structure_dict, "Project Structure", max_depth=3 ) return "graph TD\n A[No file structure available]" elif diagram_type == 'api_flow': endpoints = comprehensive_data.get('api_endpoints', []) if endpoints: return self.mermaid_generator.generate_api_flow_diagram( endpoints[:10], "API Flow" # Limit for readability ) return "sequenceDiagram\n participant C as Client\n participant A as API\n Note over C,A: No API endpoints found" elif diagram_type == 'database_er': db_schemas = comprehensive_data.get('database_schemas', []) if db_schemas: # Combine all tables from all schemas all_tables = [] for schema in db_schemas: all_tables.extend(schema.get('tables', [])) if all_tables: return self._generate_er_diagram(all_tables[:10]) # Limit for readability return "erDiagram\n NOTE {\n string message\n }\n NOTE : \"No database schemas found\"" else: return f"graph TD\n A[{diagram_type} diagram not implemented]" except Exception as e: return f"graph TD\n Error[\"Error generating {diagram_type}: {str(e)}\"]" def _generate_er_diagram(self, tables: List[Dict[str, Any]]) -> str: """Generate entity relationship diagram.""" er_diagram = "erDiagram\n" for table in tables[:10]: # Limit to prevent overcrowding table_name = table.get('name', 'UNKNOWN') columns = table.get('columns', []) for column in columns[:5]: # Limit columns per table column_name = column.get('name', 'unknown') column_type = column.get('type', 'string') nullable = '' if column.get('nullable', True) else ' NOT NULL' er_diagram += f" {table_name} {{\n {column_type} {column_name}{nullable}\n }}\n" return er_diagram def _perform_security_analysis(self, comprehensive_data: Dict[str, Any]) -> Dict[str, Any]: """Perform comprehensive security analysis.""" security_analysis = { 'issues_found': [], 'severity_counts': {'low': 0, 'medium': 0, 'high': 0, 'critical': 0}, 'recommendations': [], 'compliance_score': 0 } # Analyze dependencies for known vulnerabilities (mock implementation) dependencies = comprehensive_data.get('dependencies', []) for dep in dependencies: # In a real implementation, this would check against vulnerability databases if 'old' in str(dep).lower() or 'deprecated' in str(dep).lower(): security_analysis['issues_found'].append({ 'type': 'outdated_dependency', 'severity': 'medium', 'description': f"Potentially outdated dependency: {dep}", 'recommendation': f"Update {dep} to latest version" }) security_analysis['severity_counts']['medium'] += 1 # Check for common security patterns in code (mock implementation) # This would analyze the actual code in a real implementation return security_analysis def _extract_project_title(self, comprehensive_data: Dict[str, Any]) -> str: """Extract project title from analysis data.""" # Try to get from path path = comprehensive_data.get('path', '') if path: title = path.split('/')[-1] if '/' in path else path return title.replace('-', ' ').replace('_', ' ').title() return "Project Documentation" def _calculate_documentation_stats(self, main_doc: str, comprehensive_data: Dict[str, Any], generated_files: List[Dict], exported_files: List[Dict]) -> Dict[str, Any]: """Calculate comprehensive documentation statistics.""" stats = { 'word_count': len(main_doc.split()) if main_doc else 0, 'character_count': len(main_doc) if main_doc else 0, 'section_count': main_doc.count('##') if main_doc else 0, 'code_block_count': main_doc.count('```') // 2 if main_doc else 0, 'diagram_count': len(comprehensive_data.get('mermaid_diagrams', {})), 'files_generated': len(generated_files), 'files_exported': len([f for f in exported_files if f.get('status') == 'success']), 'total_file_size': sum(f.get('size', 0) for f in generated_files + exported_files), 'estimated_reading_time': (len(main_doc.split()) // 200) if main_doc else 0 # ~200 WPM } return stats async def _export_single_format(self, data: Dict[str, Any], format_type: str, output_path: str, theme: str, title: str, language: str, custom_css: Optional[str], custom_header: Optional[str], custom_footer: Optional[str], include_toc: bool, include_diagrams: bool, include_search: bool) -> Dict[str, Any]: """Export to a single format with all options.""" try: exported_content = self.doc_generator.export_documentation( analysis_result=data, format=format_type, theme=theme, title=title, include_toc=include_toc, include_diagrams=include_diagrams, include_search=include_search and format_type == 'html', custom_css=custom_css, output_path=output_path ) file_size = 0 if os.path.exists(output_path): file_size = os.path.getsize(output_path) return { 'format': format_type, 'path': output_path, 'size_bytes': file_size, 'size_human': self._format_file_size(file_size), 'language': language, 'status': 'success' } except Exception as e: return { 'format': format_type, 'path': output_path, 'size_bytes': 0, 'language': language, 'status': 'failed', 'error': str(e) } def _validate_exported_file(self, export_result: Dict[str, Any], format_type: str) -> Dict[str, Any]: """Validate exported file.""" validation = { 'valid': False, 'file_exists': False, 'file_size': 0, 'format_specific_checks': {}, 'issues': [] } file_path = export_result.get('path') if file_path and os.path.exists(file_path): validation['file_exists'] = True validation['file_size'] = os.path.getsize(file_path) if validation['file_size'] > 0: validation['valid'] = True else: validation['issues'].append('File is empty') # Format-specific validation if format_type == 'html': validation['format_specific_checks']['html'] = self._validate_html_file(file_path) elif format_type == 'pdf': validation['format_specific_checks']['pdf'] = self._validate_pdf_file(file_path) # Add more format-specific validations as needed else: validation['issues'].append('File does not exist') return validation def _validate_html_file(self, file_path: str) -> Dict[str, Any]: """Validate HTML file.""" validation = {'valid': True, 'issues': []} try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() if '<!DOCTYPE html>' not in content: validation['issues'].append('Missing DOCTYPE declaration') if '<html' not in content: validation['issues'].append('Missing HTML tag') if '<title>' not in content: validation['issues'].append('Missing title tag') validation['valid'] = len(validation['issues']) == 0 except Exception as e: validation['valid'] = False validation['issues'].append(f'Error reading file: {str(e)}') return validation def _validate_pdf_file(self, file_path: str) -> Dict[str, Any]: """Validate PDF file.""" validation = {'valid': True, 'issues': []} try: with open(file_path, 'rb') as f: header = f.read(4) if header != b'%PDF': validation['valid'] = False validation['issues'].append('Invalid PDF header') except Exception as e: validation['valid'] = False validation['issues'].append(f'Error reading file: {str(e)}') return validation def _optimize_export(self, export_result: Dict[str, Any]) -> Dict[str, Any]: """Optimize exported file.""" optimization = { 'applied': False, 'original_size': export_result.get('size_bytes', 0), 'optimized_size': export_result.get('size_bytes', 0), 'savings': 0, 'methods': [] } # Mock optimization - in reality would perform actual optimizations optimization['applied'] = True optimization['methods'] = ['compression', 'minification'] return optimization def _create_export_archive(self, export_results: List[Dict], output_dir: str, title: str) -> Dict[str, Any]: """Create comprehensive archive of all exports.""" try: import zipfile archive_path = os.path.join(output_dir, f"{title.replace(' ', '_').lower()}_complete_export.zip") with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zipf: for result in export_results: if result.get('status') == 'success' and os.path.exists(result.get('path', '')): file_path = result['path'] arcname = f"{result['format']}/{os.path.basename(file_path)}" zipf.write(file_path, arcname) archive_size = os.path.getsize(archive_path) return { 'status': 'success', 'path': archive_path, 'size_bytes': archive_size, 'size_human': self._format_file_size(archive_size), 'files_included': len([r for r in export_results if r.get('status') == 'success']) } except Exception as e: return { 'status': 'failed', 'error': str(e) } def _generate_sitemap(self, export_results: List[Dict], output_dir: str) -> Dict[str, Any]: """Generate XML sitemap for HTML exports.""" try: sitemap_path = os.path.join(output_dir, 'sitemap.xml') sitemap_content = '<?xml version="1.0" encoding="UTF-8"?>\n' sitemap_content += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n' for result in export_results: if result.get('format') == 'html' and result.get('status') == 'success': file_path = result.get('path', '') if file_path: # Convert file path to URL (simplified) url = f"file://{os.path.abspath(file_path)}" sitemap_content += f' <url>\n' sitemap_content += f' <loc>{url}</loc>\n' sitemap_content += f' <lastmod>{datetime.now().strftime("%Y-%m-%d")}</lastmod>\n' sitemap_content += f' </url>\n' sitemap_content += '</urlset>\n' with open(sitemap_path, 'w', encoding='utf-8') as f: f.write(sitemap_content) return { 'status': 'success', 'path': sitemap_path, 'urls_included': len([r for r in export_results if r.get('format') == 'html' and r.get('status') == 'success']) } except Exception as e: return { 'status': 'failed', 'error': str(e) } def _calculate_export_statistics(self, export_results: List[Dict], total_size: int) -> Dict[str, Any]: """Calculate comprehensive export statistics.""" stats = { 'total_exports': len(export_results), 'successful_exports': len([r for r in export_results if r.get('status') == 'success']), 'failed_exports': len([r for r in export_results if r.get('status') == 'failed']), 'total_size_bytes': total_size, 'average_file_size': total_size // len(export_results) if export_results else 0, 'format_breakdown': {}, 'size_breakdown': {} } # Calculate format breakdown for result in export_results: format_type = result.get('format', 'unknown') status = result.get('status', 'unknown') if format_type not in stats['format_breakdown']: stats['format_breakdown'][format_type] = {'success': 0, 'failed': 0} stats['format_breakdown'][format_type][status] = stats['format_breakdown'][format_type].get(status, 0) + 1 # Size breakdown size = result.get('size_bytes', 0) if size > 0: stats['size_breakdown'][format_type] = stats['size_breakdown'].get(format_type, 0) + size return stats def _format_file_size(self, size_bytes: int) -> str: """Format file size in human readable format.""" for unit in ['B', 'KB', 'MB', 'GB', 'TB']: if size_bytes < 1024: return f"{size_bytes:.1f} {unit}" size_bytes /= 1024 return f"{size_bytes:.1f} PB" # Additional helper methods for comprehensive functionality def _extract_architecture_components(self, comprehensive_data: Dict[str, Any]) -> List[Dict[str, Any]]: """Extract architecture components from comprehensive analysis data.""" components = [] # Add main application component frameworks = comprehensive_data.get('frameworks', []) if frameworks: main_fw = frameworks[0] components.append({ 'name': 'Application', 'type': main_fw.get('category', 'application'), 'description': f"Main application using {main_fw.get('name', 'unknown framework')}" }) else: components.append({ 'name': 'Application', 'type': 'application', 'description': 'Main application component' }) # Add database component if detected if comprehensive_data.get('database_schemas'): db_count = len(comprehensive_data['database_schemas']) components.append({ 'name': 'Database', 'type': 'database', 'description': f"Database layer with {db_count} schema(s)" }) # Add external services if APIs detected if comprehensive_data.get('api_endpoints'): api_count = len(comprehensive_data['api_endpoints']) components.append({ 'name': 'External APIs', 'type': 'external', 'description': f"External integration with {api_count} endpoint(s)" }) # Add frontend component if web frameworks detected web_frameworks = [fw for fw in frameworks if fw.get('category') == 'frontend_framework'] if web_frameworks: components.append({ 'name': 'Frontend', 'type': 'frontend', 'description': f"Frontend using {web_frameworks[0].get('name', 'unknown')}" }) return components def _extract_architecture_relationships(self, comprehensive_data: Dict[str, Any]) -> List[Dict[str, str]]: """Extract architecture relationships from comprehensive analysis data.""" relationships = [] # Application to APIs if comprehensive_data.get('api_endpoints'): relationships.append({ 'from': 'Application', 'to': 'External APIs', 'type': 'uses', 'description': 'Makes API calls' }) # Application to Database if comprehensive_data.get('database_schemas'): relationships.append({ 'from': 'Application', 'to': 'Database', 'type': 'stores', 'description': 'Stores and retrieves data' }) # Frontend to Application frameworks = comprehensive_data.get('frameworks', []) web_frameworks = [fw for fw in frameworks if fw.get('category') == 'frontend_framework'] if web_frameworks: relationships.append({ 'from': 'Frontend', 'to': 'Application', 'type': 'communicates', 'description': 'Sends requests and receives responses' }) return relationships def _convert_structure_to_dict(self, structure) -> Dict[str, Any]: """Convert project structure to dict format for mermaid generator.""" try: if hasattr(structure, 'subdirectories') and hasattr(structure, 'files'): # It's a DirectoryInfo object result = {} # Add files (limit for readability) for file_info in (structure.files or [])[:10]: if hasattr(file_info, 'name'): result[file_info.name] = f"file_{file_info.name}" # Add subdirectories (limit for readability) for subdir in (structure.subdirectories or [])[:5]: if hasattr(subdir, 'name'): result[subdir.name] = self._convert_structure_to_dict(subdir) return result elif isinstance(structure, dict): # Already a dict - limit depth and breadth limited_result = {} count = 0 for key, value in structure.items(): if count >= 10: # Limit to 10 items per level break if isinstance(value, dict): limited_result[key] = self._convert_structure_to_dict(value) else: limited_result[key] = value count += 1 return limited_result else: return {'unknown': 'structure'} except Exception as e: logger.warning(f"Error converting structure to dict: {e}") return {'error': 'structure_conversion_failed'} # Cache management methods def clear_analysis_cache(self): """Clear the analysis cache.""" self.analysis_cache.clear() logger.info("Analysis cache cleared") def get_cache_info(self) -> Dict[str, Any]: """Get information about the current cache.""" return { 'cached_analyses': len(self.analysis_cache), 'analysis_ids': list(self.analysis_cache.keys()), 'memory_usage_estimate': sum( len(str(data)) for data in self.analysis_cache.values() ) } def remove_from_cache(self, analysis_id: str) -> bool: """Remove specific analysis from cache.""" if analysis_id in self.analysis_cache: del self.analysis_cache[analysis_id] logger.info(f"Removed analysis {analysis_id} from cache") return True return False

Latest Blog Posts

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/vedantparmar12/Document-Automation'

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