#
tokens: 19311/50000 17/17 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .dockerignore
├── .gitignore
├── .vscode
│   └── mcp.json
├── banner.sh
├── demos
│   ├── holehe.gif
│   ├── nmap.gif
│   ├── ocr2text.png
│   ├── README.md
│   ├── sherlock.gif
│   └── sqlmap.gif
├── Dockerfile
├── docs
│   └── index.html
├── LICENSE
├── README.md
├── requirements.txt
├── server.py
└── toolkit
    ├── dnsrecon.py
    ├── holehe.py
    ├── nmap.py
    ├── ocr2text.py
    ├── sherlock.py
    ├── sqlmap.py
    ├── sublist3r.py
    ├── wpscan.py
    └── zmap.py
```

# Files

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
env/
ENV/
.env
.venv
.eggs/
*.egg-info/

# Development
.git
.github
.vscode
.idea

# Build artifacts
dist/
build/
*.log 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Python virtual environment
venv/
.venv/
env/
ENV/

# Python bytecode
__pycache__/
*.py[cod]
*$py.class

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local


# OS specific files
.DS_Store
Thumbs.db 
```

--------------------------------------------------------------------------------
/demos/README.md:
--------------------------------------------------------------------------------

```markdown
### Ocr2Text
![Ocr2Text](ocr2text.png)
```

--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------

```
mcp==1.6.0
numpy==1.26.4
opencv-python-headless==4.9.0.80
openai==1.70.0
openai-agents==0.0.7
pdf2image==1.17.0
pytesseract
python-dotenv==1.1.0
requests==2.32.4
tqdm==4.67.1
typing-inspection==0.4.0
typing_extensions==4.13.1
uvicorn==0.34.0
sublist3r
```

--------------------------------------------------------------------------------
/.vscode/mcp.json:
--------------------------------------------------------------------------------

```json
{
    "servers": {
        "HydraMCP": {
            "command": "docker",
            "args": [
                "run",
                "--rm",
                "-i",
                "--net=host",
                "--privileged",
                "--name",
                "hydramcp",
                "hydramcp"
            ]
        }
    }
}
```

--------------------------------------------------------------------------------
/banner.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

cat << "BANNER"
• Title: HydraΜCP — The Model Context Protocol (MCP) Pentesting Toolkit
• Version: 0.1.0
• License: MIT
• Description: A lightweight, extensible cybersecurity toolkit that connects AI assistants
               to security tools through the Model Context Protocol (MCP), enabling
               AI-assisted security research, scanning, and analysis.
• Community: @happyhackingspace | https://happyhacking.space  
• Author: Built with ❤️ by @atiilla  
BANNER

```

--------------------------------------------------------------------------------
/toolkit/holehe.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: Holehe
# - Description: Email account checker
# - Usage: Checks if an email address is registered on various websites
# - Parameters: email (required)

import subprocess
import logging
from typing import Dict, Any
# Logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("holehe-tool")


class Holehe:
    """
    Holehe wrapper class for checking email registrations.
    """

    def __init__(self):
        """Initialize Holehe wrapper."""
        logger.info("Holehe wrapper initialized.")

    def scan(self, email: str) -> Dict[str, Any]:
        """
        Run the holehe scan for a given email.
        
        Args:
            email (str): The email address to check.
        
        Returns:
            Dict[str, Any]: Result or error from the holehe command.
        """
        logger.info(f"Running Holehe scan for: {email}")
        try:
            result = subprocess.run(["holehe", email], capture_output=True, text=True, check=True)
            output = result.stdout.strip()
            return {
                "email": email,
                "success": True,
                "output": output
            }
        except subprocess.CalledProcessError as e:
            logger.error(f"Holehe scan failed: {e.stderr.strip()}")
            return {
                "email": email,
                "success": False,
                "error": e.stderr.strip()
            }


def ExecHolehe(email: str) -> Dict[str, Any]:
    """
    Convenience wrapper to run a holehe scan.
    
    Args:
        email (str): Email address to scan.
    
    Returns:
        Dict[str, Any]: Holehe scan results.
    """
    scanner = Holehe()
    return scanner.scan(email)

```

--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------

```python
from typing import List, Optional, Dict, Any

from mcp.server.fastmcp import FastMCP

from toolkit.holehe import ExecHolehe
from toolkit.nmap import ExecNmap
from toolkit.wpscan import ExecWpscan
from toolkit.zmap import ExecZmap
from toolkit.sqlmap import ExecSqlmap
from toolkit.ocr2text import ExecOcr2Text
from toolkit.sublist3r import ExecSublist3r
from toolkit.dnsrecon import ExecDNSRecon
from toolkit.sherlock import ExecSherlock

# Create server
mcp = FastMCP(name="HydraΜCP",
    version="0.1.0"
)


@mcp.tool()
def ZmapScanner(
    target: str,
    port: int,
    bandwidth: Optional[str] = "1M",
) -> Dict[str, Any]:
    """Wrapper for running ZMap network scanning."""
    return ExecZmap(target, port, bandwidth)


@mcp.tool()
def WPScanScanner(
    url: str,
) -> Dict[str, Any]:
    """Wrapper for running WPScan vulnerability scanning."""
    return ExecWpscan(url)


@mcp.tool()
def HoleheScanner(
    email: str,
) -> Dict[str, Any]:
    """Wrapper for running Holehe email registration checking."""
    return ExecHolehe(email)


@mcp.tool()
def NmapScanner(
    target: str,
    ports: Optional[str] = None,
) -> Dict[str, Any]:
    """Wrapper for running Nmap network scanning."""
    return ExecNmap(target, ports)


@mcp.tool()
def SqlmapScanner(
    url: str,
    data: Optional[str] = None,
) -> Dict[str, Any]:
    """Wrapper for running Sqlmap vulnerability scanning."""
    return ExecSqlmap(url, data)

@mcp.tool()
def OcrScanner(
    file_path: str,
) -> Dict[str, Any]:
    """Wrapper for running OCR (Optical Character Recognition) on images and PDFs.
    
    The file_path can be:
    - A local file path
    - A direct URL (http/https)
    - A URL prefixed with @ symbol
    """
    return ExecOcr2Text(file_path)

@mcp.tool()
def Sublist3rScanner(
    domain: str,
    output_dir: Optional[str] = "output",
) -> List[str]:
    """Wrapper for running Sublist3r subdomain enumeration."""
    return ExecSublist3r(domain, output_dir)

@mcp.tool()
def DNSReconScanner(
    domain: str,
    scan_type: Optional[str] = "std",
    name_server: Optional[str] = None,
    range: Optional[str] = None,
    dictionary: Optional[str] = None,
) -> Dict[str, Any]:
    """Wrapper for running DNSRecon for DNS reconnaissance."""
    kwargs = {}
    if name_server:
        kwargs["name_server"] = name_server
    if range:
        kwargs["range"] = range
    if dictionary:
        kwargs["dictionary"] = dictionary
    
    return ExecDNSRecon(domain, scan_type, **kwargs)

@mcp.tool()
def SherlockScanner(
    usernames: List[str],
) -> Dict[str, Any]:
    """Wrapper for running Sherlock username enumeration."""
    return ExecSherlock(usernames)


if __name__ == "__main__":
    mcp.run(transport="stdio")
```

