npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cyanheads/attack-surface-mcp-server

v0.4.0

Published

Passive external attack-surface mapping: CT subdomains, DNS, TLS, HTTP posture, RDAP/WHOIS, Shodan via MCP. STDIO or Streamable HTTP.

Readme

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


[!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. Runs as a stdio process or a local Streamable HTTP server.

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: quick discovers subdomains and liveness; standard adds DNS records, TLS, and HTTP posture; thorough adds per-IP Shodan data when a key is present, otherwise a note.
  • Returns a structured surface map and defensive assessment of observable facts. Failed sources or unreachable hosts become notes; subdomain resolution is capped by ATTACKSURFACE_MAX_SUBDOMAINS (default 200), with truncation disclosed.
  • includeRegistration adds RDAP/WHOIS data for the apex at standard or thorough depth.
  • The apex takes the first candidate slot, even at cap 1. coverage separates discovered/examined candidates from host detail; unexamined and detailOmitted name exact omissions. retrieval supplies at most three executable examples (toolName, args, reason): DNS/TLS batches of at most 50 hosts and one HTTP URL, with instructions to repeat over the remaining names. These caps bound work, not response bytes.
  • Standard and thorough depth return canonical A/AAAA/CNAME/MX/NS/TXT/CAA records from the first configured resolver, all resolver outcomes and propagation mismatches for examined live hosts. Work is bounded to ten hosts concurrently. addressStatuses and per-host errors retain blocked targets and partial failures alongside successful data; null posture at quick depth means skipped work.
  • nextToolSuggestions uses {toolName, reason, args} and prefills one local attacksurface_recon_guidance triage call from TLS, missing audited headers, versioned technologies and observed ports. Address-only quick maps have no suggestion. guidanceCoverage names eligible/included counts and exact omitted hosts; submit remaining map hosts separately. The handoff includes at most 50 complete hosts and targets 24,000 incremental serialized bytes across both surfaces, with an explicit oversized-host exception. Map results themselves are not paginated.

attacksurface_enumerate_subdomains tool

  • Discovers names through crt.sh → Certspotter → the apex TLS certificate SANs, then resolves DNS liveness. includeUnresolved: false keeps only live hosts while filteredAddressErrors retains operational failures for excluded names.
  • Returns each name's source provenance and per-source status; no DNS brute-forcing or target-resolver probing.
  • offset and limit (default 50, larger requests clamped) select a sorted candidate prefix before DNS; work is bounded by min(50, ATTACKSURFACE_MAX_SUBDOMAINS). discoveredCount, examinedCount, returnedCount, remainingCount and nextOffset distinguish attempted work from consumed records. A wholly filtered page still advances. continuation preserves domain/sources/filter; CT is re-read, so changes can shift offsets. Pages target 24,000 serialized bytes across structured and complete text output; one complete oversized record is returned with an explicit notice. Examined but undelivered candidates are re-resolved on continuation.
  • crt.sh names come from newline-delimited name_value; Certspotter uses dns_names. Certspotter follows issuance cursors for at most five pages within one 15-second budget, including retries and body reads. complete, pagesFetched and incompleteReason disclose exhaustion or partial coverage; names from earlier pages survive a later failure. addressError distinguishes operational DNS failures from ENODATA/ENOTFOUND absence.

attacksurface_resolve_dns tool

  • Accepts 1–1000 hosts, validates the complete list before I/O, and processes the first 50 in input order. processedCount and exact remainingHosts appear on both surfaces; resubmit the remainder to continue. Queries A, AAAA, CNAME, MX, NS, TXT, and CAA across public resolvers (default 8.8.8.8, 1.1.1.1, 9.9.9.9); reverse DNS (PTR) is optional.
  • Returns per-resolver answers to expose propagation gaps. Private or loopback resolver IPs produce blocked_resolver; an individual host failure becomes a per-host error.
  • Canonical records come from the first configured resolver. Resolver failures (including SERVFAIL) remain in queryError and hostError alongside any successful records; absent records are error-free.

attacksurface_inspect_tls tool

  • Accepts 1–1000 hosts, validates the complete list before I/O, and processes the first 50 in input order. processedCount and exact remainingHosts appear on both surfaces, including all-host failures; resubmit the remainder to continue. 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 requested-host identity (identityAuthorized/identityError) are separate checks. Identity is checked against the raw leaf even for an untrusted chain; an unavailable or unverifiable leaf means unknown identity, with verification errors reported.
  • Unparseable validity bounds retain their raw values and add findings. daysUntilExpiry is null only when expiry cannot be parsed; a malformed start date does not erase a known expiry.

attacksurface_probe_http tool

  • Accepts one HTTP(S) URL; timeout defaults to 10000ms (1000–30000ms). Follows up to ten redirects with an SSRF check at every hop; a refused target produces blocked_target. Another redirect at the limit returns finalStatus: 0 and a transportError.
  • The timeout is one total budget for DNS, redirects, headers, and body inspection. bodyInspection reports complete only after observed EOF, limited at the 64,000-byte prefix cap, failed on body failure, or not_read without a final response. Body failures retain usable status and headers; caller cancellation propagates.
  • Returns status, redirect chain, 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.
  • Header findings check supported framing/nosniff tokens, positive valid HSTS, and exact cookie attribute names. CSP analysis covers observed frame-ancestors restrictions, not general policy validity. Raw headers remain available alongside findings.
  • Cloudflare server banners identify a CDN; AWS ELB banners identify edge infrastructure (other). Neither banner alone establishes WAF protection.

attacksurface_lookup_registration tool

  • Accepts a domain, unscoped IP, or CIDR with one decimal integer prefix (IPv4 0–32, IPv6 0–128); type (auto / domain / ip) controls interpretation. Uses RDAP first, then authoritative WHOIS port 43 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.
  • WHOIS explicit no-match returns no_match; missing or failed referrals and empty/unrecognized authoritative responses return source_unavailable. Allocation ranges become exact CIDR coverage; invalid allocation text remains in rawWhois. The complete raw response is rendered as literal text, as are upstream strings in registration, HTTP, TLS, DNS, and Shodan output.

attacksurface_lookup_host tool

  • mode: "host" (default) looks up one IP; mode: "search" accepts an internet-wide query with optional facets and may consume Shodan query credits. Requires SHODAN_API_KEY.
  • Both modes reject blank targets. Host mode accepts bare IPv4/IPv6 addresses and returns invalid_target for other syntax before contacting Shodan; search queries are preserved verbatim.
  • Search page is a positive integer defaulting to 1; it is ignored in host mode. Each call fetches exactly one 100-match page, returning page, pageSize, hasMore, nextPage and a prefilled continuation preserving query and facets. Later pages are opt-in and may consume credits; empty pages terminate even if Shodan's total is stale.
  • Returns ports, banners, software versions, hostnames, ASN, and geography from Shodan's last scan. Missing credentials produce source_unavailable; no host data produces no_data. The server performs no port scans.

attacksurface_recon_guidance tool

  • Takes prior findings (hosts, certificates, missing headers, software versions, ports). triage includes all findings ordered by urgency; posture selects TLS/header hardening; coverage selects inspection/version gaps and observed-service verification. SSH/RDP and other ports are informational inventory evidence, never proof of a vulnerability. Empty evidence reports unknown coverage.
  • Suggestions use {toolName, reason, args} and target registered local tools. Product/version advisory checks remain in priorities and rendered text. No external executable calls or active scans are emitted.
  • Independent priorityOffset/suggestionOffset pages use limit default 50 (larger requests clamp to 50). Totals, returned counts and next offsets appear on both surfaces. Resubmit the original findings/topic with the next offsets; when one is null, use that collection's total to keep it exhausted. The complete serialized response targets 24,000 bytes, except one complete oversized record with an explicit notice. plan is a concise topic/coverage summary; detailed actions remain in priorities and render once in text.

attacksurface://surface/{domain} resource

  • Takes a domain and returns an application/json snapshot equivalent to a standard-depth attacksurface_map_domain call.
  • Includes subdomain and live-host counts plus per-host DNS/TLS/HTTP posture and errors. Candidate resolution uses the same apex-first cap as mapping; a separate 50-live-host detail cap limits posture work. coverage discloses both omission sets with executable follow-up calls, while notes retain source incompleteness and failed legs.

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 error fields and operational notes instead 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

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/attack-surface-mcp-server.git
  1. Navigate into the directory:
cd attack-surface-mcp-server
  1. Install dependencies:
bun install
  1. 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-network assessment; URL parsing and HTTP(S) scheme checks remain enforced. 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/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for 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.