Web应用安全测试MCP服务器

ShyHurricane 是一个用于辅助AI进行Web应用攻击性安全测试的MCP服务器,通过索引资源、构建应用关系图、反混淆JavaScript和执行SAST,提供攻击面测绘、漏洞搜索与验证等自动化工作流。

  • by double16
  • Python
  • 混合部署
  • 2 星标
  • 更新于
在 GitHub 查看 double16/shyhurricane

使用说明

shyhurricane

ShyHurricane is an MCP server to assist AI in offensive security testing of web applications.

Web application resources are stored in a vector database. Raw content may be retrieved by filters or embeddings. Relationships are mapped in a graph database for flow analysis. JavaScript is deobfuscated, reversed from source maps, graphed and SAST is run (opengrep). There is a simple finding database where SAST results are kept, and results from other tools such as Burp Suite and ZAP. LLMs are provided with several querying tools to help reason about the security of the application semantically.

Resources are ingested using provided spider and busting tools, Burp Suite, ZAP, Chrome or Firefox extensions. REST /index and /finding endpoints are available for ingesting data too. Formats supported are katana jsonl, hal json and Burp Suite Logger++ CSV. The ingest.py script converts Burp Suite request/response XML exports for indexing.

Documentation

See the documentation index for detailed guides and architecture, and CONTRIBUTING.md for development setup.

Assessment prompts

MCP clients can discover these reusable workflows with prompts/list and render them with prompts/get. Rendering produces assessment instructions without running tools or contacting targets.

Prompt Purpose
map_attack_surface Inventory captured endpoints, forms, scripts, services, and assessment opportunities
hunt_vulnerability Investigate the supplied vulnerability_type using captured content and graph evidence
hunt_xss Review reflected, stored, and DOM XSS candidates and source-to-sink paths
hunt_access_control Investigate IDOR/BOLA, privileged operations, and authorization boundaries
hunt_injection Review SQL, NoSQL, command, and template injection indicators
hunt_ssrf Investigate URL-consuming features and possible server-side request boundaries
review_authentication_sessions Review login, recovery, OAuth, JWTs, cookies, and session lifecycle
review_configuration_exposure Review secrets, sensitive resources, debug interfaces, CORS, and headers
trace_input_to_sink Trace graph sources and sinks and inspect supporting evidence
find_vulnerability_chains Correlate findings, graph flows, and possible journeys into evidence-supported chains
Argument Required Meaning
target Yes, all prompts Host, domain, IP, netloc such as app.example.com:8443, or HTTP(S) URL prefix
mode No analysis (default) uses captured evidence; active also guides scoped live reconnaissance and validation
vulnerability_type Yes, hunt_vulnerability only Free-text vulnerability class or classes to investigate

Use bracketed IPv6 targets, such as [2001:db8::1]:8443. Hostname scopes include subdomains; explicit ports are retained, and URL prefixes restrict scheme, host, and path. User-supplied narrower scope still applies. Query strings, fragments, credentials in target URLs, and malformed targets are rejected.

For example, send prompts/get with these parameters:

{"name":"hunt_vulnerability","arguments":{"target":"app.example.com:8443","vulnerability_type":"IDOR"}}

For graph-assisted chain analysis with scoped live testing guidance:

{"name":"find_vulnerability_chains","arguments":{"target":"example.com","mode":"active"}}

Workflows begin with existing findings and indexed evidence. Analysis mode declines scan confirmations and makes no target requests. Active mode respects existing confirmations and authorization, uses available open-world tools, and describes manual checks where those tools cannot perform validation. Prompts remain available with OPEN_WORLD=false; active workflows then continue indexed analysis and report the limitation. Low-power workflows use predicate-only retrieval and captured content.

All assessment prompts use an endpoint-first graph workflow: overview, focused candidates, traces, then step evidence. They request full graph details only when needed, distinguish bounded examples from complete results, and keep navigation hypotheses separate from data-flow evidence.

Prompt workflows name the tool arguments explicitly: continuation results map to token, deferred analysis_unit_id values map to unit_id, and saved-page tokens map to page_token. Graph discovery and traversal calls retain the assessment scope, including when using opaque IDs. score_threshold applies only to finding searches; resource searches use retrieval_limit.

Graph paths are investigation candidates, not proof of exploitation. Reports distinguish verified steps, inferred links, missing prerequisites, and analysis gaps. Workflows direct the client to save new evidence-backed findings with verification status, avoid duplicates, and keep unsupported hypotheses in the report. Rendering a prompt alone never saves findings.