--------------------------------------------------------------------------------
/toolkit/nmap.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: Nmap
# - Description: Network scanning tool
# - Usage: Scans networks for hosts and services
# - Parameters: target (required), ports (optional)

import subprocess
import logging
import re
from typing import List, Optional, Tuple

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Define dynamic mappings of phrases to Nmap options
KEYWORD_MAP = {
    "no ping": "-Pn",
    "skip ping": "-Pn",
    "version": "-sV",
    "service detection": "-sV",
    "os detection": "-O",
    "aggressive": "-A",
    "verbose": "-v",
    "top ports": "--top-ports 100",
    "udp": "-sU",
    "quick": "-T4",
}

def parse_nmap_prompt(prompt: str) -> Tuple[str, Optional[str], List[str]]:
    prompt = prompt.lower()
    options = []
    ports = None

    # Extract IP address (supports v4 only here)
    ip_match = re.search(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", prompt)
    target = ip_match.group(0) if ip_match else ""

    # Extract ports if mentioned like 'ports 22,80' or 'scan port 443'
    port_match = re.search(r"port[s]?\s*(\d{1,5}(?:\s*,\s*\d{1,5})*)", prompt)
    if port_match:
        ports = port_match.group(1).replace(" ", "")

    # Match keywords to options
    for phrase, option in KEYWORD_MAP.items():
        if phrase in prompt:
            options.append(option)

    return target, ports, options

def ExecNmap(
    target: str,
    ports: Optional[str] = None,
    options: Optional[List[str]] = None,
) -> str:
    """Run an Nmap network scan on the specified target."""
    logger.debug("Running Nmap with target=%s, ports=%s, options=%s", target, ports, options)

    if subprocess.run(["which", "nmap"], capture_output=True).returncode != 0:
        return {"error": "Nmap is not installed. Install it with 'sudo apt install nmap'."}

    cmd = ["nmap", target]
    if ports:
        cmd += ["-p", ports]
    if options:
        cmd += options

    logger.info("Executing command: %s", ' '.join(cmd))

    try:
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        return {"success": True, "output": result.stdout}
    except subprocess.CalledProcessError as e:
        logger.error("Nmap command failed: %s", e.stderr)
        return {"success": False, "error": e.stderr.strip()}
    except Exception as e:
        logger.exception("Unexpected error during Nmap execution")
        return {"success": False, "error": f"Error executing Nmap scan: {str(e)}"}

def scan_from_prompt(prompt: str) -> str:
    target, ports, options = parse_nmap_prompt(prompt)
    if not target:
        return {"success": False, "error": "Could not detect a valid IP address in the prompt."}
    return ExecNmap(target, ports, options)


```

--------------------------------------------------------------------------------
/toolkit/wpscan.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: WPScan
# - Description: WordPress vulnerability scanner
# - Usage: Scans WordPress sites for vulnerabilities and security issues
# - Parameters: url (required), format (optional), api_token (optional)

import subprocess
import logging
from typing import Any, Dict, List, Optional
import json

# Logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("wpscan-tool")

class WPScan:
    """WPScan tool wrapper class."""

    def __init__(self):
        """Initialize WPScan wrapper."""
        self._check_installed()

    def _check_installed(self) -> None:
        """Verify WPScan is installed."""
        try:
            subprocess.run(["wpscan", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            logger.info("WPScan installation verified.")
        except (subprocess.SubprocessError, FileNotFoundError):
            logger.warning("wpscan command not found. Make sure WPScan is installed and in your PATH.")
            raise RuntimeError("WPScan is not installed or not in PATH.")

    def get_version(self) -> Optional[str]:
        """Get installed WPScan version."""
        try:
            result = subprocess.run(["wpscan", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            version_line = result.stdout.strip().split('\n')[-1]
            logger.info(f"WPScan version: {version_line}")
            return version_line
        except (subprocess.SubprocessError, FileNotFoundError) as e:
            logger.error(f"WPScan version check failed: {e}")
            return None

    def scan(self, url: str, **kwargs: Any) -> Dict[str, Any]:
        """Run the WPScan scan."""
        if not url:
            raise ValueError("Target URL must be provided.")

        cmd = ["wpscan", "--url", url,"--random-user-agent","--ignore-main-redirect"]

        # Append additional options to the command
        if "format" in kwargs:
            cmd.append("--format")
            cmd.append(kwargs["format"])
        if kwargs.get("verbose", False):
            cmd.append("--verbose")
        if kwargs.get("random_user_agent", False):
            cmd.append("--random-user-agent")
        if "max_threads" in kwargs:
            cmd.append("--max-threads")
            cmd.append(str(kwargs["max_threads"]))
        if "api_token" in kwargs:
            cmd.append("--api-token")
            cmd.append(kwargs["api_token"])
        if "enumerate_opts" in kwargs:
            cmd.append("--enumerate")
            cmd.append(",".join(kwargs["enumerate_opts"]))
        
        logger.info(f"Preparing WPScan command: {' '.join(cmd)}")

        # Run the scan
        try:
            result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            output = result.stdout
            # If format is json, parse it
            if kwargs.get("format", "json") == "json":
                return json.loads(output)
            return {"status": "success", "output": output}
        except subprocess.SubprocessError as e:
            logger.error(f"Error during WPScan scan: {e}")
            return {"status": "error", "message": str(e)}

# Example usage (optional convenience function)
def ExecWpscan(url: str, **kwargs: Any) -> Dict[str, Any]:
    """Convenience wrapper to run a WPScan scan."""
    scanner = WPScan()
    try:
        return scanner.scan(url, **kwargs)
    except Exception as e:
        logger.error(f"WPScan scan failed: {e}")
        return {"error": str(e)}

```

--------------------------------------------------------------------------------
/toolkit/zmap.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: ZMap
# - Description: High-speed network scanner
# - Usage: Scans large network ranges quickly
# - Parameters: target (required), port (required), bandwidth (optional)

from typing import Any, Dict, List
import ipaddress
import logging
import subprocess
import re
import random

# Logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("zmap-tool")


class ZMap:
    """ZMap scanner class for network scanning operations."""

    def __init__(self):
        """Initialize ZMap scanner."""
        self.interface = "eth0"
        self.gateway_mac = self._generate_random_mac()
        self._check_installed()

    def _check_installed(self) -> None:
        """Verify ZMap is installed."""
        try:
            subprocess.run(["zmap", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        except (subprocess.SubprocessError, FileNotFoundError):
            logger.warning("ZMap is not installed or not in PATH.")

    def _generate_random_mac(self) -> str:
        """Generate a random locally administered unicast MAC address."""
        mac = [0x02, 0x00, 0x00,
               random.randint(0x00, 0x7F),
               random.randint(0x00, 0xFF),
               random.randint(0x00, 0xFF)]
        return ':'.join(f'{b:02x}' for b in mac)

    def _convert_ip_range_to_cidr(self, ip_range: str) -> str:
        """Convert IP range or CIDR to canonical CIDR format."""
        try:
            network = ipaddress.IPv4Network(ip_range, strict=False)
            return str(network)
        except ValueError:
            pass

        match = re.match(r'(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)', ip_range)
        if not match:
            raise ValueError(f"Invalid IP range format: {ip_range}")

        start_ip, end_ip = match.group(1), match.group(2)
        start, end = ipaddress.IPv4Address(start_ip), ipaddress.IPv4Address(end_ip)

        for prefixlen in range(32, -1, -1):
            network = ipaddress.IPv4Network(f"{start_ip}/{prefixlen}", strict=False)
            if network[0] <= start and network[-1] >= end:
                return str(network)

        raise ValueError(f"Could not convert IP range {ip_range} to CIDR notation")

    def get_version(self) -> str:
        """Get installed ZMap version."""
        try:
            result = subprocess.run(["zmap", "--version"], check=True, stdout=subprocess.PIPE, text=True)
            return result.stdout.strip()
        except subprocess.SubprocessError as e:
            logger.error(f"ZMap version check failed: {e}")
            raise RuntimeError("ZMap is not installed or misconfigured.")

    def scan(self, target_port: int, subnets: List[str], bandwidth: str = "1M") -> Dict[str, Any]:
        """Run the ZMap scan."""
        cidr_subnets = [self._convert_ip_range_to_cidr(subnet) for subnet in subnets]

        if not (0 < target_port <= 65535):
            raise ValueError("Port must be between 1 and 65535")

        cmd = [
            "zmap",
            "-p", str(target_port),
            "-B", bandwidth,
            "-i", self.interface,
            "-G", self.gateway_mac,
            "--blacklist-file=/dev/null",
            "-o", "-"
        ]
        cmd.extend(cidr_subnets)

        logger.info(f"Running ZMap scan: {' '.join(cmd)}")

        try:
            result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            hosts = [line.strip() for line in result.stdout.splitlines() if line.strip()]
            return {
                "port": target_port,
                "hosts": hosts,
                "total_hosts": len(hosts),
                "subnets_scanned": cidr_subnets
            }
        except subprocess.CalledProcessError as e:
            logger.error(f"ZMap scan failed: {e.stderr.strip()}")
            return {
                "error": "Scan failed",
                "details": e.stderr,
                "exit_code": e.returncode
            }


def ExecZmap(target: str, port: int, bandwidth: str = "1M") -> Dict[str, Any]:
    """Convenience wrapper to run a ZMap scan."""
    zmap = ZMap()
    try:
        return zmap.scan(target_port=port, subnets=[target], bandwidth=bandwidth)
    except Exception as e:
        logger.error(f"Scan error: {e}")
        return {"error": str(e)}

```

--------------------------------------------------------------------------------
/toolkit/sublist3r.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: Sublist3r
# - Description: Fast subdomain enumeration tool
# - Usage: Finds subdomains via search engines
# - Parameters: domain (required), output_dir (optional)

import subprocess
import os
import json
import logging
import re
from typing import List, Dict, Any

# Logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("sublist3r-tool")


class Sublist3r:
    """
    Sublist3r wrapper class for subdomain enumeration.
    """

    def __init__(self):
        """Initialize Sublist3r wrapper."""
        logger.info("Sublist3r wrapper initialized.")

    def extract_subdomains_from_output(self, output: str) -> List[str]:
        """
        Extract subdomains from the command output.
        
        Args:
            output (str): Command output text
            
        Returns:
            List[str]: List of extracted subdomains
        """
        # Remove ANSI color codes
        ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
        clean_output = ansi_escape.sub('', output)
        
        # Extract subdomains using regex
        # Look for lines that appear to be domains (contain at least one dot and no special chars)
        domain_pattern = re.compile(r'^(?:[\w-]+\.)+[\w-]+$', re.MULTILINE)
        return domain_pattern.findall(clean_output)

    def scan(self, domain: str, output_dir: str) -> Dict[str, Any]:
        """
        Run Sublist3r tool to enumerate subdomains of a given domain.

        Args:
            domain (str): The target domain to enumerate subdomains for.
            output_dir (str): The directory to save the output files.

        Returns:
            Dict[str, Any]: Results or error from the Sublist3r command.
        """
        # Create output directory if it doesn't exist
        os.makedirs(output_dir, exist_ok=True)
        
        output_file = os.path.join(output_dir, f"{domain}_subdomains.txt")
        
        logger.info(f"Running Sublist3r scan for domain: {domain}")
        try:
            # Run without output file first to capture stdout
            result = subprocess.run(
                ["sublist3r", "-d", domain],
                capture_output=True, 
                text=True,
                check=True
            )
            output = result.stdout.strip()
            
            # Try to extract subdomains from the output
            subdomains_from_output = self.extract_subdomains_from_output(output)
            
            # Also try to run with output file as fallback
            subprocess.run(
                ["sublist3r", "-d", domain, "-o", output_file],
                capture_output=True,
                text=True,
                check=True
            )
            
            # Parse the output file to get subdomains
            subdomains_from_file = []
            if os.path.exists(output_file):
                with open(output_file, 'r') as f:
                    subdomains_from_file = [line.strip() for line in f if line.strip()]
            
            # Combine subdomains from both sources
            subdomains = list(set(subdomains_from_output + subdomains_from_file))
            
            # Save results to JSON file
            json_output_file = os.path.join(output_dir, f"{domain}_subdomains.json")
            with open(json_output_file, 'w') as f:
                json.dump(subdomains, f)
                
            logger.info(f"Sublist3r found {len(subdomains)} subdomains for {domain}")
            logger.info(f"Sublist3r results saved to {json_output_file}")
            
            return {
                "domain": domain,
                "success": True,
                "subdomains": subdomains,
                "output": output
            }
            
        except subprocess.CalledProcessError as e:
            logger.error(f"Sublist3r scan failed: {e.stderr.strip()}")
            return {
                "domain": domain,
                "success": False,
                "error": e.stderr.strip()
            }


def ExecSublist3r(domain: str, output_dir: str = "results") -> Dict[str, Any]:
    """
    Convenience wrapper to run a Sublist3r scan.
    
    Args:
        domain (str): The target domain to enumerate subdomains for.
        output_dir (str): The directory to save the output files, defaults to "results".
    
    Returns:
        Dict[str, Any]: Sublist3r scan results.
    """
    scanner = Sublist3r()
    return scanner.scan(domain, output_dir)
```

--------------------------------------------------------------------------------
/toolkit/sherlock.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: Sherlock
# - Description: Username hunter for social networks
# - Usage: Finds usernames across multiple social networks
# - Parameters: usernames (required), various optional parameters

import subprocess
import logging
from typing import Dict, Any, List, Optional

# Logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("sherlock-tool")


class Sherlock:
    """
    Sherlock wrapper class for hunting usernames across social networks.
    """

    def __init__(self):
        """Initialize Sherlock wrapper."""
        logger.info("Sherlock wrapper initialized.")

    def hunt(self, 
             usernames: List[str], 
             output: Optional[str] = None,
             folderoutput: Optional[str] = None,
             verbose: bool = False,
             tor: bool = False,
             unique_tor: bool = False,
             csv: bool = False,
             xlsx: bool = False,
             sites: Optional[List[str]] = None,
             proxy: Optional[str] = None,
             json_file: Optional[str] = None,
             timeout: Optional[int] = None,
             print_all: bool = False,
             print_found: bool = False,
             no_color: bool = False,
             browse: bool = False,
             local: bool = False,
             nsfw: bool = False
             ) -> Dict[str, Any]:
        """
        Run the sherlock hunt for given usernames.
        
        Args:
            usernames (List[str]): List of usernames to check.
            output (Optional[str]): Output file for single username results.
            folderoutput (Optional[str]): Output folder for multiple username results.
            verbose (bool): Display extra debugging information.
            tor (bool): Make requests over Tor.
            unique_tor (bool): Make requests over Tor with new circuit per request.
            csv (bool): Create CSV output file.
            xlsx (bool): Create XLSX output file.
            sites (Optional[List[str]]): Specific sites to check.
            proxy (Optional[str]): Proxy URL to use.
            json_file (Optional[str]): JSON file for data input.
            timeout (Optional[int]): Request timeout in seconds.
            print_all (bool): Output sites where username was not found.
            print_found (bool): Output sites where username was found.
            no_color (bool): Disable colored terminal output.
            browse (bool): Open results in browser.
            local (bool): Force use of local data.json file.
            nsfw (bool): Include NSFW sites in checks.
            
        Returns:
            Dict[str, Any]: Result or error from the sherlock command.
        """
        logger.info(f"Running Sherlock hunt for: {', '.join(usernames)}")
        
        cmd = ["sherlock"]
        
        # Add all flags and their values
        if verbose:
            cmd.append("--verbose")
        if folderoutput:
            cmd.extend(["--folderoutput", folderoutput])
        if output:
            cmd.extend(["--output", output])
        if tor:
            cmd.append("--tor")
        if unique_tor:
            cmd.append("--unique-tor")
        if csv:
            cmd.append("--csv")
        if xlsx:
            cmd.append("--xlsx")
        if sites:
            for site in sites:
                cmd.extend(["--site", site])
        if proxy:
            cmd.extend(["--proxy", proxy])
        if json_file:
            cmd.extend(["--json", json_file])
        if timeout:
            cmd.extend(["--timeout", str(timeout)])
        if print_all:
            cmd.append("--print-all")
        if print_found:
            cmd.append("--print-found")
        if no_color:
            cmd.append("--no-color")
        if browse:
            cmd.append("--browse")
        if local:
            cmd.append("--local")
        if nsfw:
            cmd.append("--nsfw")
        
        # Add usernames
        cmd.extend(usernames)
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            output = result.stdout.strip()
            return {
                "usernames": usernames,
                "success": True,
                "output": output
            }
        except subprocess.CalledProcessError as e:
            logger.error(f"Sherlock hunt failed: {e.stderr.strip()}")
            return {
                "usernames": usernames,
                "success": False,
                "error": e.stderr.strip()
            }


def ExecSherlock(usernames: List[str], **kwargs) -> Dict[str, Any]:
    """
    Convenience wrapper to run a sherlock username hunt.
    
    Args:
        usernames (List[str]): Usernames to hunt.
        **kwargs: Additional parameters to pass to Sherlock.hunt().
    
    Returns:
        Dict[str, Any]: Sherlock hunt results.
    """
    scanner = Sherlock()
    return scanner.hunt(usernames, **kwargs) 
```

--------------------------------------------------------------------------------
/toolkit/ocr2text.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: OCR Scanner
# - Description: Optical Character Recognition tool
# - Usage: Extracts text from images and PDF files
# - Parameters: file_path (required) - can be a local file or URL prefixed with @

import cv2
import pytesseract
from pdf2image import convert_from_path
import os
import numpy as np
from typing import Dict, Any
import requests
import tempfile
from urllib.parse import urlparse

# Configure Tesseract path - use system path for Kali Linux
if os.path.exists('/usr/bin/tesseract'):
    pytesseract.pytesseract.tesseract_cmd = r'/usr/bin/tesseract'
elif os.path.exists('/usr/local/bin/tesseract'):
    pytesseract.pytesseract.tesseract_cmd = r'/usr/local/bin/tesseract'
elif os.path.exists('C:\\Program Files\\Tesseract-OCR\\tesseract.exe'):
    pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'

def download_image(url):
    """Download image from a URL and save to a temporary file"""
    try:
        response = requests.get(url, stream=True)
        response.raise_for_status()
        
        # Get the file extension from the URL
        parsed_url = urlparse(url)
        filename = os.path.basename(parsed_url.path)
        _, ext = os.path.splitext(filename)
        
        if not ext:
            # Default to .png if no extension found
            ext = '.png'
            
        # Create a temporary file with the correct extension
        temp_file = tempfile.NamedTemporaryFile(suffix=ext, delete=False)
        temp_filename = temp_file.name
        
        # Write the image data to the temporary file
        with open(temp_filename, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
                
        return temp_filename
    except Exception as e:
        print(f"Error downloading image: {str(e)}")
        return None

def process_image(img):
    """Process image for better OCR results"""
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray, img_bin = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    gray = cv2.bitwise_not(img_bin)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 1))
    dilation = cv2.dilate(gray, kernel, iterations=1)
    erosion = cv2.erode(dilation, kernel, iterations=1)

    text = pytesseract.image_to_string(erosion)
    return text

def extract_text_from_pdf(pdf_path):
    """Extract text from a PDF file using OCR"""
    try:
        # Use poppler_path if on Windows, but we're in Kali Linux so not needed
        images = convert_from_path(pdf_path)
        
        all_text = []
        for i, image in enumerate(images):
            opencv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
            text = process_image(opencv_image)
            all_text.append(f"=== Page {i+1} ===\n{text}\n")
        
        return "\n".join(all_text)
    except Exception as e:
        return f"Error processing PDF: {str(e)}"

def extract_text_from_image(image_path):
    """Extract text from an image file using OCR"""
    try:
        img = cv2.imread(image_path)
        if img is None:
            return f"Error: Could not read image file {image_path}"
        return process_image(img)
    except Exception as e:
        return f"Error processing image: {str(e)}"

def ExecOcr2Text(file_path: str) -> Dict[str, Any]:
    """Execute OCR on a file (image or PDF) or URL"""
    temp_file = None
    
    try:
        # Check if the file_path is a URL (starts with @ or http/https)
        if file_path.startswith('@'):
            url = file_path[1:]  # Remove the @ symbol
            print(f"Downloading image from URL: {url}")
            temp_file = download_image(url)
        elif file_path.startswith(('http://', 'https://')):
            url = file_path
            print(f"Downloading image from URL: {url}")
            temp_file = download_image(url)
            
        if temp_file is None and (file_path.startswith('@') or file_path.startswith(('http://', 'https://'))):
            return {
                "success": False,
                "error": f"Failed to download image from URL: {url}",
                "text": ""
            }
            
        # Use the temp file if a URL was provided
        if temp_file:
            file_path = temp_file
        
        # Check if the file exists
        if not os.path.exists(file_path):
            return {
                "success": False,
                "error": f"File not found: {file_path}",
                "text": ""
            }
        
        file_ext = os.path.splitext(file_path)[1].lower()
        
        if file_ext == '.pdf':
            text = extract_text_from_pdf(file_path)
        elif file_ext in ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif']:
            text = extract_text_from_image(file_path)
        else:
            return {
                "success": False,
                "error": f"Unsupported file format: {file_ext}",
                "text": ""
            }
                
        return {
            "success": True,
            "error": "",
            "text": text
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e),
            "text": ""
        }
    finally:
        # Clean up temporary file if it was created
        if temp_file and os.path.exists(temp_file):
            try:
                os.unlink(temp_file)
            except:
                pass

```

--------------------------------------------------------------------------------
/toolkit/dnsrecon.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: DNSRecon
# - Description: DNS reconnaissance tool
# - Usage: Performs various DNS-related scans and enumeration
# - Parameters: domain (required), scan_type (optional), name_server (optional), range (optional), dictionary (optional)

import subprocess
import logging
from typing import Any, Dict, List, Optional
import json

# Logging setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("dnsrecon-tool")

class DNSRecon:
    """DNSRecon tool wrapper class."""

    def __init__(self):
        """Initialize DNSRecon wrapper."""
        self._check_installed()

    def _check_installed(self) -> None:
        """Verify DNSRecon is installed."""
        try:
            subprocess.run(["dnsrecon", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            logger.info("DNSRecon installation verified.")
        except (subprocess.SubprocessError, FileNotFoundError):
            logger.warning("dnsrecon command not found. Make sure DNSRecon is installed and in your PATH.")
            raise RuntimeError("DNSRecon is not installed or not in PATH.")

    def get_version(self) -> Optional[str]:
        """Get installed DNSRecon version."""
        try:
            result = subprocess.run(["dnsrecon", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            version = result.stdout.strip() if result.stdout else result.stderr.strip()
            logger.info(f"DNSRecon version: {version}")
            return version
        except (subprocess.SubprocessError, FileNotFoundError) as e:
            logger.error(f"DNSRecon version check failed: {e}")
            return None

    def scan(self, domain: Optional[str] = None, scan_type: Optional[str] = "std", **kwargs: Any) -> Dict[str, Any]:
        """Run the DNSRecon scan."""
        cmd = ["dnsrecon"]
        
        # Handle required parameters based on scan type
        if scan_type == "rvl":
            if "range" not in kwargs:
                raise ValueError("IP range must be provided for reverse lookup scan type.")
            cmd.extend(["-t", scan_type, "-r", kwargs["range"]])
        elif scan_type not in ["tld", "zonewalk"]:
            if not domain:
                raise ValueError("Target domain must be provided for this scan type.")
            cmd.extend(["-t", scan_type, "-d", domain])
        else:
            if not domain:
                raise ValueError("Target domain must be provided.")
            cmd.extend(["-t", scan_type, "-d", domain])
            
        # Handle optional parameters
        if "name_server" in kwargs:
            cmd.extend(["-n", kwargs["name_server"]])
        if "dictionary" in kwargs:
            cmd.extend(["-D", kwargs["dictionary"]])
        if kwargs.get("filter_wildcard", False):
            cmd.append("-f")
        if kwargs.get("axfr", False):
            cmd.append("-a")
        if kwargs.get("spf", False):
            cmd.append("-s")
        if kwargs.get("bing", False):
            cmd.append("-b")
        if kwargs.get("yandex", False):
            cmd.append("-y")
        if kwargs.get("crt", False):
            cmd.append("-k")
        if kwargs.get("whois", False):
            cmd.append("-w")
        if kwargs.get("dnssec", False):
            cmd.append("-z")
        if "threads" in kwargs:
            cmd.extend(["--threads", str(kwargs["threads"])])
        if "lifetime" in kwargs:
            cmd.extend(["--lifetime", str(kwargs["lifetime"])])
        if kwargs.get("tcp", False):
            cmd.append("--tcp")
        if "db" in kwargs:
            cmd.extend(["--db", kwargs["db"]])
        if "xml" in kwargs:
            cmd.extend(["-x", kwargs["xml"]])
        if "csv" in kwargs:
            cmd.extend(["-c", kwargs["csv"]])
        if "json" in kwargs:
            cmd.extend(["-j", kwargs["json"]])
        if kwargs.get("ignore_wildcard", False):
            cmd.append("--iw")
        if kwargs.get("disable_check_recursion", False):
            cmd.append("--disable_check_recursion")
        if kwargs.get("disable_check_bindversion", False):
            cmd.append("--disable_check_bindversion")
        if kwargs.get("verbose", False):
            cmd.append("-v")
            
        logger.info(f"Preparing DNSRecon command: {' '.join(cmd)}")

        # Run the scan
        try:
            result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            output = result.stdout
            
            # Parse results
            parse_result = self._parse_output(output)
            return {
                "status": "success",
                "command": " ".join(cmd),
                "raw_output": output,
                "results": parse_result
            }
        except subprocess.SubprocessError as e:
            logger.error(f"Error during DNSRecon scan: {e}")
            return {
                "status": "error",
                "message": str(e),
                "command": " ".join(cmd),
                "stderr": e.stderr if hasattr(e, 'stderr') else ""
            }
            
    def _parse_output(self, output: str) -> List[Dict[str, Any]]:
        """Parse the output from DNSRecon into structured data."""
        results = []
        lines = output.strip().split('\n')
        
        for line in lines:
            if '[*]' in line or '[-]' in line or '[+]' in line:
                # Skip the log/status lines
                continue
                
            parts = line.strip().split()
            if len(parts) >= 4:
                try:
                    record = {
                        "record_type": parts[0],
                        "name": parts[1],
                        "data": " ".join(parts[2:])
                    }
                    results.append(record)
                except Exception as e:
                    logger.error(f"Error parsing output line: {line}, error: {e}")
        
        return results

# Example usage (optional convenience function)
def ExecDNSRecon(domain: Optional[str] = None, scan_type: str = "std", **kwargs: Any) -> Dict[str, Any]:
    """Convenience wrapper to run a DNSRecon scan."""
    scanner = DNSRecon()
    try:
        return scanner.scan(domain, scan_type, **kwargs)
    except Exception as e:
        logger.error(f"DNSRecon scan failed: {e}")
        return {"error": str(e)}

```

--------------------------------------------------------------------------------
/toolkit/sqlmap.py:
--------------------------------------------------------------------------------

```python
# INFORMATION:
# - Tool: SQLMap
# - Description: SQL injection vulnerability scanner
# - Usage: Detects and exploits SQL injection vulnerabilities in web applications
# - Parameters: url (required), data (optional)

import subprocess
import json
import logging
import re
from typing import List, Optional, Dict, Any, Union

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Database Management System (DBMS) info variables
isDbmsFound = False
dbms = ""
dbmsVersion = ""
dbmsVersionFound = False

# Tamper scripts for various DBMS
tamperscripts = {
    "MySQL": [
        "union2urls", "randomcase", "space2comment", "between", "charencode"
    ],
    "PostgreSQL": [
        "randomcase", "space2comment", "postgreSQLbool"
    ],
    "Microsoft SQL Server": [
        "charencode", "space2comment", "union2urls", "mssql08"
    ],
    "Oracle": [
        "oracle2", "space2comment", "union2urls"
    ],
    "SQLite": [
        "randomcase", "space2comment", "union2urls", "sqliteunicode"
    ],
    "Generic": [
        "charencode", "space2comment", "union2urls"
    ]
}


def run_sqlmap(cmd: List[str]) -> subprocess.CompletedProcess:
    """
    Helper function to run the sqlmap command.
    
    Args:
        cmd: List of command-line arguments for sqlmap.
    
    Returns:
        subprocess.CompletedProcess: The result of running sqlmap.
    """
    logger.debug("Running sqlmap command: %s", " ".join(cmd))
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            check=True
        )
        return result
    except subprocess.CalledProcessError as e:
        logger.error("SQLMap command failed: %s", e.stderr)
        raise e


def check_sqlmap_installed() -> bool:
    """
    Check if sqlmap is installed on the system.
    
    Returns:
        bool: True if sqlmap is installed, False otherwise
    """
    try:
        subprocess.run(["which", "sqlmap"], capture_output=True, check=True)
        return True
    except subprocess.CalledProcessError:
        return False


def gather_information(url: str) -> bool:
    """
    Gather information about the target URL by running sqlmap with the --batch and -v 0 options.
    
    Args:
        url: Target URL to scan
    
    Returns:
        bool: True if the operation was successful, False otherwise
    """
    cmd = ["sqlmap", "-u", url, "--batch", "-v", "0"]
    
    try:
        result = run_sqlmap(cmd)
        return result.returncode == 0
    except subprocess.CalledProcessError:
        return False


def try_tamper(url: str, tamper: str) -> bool:
    """
    Try a specific tamper script on the target URL by running sqlmap with the --batch and -v 0 options.
    
    Args:
        url: Target URL to scan
        tamper: Tamper script to use
    
    Returns:
        bool: True if the operation was successful, False otherwise
    """
    cmd = ["sqlmap", "-u", url, "--batch", "-v", "0", "--tamper", tamper]
    
    try:
        result = run_sqlmap(cmd)
        return result.returncode == 0
    except subprocess.CalledProcessError:
        return False


def try_with_risk_and_level(url: str, risk: int, level: int) -> bool:
    """
    Try a specific risk and level on the target URL by running sqlmap with the --batch and -v 0 options.
    
    Args:
        url: Target URL to scan
        risk: Risk level (1-3)
        level: Level (1-5)
    
    Returns:
        bool: True if the operation was successful, False otherwise
    """
    cmd = ["sqlmap", "-u", url, "--batch", "-v", "0", "--level", str(level), "--risk", str(risk)]
    
    try:
        result = run_sqlmap(cmd)
        return result.returncode == 0
    except subprocess.CalledProcessError:
        return False


def try_with_technique(url: str, technique: str) -> bool:
    """
    Try a specific technique on the target URL by running sqlmap with the --batch and -v 0 options.
    
    Args:
        url: Target URL to scan
        technique: Technique to use (e.g., "B", "E", "T", "U")
    
    Returns:
        bool: True if the operation was successful, False otherwise
    """
    cmd = ["sqlmap", "-u", url, "--batch", "-v", "0", "--technique", technique]
    
    try:
        result = run_sqlmap(cmd)
        return result.returncode == 0
    except subprocess.CalledProcessError:
        return False


def ExecSqlmap(url: str, data: Optional[str] = None) -> Dict[str, Any]:
    """
    Run sqlmap with the given URL and data.
    
    Args:
        url: Target URL to scan
        data: POST data to include in the request
    
    Returns:
        Dict[str, Any]: Result or error from the sqlmap command
    """
    options = []
    if data:
        options.extend(["--data", data])
    
    cmd = ["sqlmap", "-u", url, "--batch", "-v", "0", "--output-dir=/tmp/sqlmap"]
    cmd.extend(options)
    
    try:
        result = run_sqlmap(cmd)
        parsed_output = parse_sqlmap_output(result.stdout)
        return {
            "success": True,
            "url": url,
            "results": parsed_output
        }
    except subprocess.CalledProcessError as e:
        return {
            "success": False,
            "error": str(e),
            "stderr": e.stderr
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


def categorize_tamperscript(db_tech: str) -> str:
    """
    Categorizes tamper scripts by database technology.
    
    Args:
        db_tech: The name of the database technology (e.g., MySQL, PostgreSQL, etc.)
    
    Returns:
        str: JSON string containing the tamper scripts for the provided DB technology.
    """
    try:
        if db_tech in tamperscripts:
            return json.dumps({
                "success": True,
                "database": db_tech,
                "tamper_scripts": tamperscripts[db_tech]
            })
        else:
            return json.dumps({
                "success": False,
                "error": f"Tamper scripts for {db_tech} not found."
            })
    except Exception as e:
        return json.dumps({
            "success": False,
            "error": str(e)
        })


def parse_sqlmap_output(output: str) -> Dict[str, Any]:
    """
    Parse the output from sqlmap to extract useful information.
    
    Args:
        output: The stdout from sqlmap
    
    Returns:
        Dict: A dictionary containing parsed information
    """
    result = {
        "vulnerable": False,
        "dbms": None,
        "payloads": [],
        "tables": [],
        "raw_output": output
    }
    
    # Check if any vulnerability was found
    if "is vulnerable to" in output:
        result["vulnerable"] = True
    
    # Try to extract DBMS information
    dbms_match = re.search(r"back-end DBMS: (.+?)(?:\n|\[)", output)
    if dbms_match:
        result["dbms"] = dbms_match.group(1).strip()
    
    # Try to extract payload information
    payload_matches = re.findall(r"Payload: (.+?)(?:\n|$)", output)
    result["payloads"] = [p.strip() for p in payload_matches]
    
    # Extract tables if available
    tables_section = re.search(r"Database: .*?\nTable: (.*?)(?:\n\n|\Z)", output, re.DOTALL)
    if tables_section:
        tables_text = tables_section.group(1)
        tables = re.findall(r"\|\s+([^\|]+?)\s+\|", tables_text)
        result["tables"] = [t.strip() for t in tables]
    
    return result


def ExecSqlmap(url: str, data: Optional[str] = None) -> Dict[str, Any]:
    """
    Run sqlmap vulnerability scan on a specified target URL.
    Main entry point for the MCP server to call this tool.
    
    Args:
        url: Target URL to scan
        data: Optional POST data to include in the request
    
    Returns:
        Dict: JSON-serializable dictionary with scan results
    """
    logger.info(f"Starting SQLMap scan on {url}")
    
    # Check if sqlmap is installed
    if not check_sqlmap_installed():
        return {
            "success": False,
            "error": "SQLMap is not installed. Install it with 'pip install sqlmap' or 'apt-get install sqlmap'."
        }
    
    # Define base command
    cmd = ["sqlmap", "-u", url, "--batch", "--forms", "--json-output"]
    
    # Add data parameter if provided
    if data:
        cmd.extend(["--data", data])
    
    # Run initial scan
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        
        # Check if JSON output is available
        try:
            # Try to parse JSON output
            json_output_path = re.search(r"JSON report saved to: (.+)", result.stdout)
            if json_output_path:
                with open(json_output_path.group(1), 'r') as json_file:
                    json_data = json.load(json_file)
                    return {
                        "success": True,
                        "url": url,
                        "vulnerable": bool(json_data.get("vulnerabilities", [])),
                        "data": json_data
                    }
        except (json.JSONDecodeError, FileNotFoundError):
            pass
        
        # If JSON parsing failed, parse the output manually
        parsed_results = parse_sqlmap_output(result.stdout)
        return {
            "success": True,
            "url": url,
            "vulnerable": parsed_results["vulnerable"],
            "dbms": parsed_results["dbms"],
            "payloads": parsed_results["payloads"],
            "tables": parsed_results["tables"],
            "output": result.stdout
        }
    
    except subprocess.CalledProcessError as e:
        logger.error(f"SQLMap scan failed: {e.stderr}")
        return {
            "success": False,
            "error": "SQLMap scan failed",
            "details": e.stderr
        }
    except Exception as e:
        logger.exception("Unexpected error during SQLMap execution")
        return {
            "success": False,
            "error": f"Error executing SQLMap scan: {str(e)}"
        }



```

--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------

```html
<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
   <title>MCPHydra</title>
   <style>
      body {
         background-color: #000000;
         margin: 0;

         display: flex;
         justify-content: center;
         align-items: center;
         min-height: 100vh;
      }

      #flag-container {
         position: relative;
         overflow: hidden;
         max-width: 100%;
         height: 100vh;
      }


      #tiresult {
         font-size: 16px;
         background-color: #000000;
         font-weight: bold;
         padding: 3px 5px;
         margin: 0;
         display: inline-block;
         white-space: pre;
         width: 100%;
         overflow-x: auto;
      }

      #mcphydra {
         background-color: #000000;
         font-weight: bold;
         padding: 3px 5px;
         margin: 0;
         display: inline-block;
         font-size: 18px;
         white-space: pre;
         width: 100%;
         overflow-x: auto;
         color: #07df07;
      }

      /* Responsive font sizes */
      @media screen and (max-width: 1024px) {

         #tiresult,
         #mcphydra {
            font-size: 20px;
         }

         #mcphydra {
            font-size: 10px;
         }
      }

      @media screen and (max-width: 768px) {

         #tiresult,
         #mcphydra {
            font-size: 20px;
         }

         #mcphydra {
            font-size: 10px;
         }
      }

      @media screen and (max-width: 480px) {

         #tiresult,
         #mcphydra {
            font-size: 14px;
         }

         #mcphydra {
            font-size: 10px;
         }
      }

      /* Animation for each character */
      #tiresult b {
         display: inline-block;
         animation: wave 1s infinite ease-in-out;
         transform-origin: center bottom;
         position: relative;
      }

      /* Base animation keyframes - will be overridden by JavaScript for responsive sizing */
      @keyframes wave {
         0% {
            transform: translateY(0) rotate(0deg);
         }

         20% {
            transform: translateY(-1px) rotate(1.5deg) translateX(0.8px);
         }

         40% {
            transform: translateY(-0.5px) rotate(0.5deg) translateX(0.4px);
         }

         60% {
            transform: translateY(0) rotate(0deg);
         }

         80% {
            transform: translateY(0.8px) rotate(-1.5deg) translateX(-0.8px);
         }

         100% {
            transform: translateY(0) rotate(0deg);
         }
      }
   </style>
