A side-by-side look at the Domain Security and Intodns MCP Server MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Domain Security for security work as a local process; choose Intodns MCP Server for security as a local process. You can also run both side by side in one mcpServers block.
email_auth_audit, spf_check, dmarc_check.scan_domain, nis2_quickscan, get_everything_report.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Security | Security |
| Quality signal | 63/100 · Good | 63/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | No auth required |
| Pricing | Not declared | Free |
| Required env vars | None listed | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 1 | 2 |
| Directory activity | 1 views 0 installs 0 upvotes | 5 views 0 installs 0 upvotes |
| Tools | 19 | 45 |
| Full listing | View Domain Security | View Intodns MCP Server |
email_auth_auditHeadline tool. Audits a domain's email-authentication posture in one call — SPF, DKIM, DMARC and MX — then returns a 0–100 score, an A–F grade and a prioritised list of fixes. Use this first; reach for the per-record tools (spf_check, dmarc_check, dkim_check) only when you need the full detail of one mechanism.
Args:
- domain (string): the domain to audit.
- dkim_selectors (string[], optional): DKIM selectors to probe. If omitted, common provider selectors are tried (absence is then inconclusive).
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns (JSON):
{
"domain": string,
"grade": "A".."F",
"score": number, // 0-100
"has_mx": boolean,
"mx_hosts": string[],
"spf": { found, record, all_qualifier, lookup_count, exceeds_lookup_limit, findings[] },
"dmarc":{ found, policy, tags, findings[] },
"dkim": { any_found, selectors[], findings[] },
"top_recommendations": string[]
}
Examples:
- "Is example.com protected against email spoofing?" -> email_auth_audit(domain="example.com")
- "Audit acme.com, our DKIM selector is 'k1'" -> email_auth_audit(domain="acme.com", dkim_selectors=["k1"])
Errors: returns an error only if the domain is malformed; missing records are reported as findings, not errors.spf_checkFetch and analyse a domain's SPF record. Detects: missing/multiple records, the trailing 'all' qualifier (+all/?all/~all/-all), and counts DNS-querying terms recursively against the RFC 7208 limit of 10.
Args:
- domain (string): the domain to check.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { found, record, multiple_records, all_qualifier, lookup_count, exceeds_lookup_limit, findings[] }.
Example: "Does sendgrid.net's SPF exceed the 10-lookup limit?" -> spf_check(domain="sendgrid.net").dmarc_checkFetch and parse a domain's DMARC record (_dmarc.<domain>). Reports the policy (p=), subdomain policy (sp=), reporting addresses (rua/ruf), pct and alignment (aspf/adkim), and warns on monitor-only or partial deployments.
Args:
- domain (string): the domain to check.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { found, record, policy, tags{}, findings[] }.
Example: "What is paypal.com's DMARC policy?" -> dmarc_check(domain="paypal.com").dkim_checkLook up DKIM public keys at <selector>._domainkey.<domain>. Because DKIM selectors are arbitrary and undiscoverable, you should pass the selector(s) your mail provider uses for a definitive answer; otherwise a curated list of common selectors is probed and a miss is inconclusive.
Args:
- domain (string): the domain to check.
- selectors (string[], optional): DKIM selectors to probe.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { any_found, probed_selectors, selectors[{selector, found, record, key_type}], findings[] }.
Examples:
- "Does acme.com publish a DKIM key for selector 'google'?" -> dkim_check(domain="acme.com", selectors=["google"])
- "Find any DKIM keys for acme.com" -> dkim_check(domain="acme.com")mta_sts_checkCheck a domain's MTA-STS deployment: the _mta-sts TXT record AND the policy file at https://mta-sts.<domain>/.well-known/mta-sts.txt. Reports the enforcement mode (enforce/testing/none) and the listed MX hosts. MTA-STS forces TLS for inbound SMTP and blocks downgrade attacks.
Args:
- domain (string): the domain to check.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { dns_record_found, policy_found, mode, policy{}, findings[] }.
Example: "Does gmail.com enforce MTA-STS?" -> mta_sts_check(domain="gmail.com").tls_rpt_checkCheck a domain's TLS-RPT record (_smtp._tls.<domain> TXT). TLS-RPT lets you receive reports about TLS delivery failures to your domain.
Args:
- domain (string): the domain to check.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { found, record, findings[] }.
Example: "Does microsoft.com publish TLS-RPT?" -> tls_rpt_check(domain="microsoft.com").bimi_checkCheck a domain's BIMI record (default._bimi.<domain> TXT), which points to the brand logo (and optional VMC) displayed next to authenticated mail. BIMI requires an enforced DMARC policy to take effect.
Args:
- domain (string): the domain to check.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { found, record, findings[] }.
Example: "Does cnn.com have BIMI set up?" -> bimi_check(domain="cnn.com").dns_lookupResolve all common DNS record types (A, AAAA, CNAME, MX, NS, TXT, SOA) for a domain in one call, using public resolvers (Cloudflare/Google/Quad9).
Args:
- domain (string): the domain to query, e.g. "example.com".
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: a map of record type -> list of records. Each record has { type, host, value, priority? }.
Examples:
- "What are the MX records for stripe.com?" -> dns_lookup(domain="stripe.com")
- Use ssl_certificate for TLS details, whois_lookup for registration data.
Errors: returns an error if the domain is malformed or has no resolvable records.reverse_dnsResolve the PTR (reverse DNS) records for an IP address — the hostname(s) the IP maps back to.
Args:
- ip (string): IPv4 or IPv6 address.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { ip, hostnames: string[] }.
Example: "What hostname does 8.8.8.8 reverse to?" -> reverse_dns(ip="8.8.8.8").
Errors: returns an error if the IP is invalid or has no PTR record.ip_geolocationGeolocate an IP address (country, region, city, coordinates, time zone) using the offline DB-IP Lite database, plus its reverse-DNS hostname. No external API.
Args:
- ip (string): IPv4 or IPv6 address.
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: { ip, country_iso, country_name, region, city, latitude, longitude, time_zone, hostname }.
Example: "Where is 151.101.1.69 located?" -> ip_geolocation(ip="151.101.1.69").
Note: geolocation is approximate (city-level at best) and offline data may lag reality. The time zone is estimated from the coordinates.
Data: IP Geolocation by DB-IP (https://db-ip.com), licensed CC BY 4.0 — credit it when showing these results.ssl_certificateInspect the TLS certificate served by a host: issuer, subject, validity window, days-until-expiry, SANs, serial and SHA-256 fingerprint. Flags expired or soon-to-expire certificates.
Args:
- domain (string): host to connect to.
- port (number): TLS port (default 443).
- response_format ('markdown' | 'json'): output format (default 'markdown').
Returns: certificate fields plus { days_until_expiry, expired, expires_soon, trusted, hostname_matches, authorization_error }. An untrusted certificate (self-signed, unknown root, wrong host) is still inspected and reported, never silently passed.
Example: "When does github.com's certificate expire?" -> ssl_certificate(domain="github.com").
Errors: returns an error if the host is unreachable or serves no certificate.whois_lookupLook up domain registration data over the raw WHOIS protocol (port 43): registrar, creation/update/expiry dates, name servers and domain status. Resolves the correct WHOIS server via IANA and follows registrar referrals. No API key.
Args:
- domain (string): the domain to look up.
- response_format ('markdown' | 'json'): output format (default 'markdown'). JSON includes the raw WHOIS text.
Returns: { domain, registrar, created, updated, expires, name_servers[], status[], whois_server }.
Example: "Who is the registrar for openai.com and when does it expire?" -> whois_lookup(domain="openai.com").
Errors: returns an error if no WHOIS server answers (some ccTLDs restrict or rate-limit WHOIS).scan_domainRun the fast IntoDNS.ai DNS and email security scan (~3-8s). Returns a letter grade A+ to F, numeric score 0-100, structured issue list, prioritised recommendations, full DNS/email/web/security result sections, and canonical citation URLs. Read-only — no domain mutation, no destructive side effects. The default tool for agent-visible scan evidence; use get_everything_report for a deeper single-shot report including web/blacklist/sender data, or start_deep_scan for slower Internet.nl-grade analysis. After running, use explain_issue or generate_dns_fix on any returned issue. No auth.nis2_quickscanCompute a NIS2 Article 21.2 readiness score for a domain by mapping the IntoDNS quickscan onto the ten NIS2 measures. Returns a 0-100 weighted total, per-measure status (Article 21.2 a-j), evidence rows, critical gaps, and concrete fix suggestions. The score reflects only the DNS and email layer of NIS2 — full NIS2 compliance also requires audit of web applications, supply chain, organisational processes, and training. Use when the user asks about NIS2 compliance, NIS2 readiness, NIS2 Article 21.2, cyber-hygiene compliance, or related EU-NIS regulation checks for a domain.get_everything_reportGenerate the complete live IntoDNS.ai report covering DNS, email authentication, web/HTTPS, blacklist reputation, sender requirements, and canonical citation URLs in a single call. Read-only, no domain mutation. ~5-15s latency depending on backend cache state. Use when the user asks for everything, the full picture, or a deep current-state summary; use scan_domain for a faster default scan, or create_report_snapshot when the result must remain immutable for audit/ticket use. No auth, no side effects.create_report_snapshotCreate an immutable evidence snapshot of the current Everything Report for a domain. Returns a snapshot ID, ISO timestamp, SHA-256 content hash, and stable bookmarkable URLs for both JSON and Markdown renderings of the report. Snapshots are write-once and resolve to the same evidence months/years later — useful for tickets, audit trails, NIS2/ISO compliance evidence, and LLM citations that should not drift. A canonical POST creates one snapshot per call (additive and not idempotent); use get_report_snapshot to read it back. Use this instead of get_everything_report when the result must remain stable.get_report_snapshotRead a previously created IntoDNS.ai Everything Report evidence snapshot by snapshot ID. Read-only GET — returns the immutable JSON report exactly as it was at snapshot creation, with the original SHA-256 content hash and timestamp. Requires `snapshotId` from create_report_snapshot. Use to verify or re-read an audit-trail evidence record without re-running a live scan; use get_everything_report for current live data instead. No auth, fully idempotent.start_deep_scanStart a long-running Internet.nl deep scan (typically 30-120s). Returns a `scanId` immediately; poll get_deep_scan_status until status='finished'. Read-only — no domain mutation. Internet.nl runs an exhaustive standards-compliance audit (IPv6, DNSSEC, modern TLS, RPKI, mail authentication) used by EU governments. Use when scan_domain output is not strict enough for compliance reporting, or when the user asks for an Internet.nl-grade audit. For sub-10s answers, use scan_domain. To abort an in-progress scan, call cancel_deep_scan. No auth.get_deep_scan_statusRead-only status poll for a long-running Internet.nl deep scan. Returns scan progress (pending/running/finished), category scores, per-test results, and any failures. Requires a scanId returned by start_deep_scan; poll every 10-30s until status='finished'. Use after start_deep_scan; for fast single-vantage scans, prefer scan_domain. No auth, no side effects.cancel_deep_scanCancel an in-progress Internet.nl deep scan. Marks the scan cancelled; the polling loop then withdraws the upstream Internet.nl batch, usually within ten seconds. Requires `scanId` returned by start_deep_scan. Nothing is deleted: a scan that already finished keeps its results and is left alone, so calling this twice is safe. An unknown or expired scanId returns 404. Use when the user changes their mind mid-scan or when polling get_deep_scan_status would otherwise time out. No auth.lookup_dnsRead-only DNS record lookup via DNS-over-HTTPS. Pass `type` for a single record type or `types` for an array; if both omitted, returns A records. Returns parsed answers with TTL, raw rdata, and DNSSEC AD bit. Use for arbitrary record queries; use validate_dnssec for full DNSSEC chain validation, or check_dns_propagation for multi-resolver consensus. No auth, no rate limits beyond upstream resolver.validate_dnssecRead-only DNSSEC chain validation. Walks the DS/DNSKEY chain from root, checks signatures, algorithm strength, key rollover state, and reports any broken links or unsigned zones. Returns chain steps, algorithm grades, and a boolean `valid`. Use when a domain claims DNSSEC; use lookup_dns(type='DNSKEY') for raw key data only. Single HTTP GET, no auth, no destructive actions.check_dns_propagationCompare DNS responses across the nine currently configured public and authoritative resolvers to detect propagation lag, missing answers, or inconsistent TTL/data values. Defaults to record type A and region 'all'. Returns every resolver response plus a propagation percentage and explicit inconsistency list. Use when records were just changed and you suspect staleness; for a single DNS-over-HTTPS lookup use lookup_dns instead. Read-only HTTP, no auth, and no destructive actions.check_tlsa_daneRead-only TLSA/DANE DNS record check. With no port, resolves MX hosts and validates their `_25._tcp` TLSA tuple syntax; with an explicit port, queries `_<port>._<protocol>.<domain>`. Returns parsed usage, selector, matching type, certificate data, syntax errors, and best-practice advisories. It does not fetch or cryptographically match the live service certificate, so pair it with check_smtp_tls for SMTP certificate evidence. Use before publishing DANE records or troubleshooting DANE handover. No auth or destructive actions.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"domain-security": {
"command": "npx",
"args": [
"-y",
"domain-security-mcp-server"
]
}
}
}
{
"mcpServers": {
"rosconl-intodns-mcp-server": {
"command": "npx",
"args": [
"-y",
"intodns-mcp"
]
}
}
}