Skip to main content
Glama
djm81

Log Analyzer MCP

by djm81

create_coverage_report

Generate HTML and XML coverage reports by running analysis scripts to document test execution results and code coverage metrics.

Instructions

Run the coverage report script and generate HTML and XML reports.

Args:
    force_rebuild: Whether to force rebuilding the report even if it exists

Returns:
    Dictionary containing execution results and report paths

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
force_rebuildNo

Implementation Reference

  • The MCP tool handler function for 'create_coverage_report', registered via @mcp.tool() decorator. It delegates the core logic to the run_coverage_script helper.
    @mcp.tool()
    async def create_coverage_report(force_rebuild: bool = False) -> dict[str, Any]:
        """
        Run the coverage report script and generate HTML and XML reports.
    
        Args:
            force_rebuild: Whether to force rebuilding the report even if it exists
    
        Returns:
            Dictionary containing execution results and report paths
        """
        return await run_coverage_script(force_rebuild)
  • Core helper function implementing the coverage report generation by running 'hatch run hatch-test.py3.12:run-cov' and 'hatch run hatch-test.py3.12:cov-report' commands, handling outputs, errors, and timeouts.
    async def run_coverage_script(force_rebuild: bool = False) -> dict[str, Any]:
        """
        Run the coverage report script and generate HTML and XML reports.
        Now uses hatch scripts for better integration.
        """
        logger.info("Running coverage script...")
        # Correctly reference PROJECT_ROOT from the logger_setup module
        from log_analyzer_mcp.common import logger_setup as common_logger_setup  # Ensure this import is here or global
    
        current_project_root = common_logger_setup.PROJECT_ROOT
        # Define different timeouts for different steps
        timeout_run_cov = 300  # Longer timeout for running tests with coverage
        timeout_cov_report = 120  # Shorter timeout for generating the report
    
        # Command parts for running the coverage script via hatch
        # This assumes 'run-cov' and 'cov-report' are defined in hatch envs.
        # Step 1: Run tests with coverage enabled
        cmd_parts_run_cov = ["hatch", "run", "hatch-test.py3.12:run-cov"]  # Example: Target specific py version
        # Step 2: Generate combined report (HTML and XML)
        cmd_parts_report = ["hatch", "run", "hatch-test.py3.12:cov-report"]  # Example
    
        outputs = []
        errors_encountered = []
    
        steps_with_timeouts = [
            ("run-cov", cmd_parts_run_cov, timeout_run_cov),
            ("cov-report", cmd_parts_report, timeout_cov_report),
        ]
    
        for step_name, cmd_parts, current_timeout_seconds in steps_with_timeouts:
            logger.info(
                "Executing coverage step '%s': %s (timeout: %ss)", step_name, " ".join(cmd_parts), current_timeout_seconds
            )
            try:
                # Use functools.partial for subprocess.run
                configured_subprocess_run_step = functools.partial(
                    subprocess.run,
                    cmd_parts,
                    cwd=current_project_root,
                    capture_output=True,
                    text=True,  # Decode stdout/stderr as text
                    check=False,  # Handle non-zero exit manually
                    timeout=current_timeout_seconds,  # Use current step's timeout
                )
                process = await anyio.to_thread.run_sync(configured_subprocess_run_step)  # type: ignore[attr-defined]
                stdout_output: str = process.stdout
                stderr_output: str = process.stderr
                rc = process.returncode
    
                outputs.append(f"--- {step_name} STDOUT ---\n{stdout_output}")
                if stderr_output:
                    outputs.append(f"--- {step_name} STDERR ---\n{stderr_output}")
    
                if rc != 0:
                    error_msg = f"Coverage step '{step_name}' failed with return code {rc}."
                    logger.error("%s\nSTDERR:\n%s", error_msg, stderr_output)
                    errors_encountered.append(error_msg)
                    # Optionally break if a step fails, or collect all errors
                    # break
    
            except subprocess.TimeoutExpired as e:
                stdout_output = e.stdout.decode("utf-8", errors="replace") if e.stdout else ""
                stderr_output = e.stderr.decode("utf-8", errors="replace") if e.stderr else ""
                error_msg = f"Coverage step '{step_name}' timed out after {current_timeout_seconds} seconds."
                logger.error("%s: %s", error_msg, e)
                errors_encountered.append(error_msg)
                outputs.append(f"--- {step_name} TIMEOUT STDOUT ---\n{stdout_output}")
                outputs.append(f"--- {step_name} TIMEOUT STDERR ---\n{stderr_output}")
                # break
            except Exception as e:  # pylint: disable=broad-exception-caught
                error_msg = f"Error during coverage step '{step_name}': {e}"
                logger.error(error_msg, exc_info=True)
                errors_encountered.append(error_msg)
                # break
    
        # Ensure a dictionary is always returned, even if errors occurred.
        final_success = not errors_encountered
        overall_message = (
            "Coverage script steps completed." if final_success else "Errors encountered during coverage script execution."
        )
        # Placeholder for actual report paths, adapt as needed
        coverage_xml_report_path = os.path.join(logs_base_dir, "tests", "coverage", "coverage.xml")
        coverage_html_index_path = os.path.join(logs_base_dir, "tests", "coverage", "html", "index.html")
    
        return {
            "success": final_success,
            "message": overall_message,
            "details": "\n".join(outputs),
            "errors": errors_encountered,
            "coverage_xml_path": coverage_xml_report_path if final_success else None,  # Example path
            "coverage_html_index": coverage_html_index_path if final_success else None,  # Example path
            "timestamp": datetime.now().isoformat(),
        }
  • Pydantic BaseModel defining the input parameters for the create_coverage_report tool, including the force_rebuild flag.
    class CreateCoverageReportInput(BaseModel):
        """Parameters for creating coverage report."""
    
        force_rebuild: bool = Field(
            default=False, description="Whether to force rebuilding the coverage report even if it already exists"
        )
  • The @mcp.tool() decorator registers the create_coverage_report function as an MCP tool, with the tool name inferred from the function name.
    @mcp.tool()
    async def create_coverage_report(force_rebuild: bool = False) -> dict[str, Any]:

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/djm81/log_analyzer_mcp'

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