Skip to main content
Glama
jermeyyy
by jermeyyy

clean

Remove build artifacts from a Gradle project to free disk space and ensure clean builds. Specify a project path or use root project for complete cleanup.

Instructions

Clean build artifacts for a Gradle project.

Args: project: Project path (e.g., ':app'). Use None, empty string, or ':' for root project.

Returns: TaskResult with success status and error message if failed.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
errorNo
successYes

Implementation Reference

  • MCP tool handler for the 'clean' tool. Delegates execution to GradleWrapper.clean while handling MCP context, progress, and logging.
    @mcp.tool()
    async def clean(
        project: str | None = None,
        ctx: Context | None = None,
    ) -> TaskResult:
        """Clean build artifacts for a Gradle project.
    
        Args:
            project: Project path (e.g., ':app').
                    Use None, empty string, or ':' for root project.
    
        Returns:
            TaskResult with success status and error message if failed.
        """
        try:
            if ctx:
                await ctx.info(f"Cleaning project: {project or 'root'}")
    
            gradle = _get_gradle_wrapper(ctx)
            daemon_monitor = _get_daemon_monitor()
    
            # clean now handles progress reporting internally by parsing Gradle output
            result = await gradle.clean(project, ctx, daemon_monitor=daemon_monitor)
    
            # Log Gradle output
            if ctx:
                if result.get("stdout"):
                    await ctx.debug(f"Gradle stdout:\n{result['stdout']}")
                if result.get("stderr"):
                    await ctx.debug(f"Gradle stderr:\n{result['stderr']}")
    
                if result["success"]:
                    await ctx.info(f"Clean completed successfully for project {project or 'root'}")
                else:
                    await ctx.error(
                        f"Clean failed for project {project or 'root'}",
                        extra={"error": result.get("error")},
                    )
    
            return TaskResult(
                success=result["success"],
                error=result.get("error"),
            )
        except Exception as e:
            return TaskResult(
                success=False,
                error=ErrorInfo(
                    summary=str(e),
                    failed_tasks=[],
                    compilation_errors=[],
                ),
            )
  • FastMCP decorator that registers the 'clean' function as an MCP tool.
    @mcp.tool()
  • Pydantic model defining the output schema for the 'clean' tool (and other task tools). Contains success flag and optional structured error info.
    class TaskResult(BaseModel):
        """Result of running a Gradle task."""
    
        success: bool
        error: ErrorInfo | None = None
  • Core implementation in GradleWrapper that executes the actual Gradle clean task via subprocess, handles streaming output, progress, timeouts, error parsing, and dashboard logging.
    async def clean(
        self,
        project: str | None = None,
        ctx: "Context | None" = None,
        timeout: int | None = None,
        daemon_monitor: "DaemonMonitor | None" = None,
    ) -> dict:
        """Run the clean task for a project.
    
        Args:
            project: Project path (e.g., ':app'). Use ':' or empty string or None for root project.
            ctx: FastMCP context for progress reporting and logging.
            timeout: Optional timeout in seconds. If specified, the task will be
                     terminated if it exceeds this duration.
            daemon_monitor: Optional DaemonMonitor for logging to the dashboard.
    
        Returns:
            Dictionary with 'success', 'error' keys.
            - success (bool): True if clean completed successfully
            - error (str or None): Error message if clean failed, None otherwise
    
        Raises:
            subprocess.CalledProcessError: If Gradle command fails.
        """
        # Root project if project is None, empty, or ":"
        is_root = project is None or project == "" or project == ":"
        project_arg = "" if is_root else f"{project}:"
        # Build command - daemon is enabled by default
        # Use --console=plain for clean, parseable output without ANSI codes
        cmd = [str(self.wrapper_script), "--console=plain", f"{project_arg}clean"]
    
        logger.info(f"Executing: {' '.join(cmd)}")
    
        # Track this build in the dashboard
        build_id = None
        try:
            build_id = _log_store.start_build(f"{project_arg}clean")
        except Exception:
            pass
    
        process: asyncio.subprocess.Process | None = None
        try:
            process = await asyncio.create_subprocess_exec(
                *cmd,
                cwd=str(self.project_root),
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
                env=self._build_execution_environment(),
            )
    
            # Notify dashboard that a task started (may have spawned a new daemon)
            if _emit_daemons_changed:
                try:
                    _emit_daemons_changed()
                except Exception:
                    pass
    
            # Stream output in real-time while collecting it
            stdout_lines: list[str] = []
            stderr_lines: list[str] = []
    
            async def read_stream(
                stream: asyncio.StreamReader, lines: list[str], is_stderr: bool = False
            ) -> None:
                while True:
                    line = await stream.readline()
                    if not line:
                        break
                    decoded = line.decode().rstrip()
                    lines.append(decoded)
                    # Log to shared log_store for dashboard
                    if _log_store and decoded.strip():
                        level = _classify_gradle_log_level(decoded, is_stderr)
                        _log_store.add_log(decoded, level)
    
            # Handle timeout with streaming
            try:
                if timeout:
                    await asyncio.wait_for(
                        asyncio.gather(
                            read_stream(process.stdout, stdout_lines, False),
                            read_stream(process.stderr, stderr_lines, True),
                        ),
                        timeout=timeout,
                    )
                else:
                    await asyncio.gather(
                        read_stream(process.stdout, stdout_lines, False),
                        read_stream(process.stderr, stderr_lines, True),
                    )
                await process.wait()
            except asyncio.TimeoutError:
                # Graceful termination first
                logger.warning(f"Clean timed out after {timeout} seconds, terminating process...")
                process.terminate()
                try:
                    await asyncio.wait_for(process.wait(), timeout=5)
                except asyncio.TimeoutError:
                    # Force kill if graceful termination fails
                    logger.warning("Process did not terminate gracefully, killing...")
                    process.kill()
                    await process.wait()
                if daemon_monitor:
                    daemon_monitor.add_log("gradle", f"Clean timed out after {timeout} seconds", "ERROR")
                return {"success": False, "error": f"Clean timed out after {timeout} seconds"}
    
            stdout = "\n".join(stdout_lines)
            stderr = "\n".join(stderr_lines)
    
            # Log output for debugging
            for line in stdout_lines:
                if line.strip():
                    logger.debug(line)
            for line in stderr_lines:
                if line.strip():
                    # Use appropriate log level based on content, not just stderr
                    level = _classify_gradle_log_level(line, is_stderr=True)
                    if level == "ERROR":
                        logger.error(line)
                    elif level == "WARN":
                        logger.warning(line)
                    else:
                        logger.debug(line)
    
            # Report progress if context is available (final progress)
            if ctx:
                await ctx.report_progress(progress=100, total=100)
    
            if process.returncode == 0:
                logger.info(f"Clean completed successfully for project {project or 'root'}")
                # End build tracking
                if build_id and _log_store:
                    try:
                        _log_store.end_build(build_id)
                    except Exception:
                        pass
                return {"success": True, "error": None}
            else:
                # Extract structured error information
                error_info = self._extract_structured_error(stdout, stderr, "Clean failed")
                logger.error(f"Clean failed: {error_info.summary}")
                # End build tracking
                if build_id and _log_store:
                    try:
                        _log_store.end_build(build_id)
                    except Exception:
                        pass
                return {"success": False, "error": error_info}
    
        except Exception as e:
            # Ensure process cleanup on any exception
            if process and process.returncode is None:
                logger.warning(f"Exception occurred, cleaning up process: {e}")
                process.terminate()
                try:
                    await asyncio.wait_for(process.wait(), timeout=5)
                except asyncio.TimeoutError:
                    process.kill()
                    await process.wait()
            logger.error(f"Clean failed with exception: {e}")
            # End build tracking
            if build_id and _log_store:
                try:
                    _log_store.end_build(build_id)
                except Exception:
                    pass
            # Return structured error for exceptions
            return {
                "success": False,
                "error": ErrorInfo(
                    summary=str(e),
                    failed_tasks=[],
                    compilation_errors=[],
                ),
            }
  • Pydantic model used in TaskResult.error for structured error reporting from Gradle clean (and other tasks). Includes failed tasks and compilation errors.
    class ErrorInfo(BaseModel):
        """Structured error information."""
    
        summary: str  # e.g., "Build failed: 2 tasks failed with 12 compilation errors"
        failed_tasks: list[FailedTask]  # List of failed tasks
        compilation_errors: list[CompilationError]  # Deduplicated, first occurrence only

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other Tools

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/jermeyyy/gradle-mcp'

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