Tools

The following tools are provided:

Tool Description Open World?
port_scan Performs a port scan and service identification on the target(s), similar to the functions of nmap. Yes
spider_website Spider the website at the url and index the results for further analysis Yes
directory_buster Search a website for hidden directories and files. Yes
index_http_url Index an HTTP URL to allow for further analysis. (aka curl) Yes
find_wordlists Find available word lists for spidering and directory_buster No
find_web_resources Query indexed resources about a website using natural language . No
fetch_web_resource_content Fetch the content of a web resource that has already been indexed. No
find_domains Query indexed resources for a list of domains. No
find_hosts Query indexed resources for a list of hosts for the given domain. No
find_netloc Query indexed resources for a list of network locations, i.e. host:port, for a given domain. No
find_urls Query indexed resources for a list of URLs for the given host or domain. No
register_hostname_address Registers a hostname with an IP address. No
register_http_headers Register HTTP headers that should be sent on every request. No
save_finding Save findings as a markdown. No
query_findings Query for previous findings for a target. No
deobfuscate_javascript De-obfuscate JavaScript content (automatically done during indexing) No
get_result_page Retrieve a saved page using an opaque navigation token. No

Scan rate limits

spider_website and directory_buster accept rate_limit_requests_per_second as a non-negative integer. A positive value limits requests/second for that scan; 0 disables the explicit limit. For spidering, 0 uses a high ceiling of 3000 requests/second because Katana 1.5.0 crashes with a literal zero. For directory busting, 0 means unlimited. If omitted or null, public targets default to 5 requests/second and non-public targets retain scanner defaults, including Katana's built-in default with no rate flag supplied. Concurrent scans have separate budgets. Public-target detection uses the URL hostname or IP, without DNS lookups or registered address overrides.

For example, use spider_website(url="https://example.com/", rate_limit_requests_per_second=2) to crawl at 2 requests/second, or supply rate_limit_requests_per_second=0 to use the high ceiling. See the tool reference for backend behavior.

Search and results

Use natural-language queries with find_web_resources, or narrow captured content with filters such as site:example.com filetype:js -inurl:vendor. Predicate-only queries also work in low-power mode.

query_findings accepts scoped predicates too: site:example.com inurl:admin -ext:js. Start with site:example.com to include host-level findings before narrowing by URL. Plain root URLs search hostname scope, retaining explicit ports; explicit site: URLs retain scheme and path restrictions. Finding searches support site:, inurl:, and filetype:/ext:; the required query combines scope predicates and optional natural-language search text, like find_web_resources. For example, use query_findings(query="site:example.com session fixation") for semantic matches. The former target argument has been removed; update clients to use query. Semantic search ranks existing embeddings and saves at most 1,000 matches, with an optional minimum cosine score_threshold. limit remains the page size. Results that reach the cap instruct the caller to narrow the search. Without semantic text (for example, query="site:example.com"), score_threshold is ignored and all scoped findings remain available, including in low-power mode. Semantic search requires normal power mode. Searches report progress and idle updates; semantic scores indicate similarity, not confidence. Follow saved pages and check pagination.retrieval_truncated: semantic results are bounded, so page exhaustion does not mean every finding was reviewed. The finding collection must be rebuilt and findings replayed through the updated saver for this metadata version; existing records are not migrated. See the tool reference for details.

HTTP /findings and MCP save_finding submissions are committed to the persistent scan_finding_queue before success is returned. Pending submissions survive restarts; processing failures remain available for retry using the dashboard. Searchability follows asynchronously. The /findings endpoint accepts application/json, text/json, and application/x-www-form-urlencoded, including parameters such as charset=utf-8.

Finding submissions are deduplicated asynchronously before saving, across MCP, HTTP, and JavaScript scanner inputs. Repeated Markdown for the same target keeps the first saved finding, including its title and timestamp. Titles do not affect matching. Line endings, surrounding Markdown whitespace, hostname case, and HTTP(S) default ports are normalized; URL paths, queries, fragments, and changed evidence remain distinct. Host-level findings remain distinct from URL findings. This applies to newly processed submissions; older records are not backfilled or cleaned up.

List tools return saved pages. Follow pagination.next_page_token with get_result_page to continue without repeating a scan or query. Keep page tokens private. Retrieval waits briefly for relevant indexing and reports when results may still be incomplete; avoid starting duplicate scans.

