Skip to main content
Glama

adv_scan_folder

Scan directories for security vulnerabilities using Clean Architecture with session-aware project analysis and configurable detection options.

Instructions

Scan a directory for security vulnerabilities using Clean Architecture. Automatically uses session-aware project analysis when LLM is configured.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathNoPath to the directory to scan.
use_semgrepNoEnable Semgrep analysis
use_llmNoEnable LLM analysis
use_validationNoEnable LLM validation
severity_thresholdNoMinimum severity levelmedium
timeout_secondsNoScan timeout in seconds
recursiveNoScan subdirectories
output_formatNoOutput format for persisted scan resultsjson

Implementation Reference

  • Main handler for 'adv_scan_folder' MCP tool. Validates input, orchestrates directory scan via ScanApplicationService, persists results, formats JSON response with scan metadata, threats, summary, and persistence info.
    async def _handle_scan_folder(
        self, name: str, arguments: dict
    ) -> list[types.TextContent]:
        """Handle folder scanning requests."""
        try:
            # Log MCP tool invocation at INFO level for visibility
            logger.info(f"MCP Tool Invoked: {name}")
            logger.info(f"Parameters: {arguments}")
    
            # Comprehensive input validation
            validated_args = self._input_validator.validate_mcp_arguments(
                arguments, tool_name="adv_scan_folder"
            )
    
            path = validated_args.get("path", ".")
            use_semgrep = validated_args.get("use_semgrep", True)
            use_llm = validated_args.get("use_llm", False)
            use_validation = validated_args.get("use_validation", False)
            severity_threshold = validated_args.get("severity_threshold", "medium")
            timeout_seconds = validated_args.get("timeout_seconds")
            recursive = validated_args.get("recursive", True)
            output_format = validated_args.get("output_format", "json")
    
            # Use the same scan service as CLI for consistency and proper orchestration
            result = await self._scan_service.scan_directory(
                directory_path=path,
                requester="cli",
                enable_semgrep=use_semgrep,
                enable_llm=use_llm,
                enable_validation=use_validation,
                severity_threshold=severity_threshold,
                timeout_seconds=timeout_seconds,
                recursive=recursive,
            )
    
            # Persist scan result automatically
            try:
                output_format_enum = OutputFormat.from_string(output_format)
                file_path = await self._persistence_service.persist_scan_result(
                    result, output_format_enum
                )
                logger.info(f"Scan result persisted to {file_path}")
            except Exception as e:
                logger.warning(f"Failed to persist scan result: {e}")
                # Don't fail the scan if persistence fails
    
            formatted_result = self._format_scan_result(result)
    
            # Add persistence info to the response
            formatted_result["persistence"] = {
                "output_format": output_format,
                "file_path": file_path if "file_path" in locals() else None,
                "persisted": "file_path" in locals(),
            }
    
            # Log successful completion with key metrics
            threat_count = (
                len(result.threat_matches) if hasattr(result, "threat_matches") else 0
            )
            scan_duration = (
                getattr(result.metadata, "scan_duration_seconds", 0)
                if hasattr(result, "metadata")
                else 0
            )
            files_scanned = (
                getattr(result.metadata, "total_files_scanned", 0)
                if hasattr(result, "metadata")
                else 0
            )
            logger.info(
                f"[+] MCP Tool Completed: {name} | Threats: {threat_count} | Files: {files_scanned} | Duration: {scan_duration:.2f}s"
            )
    
            return [
                types.TextContent(
                    type="text",
                    text=json.dumps(formatted_result, indent=2, default=str),
                )
            ]
    
        except (ValidationError, SecurityError, ConfigurationError) as e:
            logger.error(f"Directory scan failed: {e}")
            raise CleanAdversaryToolError(f"Scan failed: {str(e)}")
        except Exception as e:
            logger.error(f"Unexpected error in directory scan: {e}")
            logger.error(traceback.format_exc())
            raise CleanAdversaryToolError(f"Internal error: {str(e)}")
  • Input schema definition for adv_scan_folder tool returned by get_tools(), specifying parameters like path (directory), booleans for scanners, severity_threshold, recursive, output_format.
    Tool(
        name="adv_scan_folder",
        description="Scan a directory for security vulnerabilities using Clean Architecture. Automatically uses session-aware project analysis when LLM is configured.",
        inputSchema={
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Path to the directory to scan",
                    "default": ".",
                },
                "use_semgrep": {
                    "type": "boolean",
                    "description": "Enable Semgrep analysis",
                    "default": True,
                },
                "use_llm": {
                    "type": "boolean",
                    "description": "Enable LLM analysis",
                    "default": False,
                },
                "use_validation": {
                    "type": "boolean",
                    "description": "Enable LLM validation",
                    "default": False,
                },
                "severity_threshold": {
                    "type": "string",
                    "description": "Minimum severity level",
                    "default": "medium",
                },
                "timeout_seconds": {
                    "type": "integer",
                    "description": "Scan timeout in seconds",
                },
                "recursive": {
                    "type": "boolean",
                    "description": "Scan subdirectories",
                    "default": True,
                },
                "output_format": {
                    "type": "string",
                    "description": "Output format for persisted scan results",
                    "enum": ["json", "md", "markdown", "csv"],
                    "default": "json",
                },
            },
        },
    ),
    Tool(
  • Registration of tool dispatcher in _register_tools(). Routes calls to 'adv_scan_folder' to _handle_scan_folder handler.
    @self.server.call_tool()
    async def tool_dispatcher(
        name: str, arguments: dict
    ) -> list[types.TextContent]:
        """Dispatch MCP tool calls to the appropriate handler."""
        if name == "adv_scan_file":
            return await self._handle_scan_file(name, arguments)
        elif name == "adv_scan_folder":
            return await self._handle_scan_folder(name, arguments)
        elif name == "adv_scan_code":
            return await self._handle_scan_code(name, arguments)
        elif name == "adv_get_status":
            return await self._handle_get_status(name, arguments)
        elif name == "adv_get_version":
            return await self._handle_get_version(name, arguments)
        elif name == "adv_mark_false_positive":
            return await self._handle_mark_false_positive(name, arguments)
        elif name == "adv_unmark_false_positive":
            return await self._handle_unmark_false_positive(name, arguments)
        else:
            raise ValueError(f"Unknown tool: {name}")
  • Core scanning logic delegated by handler: creates domain ScanRequest for directory, validates, executes via ScanOrchestrator (Semgrep/LLM strategies), returns ScanResult.
    async def scan_directory(
        self,
        directory_path: str,
        *,
        requester: str = "application",
        enable_semgrep: bool = True,
        enable_llm: bool = False,
        enable_validation: bool = False,
        severity_threshold: str | None = None,
        timeout_seconds: int | None = None,
        recursive: bool = True,
    ) -> ScanResult:
        """
        Scan a directory for security vulnerabilities.
    
        Args:
            directory_path: Path to the directory to scan
            requester: Who requested the scan
            enable_semgrep: Whether to enable Semgrep scanning
            enable_llm: Whether to enable LLM analysis
            enable_validation: Whether to enable LLM validation
            severity_threshold: Minimum severity level to include
            timeout_seconds: Scan timeout in seconds
            recursive: Whether to scan subdirectories
    
        Returns:
            ScanResult containing found threats and metadata
        """
        # Create domain objects
        dir_path_obj = FilePath.from_string(directory_path)
    
        metadata = ScanMetadata(
            scan_id=str(uuid.uuid4()),
            scan_type="directory",
            timestamp=datetime.now(UTC),
            requester=requester,
            timeout_seconds=timeout_seconds
            or 600,  # Default 10 minutes for directories
            enable_semgrep=enable_semgrep,
            enable_llm=enable_llm,
            enable_validation=enable_validation,
        )
    
        context = ScanContext(target_path=dir_path_obj, metadata=metadata)
    
        severity_level = (
            SeverityLevel.from_string(severity_threshold)
            if severity_threshold
            else None
        )
    
        request = ScanRequest(
            context=context,
            enable_semgrep=enable_semgrep,
            enable_llm=enable_llm,
            enable_validation=enable_validation,
            severity_threshold=severity_level,
        )
    
        # Validate and execute
        self._validation_service.validate_scan_request(request)
        self._validation_service.enforce_security_constraints(context)
    
        result = await self._scan_orchestrator.execute_scan(request)
    
        return result
  • Input validation logic in validate_mcp_arguments specifically treats 'path' as directory for adv_scan_folder tool, calling validate_directory_path which checks for path traversal, existence, etc.
    # Context-aware path validation based on tool name
    if tool_name in ("adv_scan_folder", "adv_diff_scan"):
        # These tools expect directory paths
        validated[key] = str(
            InputValidator.validate_directory_path(str(value))
        )
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'session-aware project analysis' and implies automation with LLM configuration, but fails to describe critical behaviors such as whether the scan is destructive, what permissions are required, how results are returned (e.g., format, persistence), or any rate limits. This leaves significant gaps for a security scanning tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two sentences that efficiently convey the core functionality and a key behavioral aspect. It's front-loaded with the main purpose, though it could be slightly more structured by explicitly mentioning the tool's scope relative to siblings.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex security scanning tool with 8 parameters, no annotations, and no output schema, the description is incomplete. It lacks details on behavioral traits (e.g., safety, permissions), output handling, and differentiation from sibling tools, making it inadequate for full contextual understanding by an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, providing clear documentation for all 8 parameters. The description adds no additional parameter semantics beyond what's in the schema, such as explaining interactions between parameters (e.g., how 'use_llm' and 'use_validation' relate). Given the high schema coverage, the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Scan a directory for security vulnerabilities using Clean Architecture.' It specifies the verb ('scan'), resource ('directory'), and methodology ('Clean Architecture'), but doesn't explicitly differentiate it from sibling tools like 'adv_scan_code' or 'adv_scan_file' which likely perform similar scanning on different targets.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context with 'Automatically uses session-aware project analysis when LLM is configured,' which implies when LLM analysis is enabled. However, it doesn't offer explicit guidance on when to choose this tool over alternatives like 'adv_scan_code' or 'adv_scan_file,' nor does it specify prerequisites or exclusions for use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/brettbergin/adversary-mcp-server'

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