About attack-surface-mcp-server
attack-surface-mcp-server maps a domain’s public external attack surface for security teams and authorized defenders. It combines passive discovery with DNS, TLS, HTTP, and optional Shodan data, while excluding active scans and exploitation.
Key features
- 01Passive subdomain discovery from CT logs
- 02DNS records and propagation checks
- 03TLS certificate and posture inspection
- 04HTTP headers and technology evidence
- 05RDAP/WHOIS and optional Shodan data
- 06Defensive findings and follow-up guidance
Use cases
- 01Inventory live subdomains for an owned domain
- 02Review TLS and HTTP security posture
- 03Compare DNS answers across public resolvers
- 04Triage observed host and certificate findings
MCP support
Exposes eight tools and one JSON resource to MCP clients. Supports STDIO and Streamable HTTP; tools provide passive mapping, lookups, posture checks, and defensive guidance.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
npx -y @cyanheads/attack-surface-mcp-serverSHODAN_API_KEYSecretOptional Shodan API key. Enables attacksurface_lookup_host; absent → that one tool returns source_unavailable and the rest of the server works.CERTSPOTTER_API_KEYSecretOptional Certspotter API key. Raises CT-fallback rate limits; absent → free unauthenticated tier.ATTACKSURFACE_DEFAULT_RESOLVERSComma-separated default DNS resolver IPs for attacksurface_resolve_dns.ATTACKSURFACE_HTTP_USER_AGENTDefault User-Agent for attacksurface_probe_http (overridable per call).ATTACKSURFACE_MAX_SUBDOMAINSMap/snapshot resolution cap; enumeration also caps each page at 50 candidates.ATTACKSURFACE_RDAP_BOOTSTRAP_URLRDAP bootstrap base URL; override for a private/mirrored RDAP.ATTACKSURFACE_ALLOW_PRIVATE_TARGETSSet true to permit non-public target ranges for trusted internal-network assessment; URL and HTTP(S) scheme checks remain enforced.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').MCP_TRANSPORT_TYPESelects the HTTP transport.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODESecretAuthentication mode to use: 'none', 'jwt', or 'oauth'.Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.
Tools
8Saved tool list · updated 2w ago
| Parameter | Type | Required | Description |
|---|---|---|---|
| depth | enum | No | "quick" = subdomains + liveness only; "standard" = + DNS/TLS/HTTP posture; "thorough" = + Shodan enrichment (when keyed).quick | standard | thorough |
| domain | string | Yes | Apex domain to map (e.g. "example.com"). |
| includeRegistration | boolean | No | When true (and depth is standard+), include RDAP/WHOIS registration for the apex. |
Details
From the project README@cyanheads/attack-surface-mcp-server
Passive external attack-surface mapping: CT subdomains, DNS, TLS, HTTP posture, RDAP/WHOIS, Shodan via MCP. STDIO or Streamable HTTP.
Important
Authorized, defensive use only. Point this server only at assets you own or are explicitly authorized to assess. It performs passive, non-intrusive reconnaissance — it reads public records (Certificate Transparency logs, DNS, RDAP/WHOIS) and each target's own published surface (one TLS handshake and one HTTP GET per host). It does not port-scan, exploit, brute-force, fuzz, or probe for vulnerabilities; that capability is excluded from the surface by design, not gated behind a flag. Output is descriptive — what exists and what the security posture is — never an exploitation plan. Every outbound connection passes an SSRF guard that refuses private, loopback, link-local, and cloud-metadata targets.
Overview
Passive external attack-surface mapping (EASM) over Certificate Transparency logs, DNS, TLS, HTTP, and RDAP/WHOIS registries, with optional Shodan host intelligence. Discover subdomains, resolve DNS records, and inspect TLS and HTTP security posture across a domain's live hosts.
Tools
| Tool | Description |
|---|---|
attacksurface_map_domain | Flagship workflow. Maps a domain's external surface end to end: CT-log subdomain discovery → DNS liveness → (standard+) DNS records, TLS posture, HTTP headers/tech → optional RDAP/WHOIS → (thorough + key) per-IP Shodan enrichment. Returns a structured surface map and a defensive assessment of observable facts. |
attacksurface_enumerate_subdomains | Passive subdomain discovery from Certificate Transparency logs (crt.sh → Certspotter → TLS-SAN fallback chain), with DNS resolution to mark which names are live. Per-source provenance; no DNS brute-forcing. |
attacksurface_resolve_dns | Resolve and enumerate DNS records (A/AAAA/CNAME/MX/NS/TXT/CAA) for one or more hosts across multiple public resolvers, with optional reverse DNS (PTR). Per-resolver values surface propagation gaps. |
attacksurface_inspect_tls | Inspect TLS/SSL posture via a real read-only handshake: protocol, cipher, leaf certificate and chain depth, SANs, validity window, days-to-expiry, issuer, validation status. Reports invalid/expired/self-signed certs instead of failing. |
attacksurface_probe_http | Passive HTTP(S) probe: one GET following redirects. Returns status, redirect chain, headers, a security-header audit (HSTS/CSP/X-Frame-Options/cookie flags/CORS reflection), and an evidence-bound technology fingerprint. |
attacksurface_lookup_registration | Registration and ownership lookup via RDAP (JSON; WHOIS fallback). A domain returns registrar, status, lifecycle events, nameservers, DNSSEC; an IP/CIDR returns netblock, allocation CIDRs, origin ASN, country. |
attacksurface_lookup_host | Infrastructure intelligence for a single IP (open ports, banners, software versions, ASN, geo) or a faceted internet-wide search, via Shodan. Requires SHODAN_API_KEY — returns a typed source_unavailable error when unset; the rest of the server is unaffected. |
attacksurface_recon_guidance | Offline defensive review with topic selection, bounded priority/suggestion pages, local follow-up calls and product/version advisory-check guidance. No external calls. |
Resources
| Resource | Description |
|---|---|
attacksurface://surface/{domain} | Read-once snapshot of a domain's mapped external surface (subdomains, live hosts, per-host DNS/TLS/HTTP posture summary), with separate candidate and 50-host detail coverage. |
All resource data is also reachable via tools — tool-only clients lose nothing, since attacksurface_map_domain covers the same ground.
Capability reference
attacksurface_map_domain tool
depth:quickdiscovers subdomains and liveness;standardadds DNS records, TLS, and HTTP posture;thoroughadds per-IP Shodan data when a key is present, otherwise a note.- Returns a structured surface map and a defensive
assessmentof observable facts. Failed sources and unreachable hosts become notes, and subdomain resolution is capped byATTACKSURFACE_MAX_SUBDOMAINS(default 200), with truncation disclosed. includeRegistrationadds RDAP/WHOIS data for the apex at standard or thorough depth. Standard and thorough also return DNS records from the first configured resolver plus propagation mismatches for live hosts; null posture at quick depth means the work was skipped.coveragelists what was left unexamined or without host detail (unexamined,detailOmitted), andretrievalgives up to three ready-to-run follow-up calls (DNS/TLS batches of at most 50 hosts, one HTTP URL).nextToolSuggestionsprefills anattacksurface_recon_guidancecall for up to 50 hosts, withguidanceCoveragenaming any omitted.
attacksurface_enumerate_subdomains tool
- Discovers names through crt.sh → Certspotter → the apex TLS certificate SANs, then resolves DNS liveness.
includeUnresolved: falsekeeps only live hosts;filteredAddressErrorsretains DNS failures for excluded names. - Returns each name's source provenance and per-source status.
complete,pagesFetched, andincompleteReasondisclose partial coverage, andaddressErrorseparates DNS failures from ENODATA/ENOTFOUND absence. offsetandlimit(default 50, larger requests clamped) page through candidates, bounded bymin(50, ATTACKSURFACE_MAX_SUBDOMAINS). UsenextOffsetor the prefilledcontinuationfor the next page, andremainingCountto see what is left. CT logs are re-read per page, so changes can shift offsets.
attacksurface_resolve_dns tool
- Accepts 1–1000 hosts and processes the first 50 in input order;
processedCountandremainingHostsshow what to resubmit. Queries A, AAAA, CNAME, MX, NS, TXT, and CAA across public resolvers (default8.8.8.8,1.1.1.1,9.9.9.9); reverse DNS (PTR) is optional. - Returns per-resolver answers to expose propagation gaps, with canonical records from the first configured resolver. Resolver failures (including SERVFAIL) appear in
queryErrorandhostErrornext to any successful records. - Private or loopback resolver IPs produce
blocked_resolver; an individual host failure becomes a per-host error.
attacksurface_inspect_tls tool
- Accepts 1–1000 hosts and processes the first 50 in input order;
processedCountandremainingHostsshow what to resubmit. Port defaults to 443 and handshake timeout to 8000ms (1000–30000ms). - Returns negotiated protocol, cipher, leaf certificate and chain depth, SANs, issuer, validity window, days to expiry, and validation status. Invalid, expired, and self-signed certificates are reported; one host failure becomes a per-host error.
- Chain trust (
validationAuthorized/validationError) and hostname match (identityAuthorized/identityError) are separate checks.daysUntilExpiryis null only when the expiry date cannot be parsed.
attacksurface_probe_http tool
- Accepts one HTTP(S) URL; timeout defaults to 10000ms (1000–30000ms) and covers the whole probe. Follows up to ten redirects with an SSRF check at every hop; a refused target produces
blocked_target, and exceeding the redirect limit returnsfinalStatus: 0and atransportError. - Returns status, redirect chain, raw headers, security-header findings (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, cookie flags, CORS reflection), and technology detections with their triggering evidence. CSP analysis covers
frame-ancestorsonly. bodyInspectionreportscomplete,limited(64,000-byte prefix cap),failed, ornot_read; a body failure keeps the status and headers.
attacksurface_lookup_registration tool
- Accepts a domain, IP, or CIDR (prefix IPv4 0–32, IPv6 0–128);
type(auto/domain/ip) controls interpretation. Uses RDAP first, then WHOIS when needed. - Domain results include registrar, EPP statuses, lifecycle events, nameservers, and DNSSEC; IP/CIDR results include netblock, allocation CIDRs, ASN, and country. Sparse or redacted fields stay unknown, and the raw WHOIS response is kept in
rawWhois. - An explicit WHOIS no-match returns
no_match; a failed referral or an empty or unrecognized WHOIS response returnssource_unavailable.
attacksurface_lookup_host tool
mode: "host"(default) looks up one bare IPv4/IPv6 address and returnsinvalid_targetfor anything else;mode: "search"takes an internet-wide query with optionalfacetsand may consume Shodan query credits. RequiresSHODAN_API_KEY.- Search returns one 100-match page per call (
pagedefaults to 1, ignored in host mode), withhasMore,nextPage, and a prefilledcontinuation. Later pages are opt-in and may consume credits. - Returns ports, banners, software versions, hostnames, ASN, and geography from Shodan's last scan. Missing credentials produce
source_unavailable; no host data producesno_data. The server performs no port scans.
attacksurface_recon_guidance tool
- Takes prior findings (hosts, certificates, missing headers, software versions, ports). Topic
triagecovers all findings ordered by urgency,posturecovers TLS/header hardening, andcoveragecovers inspection and version gaps. Open ports are inventory evidence, never proof of a vulnerability. - Returns
prioritiesplus suggestions as{toolName, reason, args}that target local tools only; no external calls or active scans are emitted. priorityOffsetandsuggestionOffsetpage independently withlimit(default 50, max 50). Resubmit the original findings and topic with the next offsets.
attacksurface://surface/{domain} resource
- Takes a domain and returns an
application/jsonsnapshot equivalent to a standard-depthattacksurface_map_domaincall. - Includes subdomain and live-host counts plus per-host DNS/TLS/HTTP posture and errors. Resolution uses the same cap as mapping, and posture detail covers at most 50 live hosts;
coveragelists what was omitted with follow-up calls.
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Attack-surface-specific:
- Passive and non-intrusive by mandate — public records plus each target's own single published response; active scanning, exploitation, and brute-forcing are excluded from the surface, not toggled by a flag
- Keyless core — CT subdomain enumeration, DNS, TLS, HTTP/tech, and RDAP/WHOIS all work with zero API keys; Shodan is strictly additive depth
- SSRF guard on every outbound connection — rejects private, loopback, link-local, cloud-metadata, and reserved IPv4/IPv6 ranges before connecting (opt out for trusted internal assessment via
ATTACKSURFACE_ALLOW_PRIVATE_TARGETS) - Multi-source aggregation with fallback chains — CT discovery falls through crt.sh → Certspotter → TLS-SAN; registration falls through RDAP → WHOIS
Agent-friendly output:
- Provenance on every result — source labels (
source: crt.sh | certspotter | tls-san,source: rdap | whois) and per-source status so agents can assess completeness and trust - Graceful partial failure — multi-target and multi-source tools return per-item/per-source
errorfields and operationalnotesinstead of failing the whole call; only malformed input throws - Discriminated, typed contracts — typed error reasons (
source_unavailable,blocked_target,all_sources_failed) and union output (kind: domain | ip) let callers branch on data, not string parsing - No fabricated signal — technology detections carry their triggering evidence; absent CT/DNS/RDAP fields are reported as unknown, never inferred
Getting started
Add the following to your MCP client configuration file. Every tool except attacksurface_lookup_host works with no configuration — the keyless core boots on an empty environment.
{
"mcpServers": {
"attack-surface-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/attack-surface-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"attack-surface-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/attack-surface-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"attack-surface-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/attack-surface-mcp-server:latest"]
}
}
}
To enable Shodan host intelligence (attacksurface_lookup_host), add SHODAN_API_KEY to the env block.
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- No API key required for the core tools. Optional: a Shodan API key for
attacksurface_lookup_host, and a Certspotter API key to raise CT-fallback rate limits.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/attack-surface-mcp-server.git
- Navigate into the directory:
cd attack-surface-mcp-server
- Install dependencies:
bun install
- Configure environment (optional):
cp .env.example .env
# edit .env only if you want Shodan, a Certspotter key, or non-default behavior
Configuration
All variables are optional — the server boots and delivers its keyless core with an empty environment.
| Variable | Description | Default |
|---|---|---|
SHODAN_API_KEY | Enables attacksurface_lookup_host. Absent → that tool returns source_unavailable; every other tool keeps working. | — |
CERTSPOTTER_API_KEY | Raises Certspotter rate limits for the CT-log subdomain fallback. Absent → free unauthenticated tier (rate-limited but functional). | — |
ATTACKSURFACE_DEFAULT_RESOLVERS | Comma-separated public DNS resolver IPs for attacksurface_resolve_dns and standard+ map/snapshot posture. | 8.8.8.8,1.1.1.1,9.9.9.9 |
ATTACKSURFACE_HTTP_USER_AGENT | Default User-Agent for attacksurface_probe_http (overridable per call). | attack-surface-mcp-server/passive-recon (+https://github.com/cyanheads/attack-surface-mcp-server) |
ATTACKSURFACE_MAX_SUBDOMAINS | Map/snapshot resolution cap; enumeration also caps each page at 50 candidates. | 200 |
ATTACKSURFACE_RDAP_BOOTSTRAP_URL | RDAP bootstrap base URL; override for a private/mirrored RDAP. | https://rdap.org |
ATTACKSURFACE_ALLOW_PRIVATE_TARGETS | Set true to permit non-public target ranges for trusted internal assessment. Leave false on any public deployment. | false |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. The server declares stateless; a value set here overrides it. Ignored under stdio. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
-
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t attack-surface-mcp-server .
docker run --rm -e MCP_TRANSPORT_TYPE=http -p 3010:3010 attack-surface-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/attack-surface-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources and inits the six services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/services | Domain service integrations (ct, dns, tls, http, registration, shodan). |
src/utils | SSRF guard and input validation. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools and resources via the barrels in
src/mcp-server/*/definitions/index.ts - Every outbound connection to a user-supplied target must pass the SSRF guard (
assertSafeDomain/assertSafeUrl/assertSafeResolverIp) before connecting - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.