See the tool reference for search predicates, result limits, pagination, content retrieval, and indexing-status fields.

Application Relationship Graph

Explore captured resources, forms, fields, scripts, routes, and possible data flows using the read-only application graph tools. Start with find_application_endpoints, inspect focused elements and relationships, then trace data flow or possible user journeys and review the supporting evidence. Standalone and inline SVG analysis extracts links, resource dependencies, scripts, and event handlers. The tools work in low-power mode and do not contact targets. Graph production runs in finite, restartable epochs: captured inputs are frozen, structural analysis finishes, then inferred links are published without replacing structural entities. New captures wait for the next epoch; duplicate deliveries do not restart completed work. Inspect graph.epoch in /status for progress and failures. Reference discovery and current-job fields show work before publication; unchanged Neo4j counts during discovery do not indicate a stall. Upgrading unfinished legacy graph work requires a fresh data directory. All graph tools share a 120-second request budget after the separately configured indexing wait. The final five seconds are reserved for continuation checkpoints and saved result pages. Interrupted queries preserve completed records and report analysis gaps and time_limit. Continuations retain unfinished work; failed checkpoints require restarting and publish no unusable token. Graph tools continue sending client progress at the established five-second interval. Blank optional URL, context URL, and method filters behave like omitted filters. Coverage describes retained analysis units, independently of queued work; proven stale capture-unit statuses are reconciled automatically in bounded background maintenance. Graph discovery and traversal tools infer omitted, null, or blank scope from a supplied HTTP(S) URL's hostname and explicit port. Endpoints use url; elements use context_url before url; relationships use URL-valued element_id; flow traces and journeys use URL-valued start. Explicit scope takes precedence. Opaque IDs require explicit scope, and end never supplies scope. For example, find_application_endpoints(url="https://app.example.com:8443/api/items") uses app.example.com:8443 scope while retaining the exact URL filter. Discovery inventories and full IR are retained separately from bounded Neo4j summaries. Results report discovery/flow coverage and deferred analysis units. Use continue_application_graph_query for exploration continuations and expand_application_graph_analysis for offline detail expansion. Graph state uses immutable, compressed SQLite artifacts, normalized discovery indexes, and incremental storage accounting. Cleanup commits bounded batches and resumes unfinished work. Recomputable analysis caches have a 256 MiB default budget; captured evidence and offline expansion artifacts are retained independently. This storage version requires a fresh assessment directory; existing layouts are rejected without migration. Preserve existing assessment directories. Graph paths are investigation candidates; inspect their evidence before treating them as confirmed vulnerabilities.

See the application graph guide for tool arguments and examples, analysis coverage, architecture, Neo4j configuration, and backfill/rebuild procedures.

Install

The MCP server itself uses an LLM for light tasks such that the llama3.2:3b model is sufficient. Ollama is recommended but not required. OpenAI, Google AI, AWS Bedrock, and LiteLLM models are supported. Docker is required for tool specific commands such as spidering and directory busting.

Docker Desktop or colima

Docker is required and the quality of the networking stack is important. Docker Desktop is acceptable. On macOS, Apple Virtualization networking has issues. Use colima with qemu virtualization for better results.

If you use Homebrew, brew bundle may be used for installation. Otherwise, use your operating system to install colima, qemu, docker, and docker-compose.

Start colima with a command such as the following:

colima start --runtime docker --cpu 4 --disk 50 -m 4 --vm-type qemu

nmap

It is best to run nmap on the host. If not installed on the host, the docker container will be used.

Docker Compose

As a Docker Service

Configure one desired provider and model in .env:

Provider Model variable Credential and connection variables
Ollama OLLAMA_MODEL=llama3.2:3b OLLAMA_HOST=192.168.253.100:11434
Google AI GEMINI_MODEL=gemini-2.5-pro GEMINI_API_KEY or GOOGLE_API_KEY
OpenAI OPENAI_MODEL=gpt-5-nano OPENAI_API_KEY
AWS Bedrock BEDROCK_MODEL=us.meta.llama3-2-3b-instruct-v1:0 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
LiteLLM LITELLM_MODEL=anthropic/claude-sonnet-4-6 LITELLM_API_KEY; optionally LITELLM_API_BASE for a LiteLLM proxy