</head>

<body>
   <div id="flag-container">
      <pre id="mcphydra">
                         __              __          
   ____ ___  _________  / /_  __  ______/ /________ _
  / __ `__ \/ ___/ __ \/ __ \/ / / / __  / ___/ __ `/
 / / / / / / /__/ /_/ / / / / /_/ / /_/ / /  / /_/ / 
/_/ /_/ /_/\___/ .___/_/ /_/\__, /\__,_/_/   \__,_/  
              /_/          /____/       v0.1.0             
</pre>
      <pre id="tiresult">
<b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⢿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⠟</b><b style="color:#07df07">⠙</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠋</b><b style="color:#07df07">⠙</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⠷</b><b style="color:#07df07">⠄</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢸</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⢿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠋</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢠</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⡿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣀</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣴</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣇</b><b style="color:#07df07">⡀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⣠</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⢿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣷</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠟</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣠</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⡿</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠟</b><b style="color:#07df07">⠙</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠟</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⡆</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⠏</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠁</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠁</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⣠</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣦</b><b style="color:#07df07">⡄</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⣠</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣦</b><b style="color:#07df07">⡀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣆</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">mcphydra</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠘</b><b style="color:#07df07">⠛</b><b style="color:#07df07">⠛</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣦</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⣻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⢻</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⡿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⢿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣠</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣄</b><b style="color:#07df07">⣀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠛</b><b style="color:#07df07">⠹</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⠷</b><b style="color:#07df07">⣄</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⠉</b><b style="color:#07df07">⣹</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣾</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣷</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣴</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⢀</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣷</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣆</b><b style="color:#07df07">⡀</b><b style="color:#07df07">⠀</b><b style="color:#07df07">⠈</b><b style="color:#07df07">⠻</b><b style="color:#07df07">⠿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
<b style="color:#07df07">⣿</b><b style="color:#07df07">⣤</b><b style="color:#07df07">⣼</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣶</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b><b style="color:#07df07">⣿</b>
      </pre>
      <script>
         document.addEventListener('DOMContentLoaded', function () {
            const chars = document.querySelectorAll('#tiresult b');

            // Handle responsive animation scaling
            function adjustAnimation() {
               const viewportWidth = window.innerWidth;
               let scaleFactor = 1;

               if (viewportWidth <= 480) {
                  scaleFactor = 0.5;
               } else if (viewportWidth <= 768) {
                  scaleFactor = 0.7;
               } else if (viewportWidth <= 1024) {
                  scaleFactor = 0.85;
               }

               function getRandomColor() {
                  return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
               }

               let randomColors = Array.from({ length: 10 }, getRandomColor);
               setInterval(() => {
                  randomColors = Array.from({ length: 10 }, getRandomColor);
                   chars.forEach((char, index) => {
                  const colorIndex = Math.floor(Math.random() * randomColors.length);
                  char.style.color = randomColors[colorIndex];
               });
               }, 2000);
              
               console.log(randomColors);
               
               chars.forEach((char, index) => {
                  const row = Math.floor(index / 130);
                  const col = index % 130;
                  const delay = (col * 0.015 + row * 0.06) % 1.5;

                  char.style.animationDelay = delay + 's';
                  const durationVariation = 0.9 + Math.random() * 0.2; // 0.9-1.1
                  char.style.animationDuration = (1.5 * durationVariation) + 's';

                  // Scale transform values based on viewport
                  const style = document.createElement('style');
                  style.innerHTML = `
                     @keyframes wave {
                        0% { transform: translateY(0) rotate(0deg); }
                        20% { transform: translateY(${-1 * scaleFactor}px) rotate(${1.5 * scaleFactor}deg) translateX(${0.8 * scaleFactor}px); }
                        40% { transform: translateY(${-0.5 * scaleFactor}px) rotate(${0.5 * scaleFactor}deg) translateX(${0.4 * scaleFactor}px); }
                        60% { transform: translateY(0) rotate(0deg); }
                        80% { transform: translateY(${0.8 * scaleFactor}px) rotate(${-1.5 * scaleFactor}deg) translateX(${-0.8 * scaleFactor}px); }
                        100% { transform: translateY(0) rotate(0deg); }
                     }
                     
                  `;
                  document.head.appendChild(style);
               });
            }

            // Initial setup
            adjustAnimation();

            // Re-adjust on window resize
            window.addEventListener('resize', adjustAnimation);
         });
      </script>
</body>

</html>
```