ASH MCP Server Guide
This guide provides comprehensive documentation for the ASH MCP server implementation, which uses a file-based approach to track scan progress and completion.
Overview
The Automated Security Helper (ASH) MCP server provides a reliable interface for AI assistants to perform security scans on your codebase. The server implements the Model Context Protocol (MCP) to enable seamless integration with AI assistants.
The MCP server uses a file-based approach to track scan progress and completion, making it more reliable than event-based tracking. This approach ensures that scan progress and results are accurately tracked even in complex threading scenarios.
Architecture
The ASH MCP server architecture consists of the following components:
- MCP Server: The main server component that registers and exposes MCP tools
- Scan Registry: A thread-safe registry that tracks active scans
- File-Based Tracking: A system that uses file existence to track scan progress
- Error Handling: Comprehensive error handling for various scenarios
File-Based Tracking Approach
The file-based tracking approach works as follows:
- When a scan is started, a unique scan ID is generated and registered in the scan registry
- The scan process is started asynchronously
- The scan progress is tracked by checking for the existence of result files:
ash_aggregated_results.json
: Indicates the scan has completed- Individual scanner result files: Indicate which scanners have completed
- The scan results are retrieved by parsing these files
This approach is more reliable than event-based tracking because it doesn't depend on in-memory state that could be lost due to threading issues.
File Structure
The file structure used for tracking is as follows:
.ash/ash_output/
├── ash_aggregated_results.json # Complete scan results
├── scanners/
│ ├── bandit/
│ │ ├── source/
│ │ │ └── ASH.ScanResults.json # Bandit scanner results
│ ├── detect-secrets/
│ │ ├── source/
│ │ │ └── ASH.ScanResults.json # Detect-secrets scanner results
│ └── ...
└── reports/
├── ash_report.csv # CSV report
├── ash_report.html # HTML report
└── ash_report.md # Markdown report
MCP Tools
The ASH MCP server provides the following tools:
scan_directory
Starts a security scan asynchronously and returns a scan ID for tracking.
Parameters:
- directory_path
(required): Path to the directory to scan
- severity_threshold
(optional): Minimum severity threshold (LOW, MEDIUM, HIGH, CRITICAL)
- config_path
(optional): Path to ASH configuration file
Returns: A dictionary with scan ID and status information.
Example:
result = await mcp_scan_directory(
directory_path="/path/to/your/code",
severity_threshold="MEDIUM",
config_path=None
)
Example Response:
{
"success": true,
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"directory_path": "/path/to/your/code",
"output_directory": "/path/to/your/code/.ash/ash_output",
"severity_threshold": "MEDIUM",
"config_path": null,
"start_time": "2025-07-16T12:34:56.789012",
"message": "Scan started successfully. Use get_scan_progress to track progress."
}
get_scan_progress
Gets the current progress of a running scan using file-based tracking.
Parameters:
- scan_id
(required): The scan ID returned from scan_directory
Returns: A dictionary with scan progress information.
Example:
Example Response:
{
"success": true,
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"completed_scanners": 2,
"total_scanners": 5,
"scanners": {
"bandit": {
"status": "completed",
"target_type": "source",
"finding_count": 3,
"severity_counts": {
"LOW": 1,
"MEDIUM": 2,
"HIGH": 0,
"CRITICAL": 0
}
},
"detect-secrets": {
"status": "completed",
"target_type": "source",
"finding_count": 0,
"severity_counts": {}
},
"semgrep": {
"status": "running",
"target_type": "source"
},
"checkov": {
"status": "pending",
"target_type": "source"
},
"cdk-nag": {
"status": "pending",
"target_type": "source"
}
},
"start_time": "2025-07-16T12:34:56.789012",
"current_time": "2025-07-16T12:36:23.456789",
"duration": 86.667777,
"timestamp": "2025-07-16T12:36:23.456789"
}
get_scan_results
Gets the final results of a completed scan using file-based tracking.
Parameters:
- scan_id
(required): The scan ID returned from scan_directory
Returns: A dictionary with scan results information.
Example:
Example Response:
{
"success": true,
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"finding_count": 5,
"severity_counts": {
"LOW": 1,
"MEDIUM": 3,
"HIGH": 1,
"CRITICAL": 0
},
"findings": [
{
"id": "finding-1",
"title": "Hardcoded password",
"severity": "HIGH",
"scanner": "detect-secrets",
"file_path": "config.py",
"line_number": 42,
"description": "Hardcoded password found in configuration file"
},
// Additional findings...
],
"scan_duration": 124.567890,
"start_time": "2025-07-16T12:34:56.789012",
"end_time": "2025-07-16T12:37:01.356902",
"timestamp": "2025-07-16T12:37:01.356902"
}
list_active_scans
Lists all active and recent scans with their current status.
Parameters: None
Returns: A dictionary with information about all scans in the registry.
Example:
Example Response:
{
"success": true,
"active_scans": [
{
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"directory_path": "/path/to/your/code",
"start_time": "2025-07-16T12:34:56.789012"
}
],
"all_scans": [
{
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"directory_path": "/path/to/your/code",
"start_time": "2025-07-16T12:34:56.789012"
},
{
"scan_id": "660e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"directory_path": "/path/to/another/code",
"start_time": "2025-07-16T11:22:33.444555",
"end_time": "2025-07-16T11:25:12.345678"
}
],
"stats": {
"total_scans": 2,
"active_scans": 1,
"status_counts": {
"running": 1,
"completed": 1,
"failed": 0,
"cancelled": 0
}
},
"timestamp": "2025-07-16T12:36:23.456789"
}
cancel_scan
Cancels a running scan and cleans up its resources.
Parameters:
- scan_id
(required): The scan ID to cancel
Returns: A dictionary with cancellation result information.
Example:
Example Response:
{
"success": true,
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "cancelled",
"message": "Scan cancelled successfully",
"timestamp": "2025-07-16T12:36:45.123456"
}
check_installation
Checks if ASH is properly installed and ready to use.
Parameters: None
Returns: A dictionary with installation status information.
Example:
Example Response:
{
"success": true,
"installed": true,
"version": "3.0.0",
"ash_command_available": true,
"ash_command_output": "ASH v3.0.0",
"ash_dir_exists": true,
"timestamp": "2025-07-16T12:37:12.345678"
}
Error Handling
The ASH MCP server provides comprehensive error handling for various scenarios. Each error response includes:
success
: Alwaysfalse
for error responsesoperation
: The operation that was being performederror
: Error messageerror_type
: Type of errorerror_category
: Category of errorcontext
: Additional context about the errorsuggestions
: List of suggestions for resolving the error
Error Categories
The following error categories are used:
file_not_found
: A required file or directory was not foundpermission_denied
: Permission issues accessing files or directoriesinvalid_format
: File format or structure is invalidinvalid_parameter
: Invalid parameter values providedinvalid_path
: Path is invalid or improperly formattedresource_exhausted
: System resources are exhaustedoperation_timeout
: Operation took too long and timed outscan_not_found
: Requested scan ID was not found in the registryscan_incomplete
: Scan has not completed yetunexpected_error
: An unexpected error occurred
Example Error Response
{
"success": false,
"operation": "get_scan_results",
"error": "Scan 550e8400-e29b-41d4-a716-446655440000 not found",
"error_type": "MCPResourceError",
"error_category": "scan_not_found",
"context": {
"scan_id": "550e8400-e29b-41d4-a716-446655440000",
"error_category": "scan_not_found"
},
"suggestions": [
"Check that the scan ID is correct",
"Verify that the scan exists in the registry",
"The scan may have been cleaned up if it was completed a long time ago"
],
"timestamp": "2025-07-16T12:37:12.345678"
}
Configuration
The ASH MCP server can be configured using the mcp-resource-management
section in the ASH configuration file:
# .ash/ash.yaml
mcp-resource-management:
# Concurrent operations
max_concurrent_scans: 5
max_concurrent_tasks: 30
thread_pool_max_workers: 8
# Timeouts
scan_timeout_seconds: 2400
operation_timeout_seconds: 300
# Resource monitoring
enable_health_checks: true
health_check_interval_seconds: 30
memory_warning_threshold_mb: 1024
memory_critical_threshold_mb: 2048
task_count_warning_threshold: 20
# Message limits
max_message_size_bytes: 10485760 # 10MB
max_directory_size_mb: 1024 # 1GB
Configuration Options
max_concurrent_scans
: Maximum number of concurrent scansmax_concurrent_tasks
: Maximum number of concurrent tasksthread_pool_max_workers
: Maximum number of worker threadsscan_timeout_seconds
: Timeout for scans in secondsoperation_timeout_seconds
: Timeout for operations in secondsenable_health_checks
: Whether to enable health checkshealth_check_interval_seconds
: Interval between health checks in secondsmemory_warning_threshold_mb
: Memory usage warning threshold in MBmemory_critical_threshold_mb
: Memory usage critical threshold in MBtask_count_warning_threshold
: Task count warning thresholdmax_message_size_bytes
: Maximum message size in bytesmax_directory_size_mb
: Maximum directory size in MB
Best Practices
When using the ASH MCP server, follow these best practices:
- Store scan IDs: Always store the scan ID returned by
scan_directory
for later use - Check scan progress: Periodically check scan progress using
get_scan_progress
- Handle errors gracefully: Check the
success
field in responses and handle errors appropriately - Clean up resources: Cancel scans that are no longer needed using
cancel_scan
- Validate parameters: Ensure all parameters are valid before calling MCP tools
- Check installation: Use
check_installation
to verify ASH is properly installed - Configure appropriately: Adjust configuration options based on your system resources and usage patterns
- Monitor resource usage: Keep an eye on system resources during scans
- Implement timeouts: Set appropriate timeouts for scans and operations
- Handle concurrent scans: Limit the number of concurrent scans based on your system resources
Examples
Basic Scan Workflow
# Start a scan
result = await mcp_scan_directory(
directory_path="/path/to/your/code",
severity_threshold="MEDIUM"
)
# Get the scan ID
scan_id = result["scan_id"]
# Check scan progress periodically
while True:
progress = await mcp_get_scan_progress(scan_id=scan_id)
if progress["status"] in ["completed", "failed", "cancelled"]:
break
# Wait before checking again
await asyncio.sleep(5)
# Get scan results
if progress["status"] == "completed":
results = await mcp_get_scan_results(scan_id=scan_id)
# Process results
else:
# Handle failure or cancellation
print(f"Scan {scan_id} {progress['status']}")
Handling Multiple Scans
# Start multiple scans
scan_ids = []
for directory in directories:
result = await mcp_scan_directory(
directory_path=directory,
severity_threshold="MEDIUM"
)
scan_ids.append(result["scan_id"])
# Check progress of all scans
completed_scans = set()
while len(completed_scans) < len(scan_ids):
for scan_id in scan_ids:
if scan_id in completed_scans:
continue
progress = await mcp_get_scan_progress(scan_id=scan_id)
if progress["status"] in ["completed", "failed", "cancelled"]:
completed_scans.add(scan_id)
# Wait before checking again
await asyncio.sleep(5)
# Get results for all completed scans
for scan_id in scan_ids:
progress = await mcp_get_scan_progress(scan_id=scan_id)
if progress["status"] == "completed":
results = await mcp_get_scan_results(scan_id=scan_id)
# Process results
else:
# Handle failure or cancellation
print(f"Scan {scan_id} {progress['status']}")
Error Handling Example
# Start a scan with error handling
try:
result = await mcp_scan_directory(
directory_path="/path/to/your/code",
severity_threshold="MEDIUM"
)
if not result["success"]:
# Handle error
print(f"Error: {result['error']}")
print(f"Suggestions: {result['suggestions']}")
return
scan_id = result["scan_id"]
# Check scan progress
while True:
progress = await mcp_get_scan_progress(scan_id=scan_id)
if not progress["success"]:
# Handle error
print(f"Error: {progress['error']}")
print(f"Suggestions: {progress['suggestions']}")
return
if progress["status"] in ["completed", "failed", "cancelled"]:
break
# Wait before checking again
await asyncio.sleep(5)
# Get scan results
if progress["status"] == "completed":
results = await mcp_get_scan_results(scan_id=scan_id)
if not results["success"]:
# Handle error
print(f"Error: {results['error']}")
print(f"Suggestions: {results['suggestions']}")
return
# Process results
else:
# Handle failure or cancellation
print(f"Scan {scan_id} {progress['status']}")
except Exception as e:
# Handle unexpected errors
print(f"Unexpected error: {str(e)}")