For a direct LiteLLM provider connection, omit LITELLM_API_KEY and supply the selected provider's standard credential environment variable, such as ANTHROPIC_API_KEY. When using Docker Compose, use LITELLM_API_KEY and LITELLM_API_BASE to connect to a LiteLLM proxy.

Run the MCP server:

docker compose up -d

or to build the images from source:

docker compose -f docker-compose.dev.yml up -d

Add the MCP server to your client of choice at http://127.0.0.1:8000/mcp.

MCP transports and elicitation

The default MCP endpoint is /mcp; legacy SSE clients can use /sse with --transport sse. Set MCP_TRANSPORT or --transport to choose a transport. Clients supporting elicitation can ask for missing targets and scan confirmations when enabled.

See MCP transports and elicitation for settings, client compatibility, request-local state, progress notifications, and deployment limits.

Run From Source

Python Environment

$(command -v python3.14) -m venv .venv
source .venv/bin/activate
uv sync

Storage and logging

Use --data-dir or DATA_DIR to select application storage. Relative names, such as assessment, create separate assessment directories beneath the platform default. Docker Compose retains its named volumes. Existing data is not migrated automatically.

Use Qdrant 1.17 or newer and fresh collections for the tuned payload index settings. New indexes, including missing indexes repaired at startup, use the same tuning policy.

Diagnostic logs default to <data-dir>/logs/shyhurricane.log. Override the destination with --log-file or LOG_FILE. Logs rotate at 10 MiB and retain five numbered backups by default. Use --log-max-bytes/LOG_MAX_BYTES and --log-backup-count/LOG_BACKUP_COUNT to configure rotation; a byte limit of 0 disables it.

See storage, database tuning, and logging for platform defaults, environment variables, Qdrant requirements, and migration guidance.

Ollama

Install Ollama and the llama3.2:3b model:

Ubuntu:

apt-get install ollama
ollama pull llama3.2:3b

macOS:

brew install ollama
brew services start ollama
ollama pull llama3.2:3b

Command Container Image

docker build -t ghcr.io/double16/shyhurricane_unix_command:main src/docker/unix_command

MCP Server

When started from an interactive terminal, the server displays a shyhurricane monitoring dashboard with configuration, queue, database, indexing, and MCP tool information. It refreshes immediately on startup and every 30 seconds afterward. Press r to refresh immediately. Press q to stop the dashboard and shut down the server. Press f to mark failed items pending across the ingest, type-specific indexing, scan-finding, and graph queues. The notification reports counts and any per-queue errors; successful queues are committed even if another queue fails. Retrying preserves payloads and leaves processing items untouched. Workers pick up pending work asynchronously and may fail again if the original problem remains. Repeated key presses do not overlap retry operations.

The MCP tools panel lists running tools followed by the last five completed calls, newest first. Yellow dots indicate running tools, green dots indicate completed calls, and red dots indicate failures (including cancelled calls). History is kept in memory until the server restarts.

Queue status counts include pending and processing items. Each persistent queue row appends , N failed only when its failed count is greater than zero. Scan findings have their own row. Missing queue databases count as empty; read failures display unavailable. Database statistics always shows Findings: <count> first, using the primary Qdrant finding collection count, followed by the top five other collections. The finding count is read directly even when retrieval stores have not been initialized. After the dashboard closes, a log message reports that shutdown is waiting up to five minutes for current operations to finish. Shutdown stops claiming new work and allows current operations up to five minutes to finish before terminating remaining workers. Pending persistent queue items remain available for the next startup. SIGINT and SIGTERM use the same shutdown behavior. Non-interactive starts, including Docker Compose, continue to use normal logging output.

Ollama with llama3.2:3b:

python3 mcp_service.py

OpenAI:

export OPENAI_API_KEY=xxxx
python3 mcp_service.py --openai-model gpt-4-turbo

LiteLLM:

export ANTHROPIC_API_KEY=xxxx
python3 mcp_service.py --litellm-model anthropic/claude-sonnet-4-6

Google AI:

export GOOGLE_API_KEY=xxxx
python3 mcp_service.py --gemini-model gemini-2.0-flash

AWS Bedrock:

python3 mcp_service.py --bedrock-model us.meta.llama3-2-3b-instruct-v1:0

Disabling Open World Tools

Open-world tools allow the LLM to reach out to the Internet for spidering, directory busting, etc. There are use cases where this is undesired and only indexed content should be used.

Configure .env:

OPEN_WORLD=false

Restart Docker:

docker compose up -d

OR

Start the MCP server with --open-world false:

python3 mcp_service.py --open-world false

Indexing Data

The MCP tools will index data if appropriate. For example, spidering and directory busting. Data can be indexed by external means using the /index endpoint. The endpoint is not part of an MCP tool or protocol.

Indexing runs asynchronously and pauses when dependencies are unhealthy or disk space is low. Incoming submissions remain queued, and indexing resumes when readiness recovers. JavaScript is transformed and scanned during ingestion; findings are available through query_findings. On macOS, reported free space and disk readiness include capacity macOS expects to reclaim from purgeable resources, falling back to ordinary free space if that estimate is unavailable.

See ingestion architecture and operations for dependency readiness, disk-space configuration, JavaScript scanning, queue durability, storage formats, and maintenance.

curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/index @katana.json

The ingest.py script makes using this endpoint more convenient. Select its input format with --katana, --csv, or --burp-xml. The /index endpoint infers its supported data formats. Katana JSON is the preferred format.

katana

cat katana.jsonl | python3 ingest.py --mcp-url http://127.0.0.1:8000/ --katana

# live ingestion:
tail -f katana.jsonl | python3 ingest.py --mcp-url http://127.0.0.1:8000/ --katana

Recover deleted ingestion files

Use the read-only recovery exporter to reconstruct replayable HTTP exchanges and findings from surviving graph captures and Qdrant. See ingestion recovery for commands, prerequisites, recovery limitations, and reingestion instructions.

Burp Logger++ CSV

Minimum fields to export:

  • Request.AsBase64
  • Request.Time
  • Request.URL
  • Response.AsBase64
  • Response.RTT
cat LoggerPlusPlus.csv | python3 ingest.py --mcp-url http://127.0.0.1:8000/ --csv

# live ingestion using the auto-export feature of Logger++:
tail -f LoggerPlusPlus.csv | python3 ingest.py --mcp-url http://127.0.0.1:8000/ --csv

Burp Suite request/response XML

Export requests and responses as XML from Burp Suite, then ingest the complete export:

python3 ingest.py --mcp-url http://127.0.0.1:8000/ --burp-xml < export.xml

Both base64 and plain-text HTTP messages are supported. Each valid exchange is converted to Katana JSON and sent to /index. Invalid or incomplete items are reported to stderr and skipped. Malformed XML stops ingestion with a nonzero exit status; exchanges already queued remain queued. XML ingestion requires a complete export rather than a continuously appended stream. XML exports must be converted through ingest.py before submission to /index.

Extensions

Browser and intercepting proxy extensions are available at the following GitHub repos:

  • https://github.com/double16/shyhurricane-chrome
  • https://github.com/double16/shyhurricane-firefox
  • https://github.com/double16/shyhurricane-burpsuite
  • https://github.com/double16/shyhurricane-zap

The browser extensions will forward requests and responses made in Chrome and Firefox to the MCP server for indexing. There are controls for setting in-scope domains.

The Burp Suite and ZAP extensions will forward both requests/responses and alerts/findings. The alerts are used by the LLM to improve effectiveness.

Status Endpoint

The /status endpoint is an HTTP POST endpoint and is not part of the MCP server protocol.

curl -X POST http://127.0.0.1:8000/status

Use /status to inspect queue counts, indexing readiness, graph details, and proxy configuration. See the status response reference for fields and an example.

Proxy Serving Indexed Content

The MCP server exposes a proxy port, 8010 by default, that serves the indexed content. The intent is to use tools on the indexed content after the fact. For example, to run nuclei and feed the findings into the MCP server.

The proxy supports HTTP and HTTPS with self-signed certs. Look for a log line like the following to find the CA cert or POST an empty body to /status. Either your tools can be configured to ignore certificate validation or trust this cert.

replay proxy listening on ('127.0.0.1', 8010), CA cert is at /home/user/.local/state/shyhurricane/certs/ca.pem (CONNECT→TLS ALPN: h2/http1.1)

An example curl call:

curl -x 127.0.0.1:8010 -k https://example.com

Here is an example of using Nuclei on indexed content to submit findings passively:

nuclei -proxy http://127.0.0.1:8010 -target https://example.com -j | curl http://127.0.0.1:8000/findings -H "Content-Type: text/json" --data-binary @-

If a URL or domain isn't indexed, the 404 page will include links to URLs that have been indexed. A tool that spiders links may use this to find the indexed content.