Pro and Enterprise
DomainGuard MCP server
One endpoint that hands Claude your whole account and lets it act on it: add a domain by answering a few questions, see what is broken across your domains, what is holding a page back in search, where you rank on a map, and what to change. Claude also checks the scanner's work and tells us when it is wrong. This page is the complete reference plus the playbooks Claude follows.
Built on the Model Context Protocol, the open standard for connecting AI assistants to outside systems.
What this does
DomainGuard already checks your domains: DNS, SSL, SPF, DKIM, DMARC, HTTP headers, blacklists, vulnerabilities, uptime, on-page SEO, whole-site crawls, accessibility, local rankings, reviews, citations, backlinks, and how often AI answers cite you. The dashboard shows you the findings. The MCP server hands those same findings to Claude, so you can ask about them in plain language and get a specific edit list back.
With a read-only key it explains. Ask which certificate expires first, why invoices are landing in spam, what regressed since last month, or which of eleven sites share the same misconfiguration. With a write-capable key it also works: run the scan, check the rankings, pull the competitor gap, then compare the result against the baseline it took before you started.
One boundary worth stating plainly. We monitor your domains, we do not host them. Every tool here reports, records, or measures. Claude can tell you exactly what your DMARC record should say and why the one you have is failing — publishing it still happens at your DNS provider.
At a glance
- Endpoint
- /api/mcp
- Transport
- Streamable HTTP
- Auth
- Bearer nhm_…
- Tools
- 176 — 95 read, 81 write
- Local SEO actions
- 39, catalogued
- Sessions
- None — stateless
Setting it up
1. Create an API key
In the dashboard, go to Settings, then API Keys. Pick the scopes the key should reach — domains, scans, SEO, accessibility, vulnerability, compliance, status — and copy the key when it is shown. It is shown once. On Enterprise you can also tick write access; leave it off for a key that should only ever report. Give each key an expiry if you have a date in mind.
2. Add the server
# Claude Code
claude mcp add --transport http domainguard https://nhmohio.com/api/mcp \
--header "Authorization: Bearer nhm_your_key_here"# Claude Desktop, or any client taking a JSON config
{
"mcpServers": {
"domainguard": {
"type": "http",
"url": "https://nhmohio.com/api/mcp",
"headers": { "Authorization": "Bearer nhm_your_key_here" }
}
}
}Any MCP client that supports a remote server over HTTP with a custom header works the same way. There is no OAuth flow to complete and no event stream to open.
3. Confirm it works
# Ask the server what this key can do
curl -s https://nhmohio.com/api/mcp \
-H "Authorization: Bearer nhm_your_key_here" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The list that comes back is already filtered to the key's scopes and the account's plan, so it is the real answer to what this key can reach. If a tool you expected is missing, the key is missing a scope — mint a new one rather than rewording the prompt.
4. Ask it something
"Which of my domains have SSL certificates expiring in the next 30 days?"
"Why might email from my domain be going to spam?"
"Read the SEO results for every domain and give me one ranked fix list."
"Where do we drop out of the map pack for 'emergency plumber', and why?"
"Compare this month against last month and tell me what actually moved."Plans, scopes, and what a key can reach
A key carries resource scopes, and optionally the write scope on top of them. Both are checked on every call, and the plan is re-read on every request — an account that moves from Enterprise to Pro loses write access on its next call rather than whenever someone remembers to rotate the key.
Free and Starter
No API or MCP access.
The endpoint answers 401.
Pro
Read access. 95 tools.
Everything that reads stored data, across every scope you grant.
Enterprise
Read and write. All 176 customer tools.
Adds the write scope: start scans, spend credits, change saved data.
| Scope | What it unlocks |
|---|---|
| domains | Inventory, history, DNS and registrar events, uptime, DMARC reporting |
| scans | Domain scans, security checkups, dark-web monitoring |
| seo | On-page SEO, whole-site audits, and the entire Local SEO suite |
| accessibility | Audit-grade WCAG 2.2 AA evaluations |
| vulnscan | Vulnerability scans, ports, components, ownership verification |
| compliance | PCI, HIPAA, and cyber-insurance control records |
| status | Third-party service status |
| write | Required on top of any of the above for anything that changes state |
Adding a domain is a conversation
You do not have to know what SPF is, what an exposure scan does, or which of nine setup steps matter. Tell Claude the domain. It asks a few plain questions — does this domain send email, is it a business that serves a place, which mailboxes to watch for breaches, is there an address at the domain we can send an ownership link to — and then makes one call that adds the domain and sets all of it up. Nothing in that call spends credits. Anything that does is quoted first.
1. plan_domain_onboarding
Returns where the account stands — plan, whether active scanning is already cleared, any saved business profile, credits and prices — and the ordered questions to ask, each with why it matters and what the answer becomes.
2. The questions, in your words
Claude confirms what is already known instead of re-asking, and can read the site's homepage (discover_local_seo_details) to pre-fill the business name, city, phone and phrases for you to correct.
3. onboard_domain, once
Adds the domain, starts the add-time checks you chose, records whether it sends mail, files it under a group, saves the business and phrases, sends the ownership link, watches the addresses — and returns the setup guide so Claude can say what is left.
# The write, with every answer collected
onboard_domain {
domain: "siffrin.org",
sends_email: true,
exposure_monitoring: false, // one exposure scan now, none after unless asked
group: "Clients",
ownership_email: "admin@siffrin.org", // one click clears the whole account for active scans
local_business: {
business_name: "Siffrin",
location: "Canton, OH",
is_service_business: false,
keywords: ["tool repair canton", "pipe threader rental"]
}
}
→ steps: add_domain done · domain_settings done · local_business done · keywords 2 of 2 ·
ownership_email done
→ setup: first_scan running · alerts done · email_auth todo · ownership todo (link sent) · …Local search is the same shape. get_local_seo_quickstart says what is saved and the exact price of a first measurement; run_local_seo_quickstart saves the business and up to five phrases, confirms the city, then checks the map pack and the organic result for every phrase and reads the Google Business Profile — two credits a phrase plus two. Claude quotes that number and waits for a yes. In Claude Code the whole thing is /mcp__domainguard__onboard_domain and /mcp__domainguard__set_up_local_seo.
Claude checks our work
A scanner is a set of claims about the live world, and an assistant reading them can test many of those claims itself: resolve the DMARC record and compare it with what we quoted, fetch the page and read the headers, count the scripts. The server tells every connected assistant to do exactly that before it presents a finding as fact.
When what it verified disagrees with what we said — a false positive, a real problem we missed, a count or a record we got wrong, a check that errored — it files the difference with report_scanner_issue: what we reported, what is true, how it knows, what to change. The reports for one domain collect into a review, and submit_scanner_review sends us one message. That message goes to a person, and the fix comes back as a code change with a test.
The channel is deliberately narrow: Enterprise accounts, only from the account's own API keys (a dashboard session cannot file one), thirty reports a day, ten per domain, one message per review, repeats folded. Reporting never softens what you are told — Claude says what we said and what it found.
Prompts and resources the server carries
- onboard_domain
- the interview, start to finish
- set_up_local_seo
- profile, phrases, quote, first measurement
- review_domain
- read everything, verify, report, deliver fixes
- portfolio_triage
- one ranked list across every domain, spending nothing
- fix_email_deliverability
- SPF, DKIM, DMARC and the report stream, in order
Resources: domainguard://guide, domainguard://onboarding, domainguard://local-seo, domainguard://review — the same text as this page's guide, readable through the protocol.
How to use it well
That many tools is enough that the order you call them in decides whether a session is useful or expensive. These are the rules we give Claude, and the sequences that actually produce a fix list.
Ten ground rules
Start with list_domains
Almost every tool takes a numeric domain_id, and that is where the ids come from. Never guess one.
Add a domain by interview
plan_domain_onboarding returns the questions to ask — does it send email, is it a local business, which addresses to watch for breaches — and onboard_domain applies the answers in one call without spending anything.
Verify, then report
Before presenting a DNS, header, certificate or count finding, check it. When the scanner is wrong, report_scanner_issue says what it said, what is true and how you know; submit_scanner_review sends one message per domain.
Read before you scan
get_checkup_history, get_seo_results, get_vuln_scan_latest and get_local_rank_history return data already collected — free, instant, and usually enough.
Know what spends
Anything named run_, start_, check_ or verify_ does real work. So do get_domain_email_security, discover_domain_subdomains and get_scan_ownership_verification, which perform live lookups.
Quote before spending
get_local_seo_credits for the balance, get_local_seo_catalog for what an action costs and requires. Say the cost before the first paid call of a session.
Fresh request_key every paid run
Eight to a hundred characters of letters, digits, dots, underscores, and hyphens. Reuse one only to retry the identical request.
Recover, do not re-run
After a timeout, get_local_seo_operation with the same key returns the saved or in-flight result. A new key charges again.
Poll asynchronous scans
start_vuln_scan, start_site_audit and start_accessibility_audit return an id immediately and finish in the background. Leave real time between polls.
Confirm deletions in words
delete_domain erases a domain's whole history and cannot be undone. Name the exact thing and get a yes.
Say who applies the fix
The registrar, the DNS provider, the CMS, or Google Business Profile. A finding without an owner is not a fix.
Never invent a number
Scores, positions, volumes and prices come from tool output. If a tool did not return it, say it is not measured.
Playbooks
Each one is a sequence that ends in something the user can act on. Every one of them opens with free reads.
Triage the whole portfolio
The first session on an account. Costs nothing — every call here reads data already collected.
list_domains ├─ get_domain_history → getting worse, or always been this way? ├─ get_checkup_history → what is broken, with severity └─ get_seo_results → titles, descriptions, headings, violations get_domain_events → anything hijack-shaped across the account get_downtime_events → was anything actually down
Report one ranked list across the portfolio, severity first and then by how many domains share the finding. Fixing the one misconfiguration that appears on nine sites beats fixing nine unrelated things, and that ordering is only visible when you read the whole account at once.
Make a page rank — the technical loop
The order matters. A page cannot rank if it cannot be crawled, cannot be trusted, or cannot be read.
get_seo_results → title, meta description, H1 and heading structure,
accessibility violations that also block crawling
get_checkup_history → HTTPS, redirects, response headers, exposed files
get_seo_history → has the score been moving, and in which direction
↓ hand the user a specific edit list, then wait for them to publish
run_seo_scan → re-score
get_seo_results → confirm the finding cleared
get_seo_history → confirm the number movedRead the SEO result for what it names, not for its score. A missing meta description, a title duplicated across a template, two H1s, a skipped heading level — each of those is a concrete edit with an owner. “Score is 71” is not. Batch the edits and scan once: free accounts get one manual scan a day.
Audit the whole site, not one page
A crawl of the sitemap or same-site links, checking every discovered page. Asynchronous — start, poll, read.
start_site_audit { domain_id, max_pages } → audit_id
get_site_audit_status { audit_id } → poll; minutes, not seconds
get_site_audit_results{ audit_id } → page-level findingsCluster before reporting. Three hundred crawled pages do not produce three hundred problems — they produce perhaps six template defects repeated three hundred times. Sort by pages affected multiplied by severity, name the template or page type where it can be inferred, and the fix list goes from unusable to a morning's work.
Get into the local map pack
The highest-leverage sequence for a business that serves a place. Set up once, then measure and diagnose.
Setup, once per domain
get_local_seo_profile → is there a profile at all?
save_local_seo_profile → business_name, location ("Akron, OH"),
is_service_business, radius_miles, and
street_address + phone if you will ever
want citation checks
add_local_keyword_target → phrases a customer would actually type,
up to 25 per domain
Measure
get_local_rank_history → free; is there recent data already?
get_local_seo_credits → check the balance before spending
check_local_ranks → live positions, local pack. For the organic
column too, or one phrase only, use
run_local_seo_action rank_check with
surfaces and keywords
run_geogrid_scan → where the ranking holds across the area
Diagnose
run_local_seo_action profile_health → what the profile is missing
run_local_seo_action citations → name/address/phone consistency
run_local_seo_action competitor_gap → who beats you, and on what
run_local_seo_action reviews → recent and unanswered reviewsHow to read a geogrid. A business ranks best at its own pin and decays outward. Strong at the centre and dead three miles out is a proximity problem — the answer is location-specific content and citations, not more keywords. Weak everywhere, including at the pin, is a profile or trust problem: go to profile health and citations first, because no amount of content fixes a profile Google does not trust.
A saved profile is the prerequisite for both rank checks and map scans. The service radius defaults to three miles and tops out at five hundred.
Find phrases worth targeting
Research before tracking, and a baseline before any work starts.
get_local_seo_catalog → inputs and cost
run_local_seo_action keyword_opportunities → local searches to consider
run_local_seo_action keyword_search_volume → is there real demand
run_local_seo_action keywords_for_site → what this domain already
associates with
run_local_seo_action keywords_related → adjacent phrases
run_local_seo_action keyword_trends → rising, or dying
↓
add_local_keyword_target → the survivors
check_local_ranks → the baseline, before anything changesTake the baseline before the user changes anything. Without it there is nothing to compare against in three months, and the work cannot be shown to have worked — which is usually the thing that decides whether it continues.
Beat a specific competitor
Three reads of the same rival from different angles, turned into three things to do this month.
run_local_seo_action competitor_gap → ranking, review, citation gap run_local_seo_action backlinks_gap → links they have, you do not run_local_seo_action local_authority → your own signals in one score
Convert the gap into three actions, not thirty. Link gaps are the slowest to close and the most durable once closed; review count and profile completeness are the fastest and the most visible to a searcher deciding between two names.
Be visible in AI answers
Increasingly the click never happens — the answer does. That is measurable, and it is a different surface from the ten blue links.
run_local_seo_action ai_visibility → how often answers surface this site run_local_seo_action serp_ai_mode → the AI answer, with references run_local_seo_action ai_mentions → which brands and sources get named run_local_seo_action ai_top_sources → what these systems rely on run_local_seo_action ai_top_pages → the pages they surface most run_local_seo_action ai_compare → you versus competitors, by phrase
The actionable output is almost always the cited sources, not the answer text. When the same three directories or publications are cited across every phrase in a category, being present and accurate on those three is the work — and it is work that pays off in ordinary search too.
Reputation and links
The two cheapest wins in local search, and the warmest outreach list that exists.
run_local_seo_action reviews → recent reviews, owner replies run_local_seo_action review_velocity → rate of arrival, not total run_local_seo_action multi_platform_reviews → beyond Google run_local_seo_action reputation_audit → quality, quantity, recency run_local_seo_action backlinks_summary → the link signals you have run_local_seo_action backlinks_gap → what competitors have run_local_seo_action brand_mentions → named, but not linked
Unanswered reviews are visible to searchers and free to fix. Unlinked brand mentions are the warmest outreach list there is — the site already decided to mention the business, so the ask is small.
Email that reaches the inbox
Not a ranking factor, but the fastest way to lose a customer who already found you — and the most common thing a monitored portfolio gets wrong.
get_domain_email_security { domain_id } → live SPF, DKIM, DMARC, MX,
MTA-STS, TLS-RPT
get_dmarc_setup_status { domain } → the authoritative checklist
get_dmarc_summary { domain } → 30-day volume and alignment
get_dmarc_diagnostics { domain } → ranked, with sender attributionRead the diagnostics before recommending a policy change. Moving a domain to p=reject while a legitimate sender is failing alignment stops that sender's mail on the next send. The diagnostics name which sender, which is the whole reason to look before you leap.
Security findings that are also search problems
Mixed content, an expired certificate, a blacklist entry, a compromised page. These belong in the SEO report, not a separate one.
get_checkup_history → stored and free; start here
run_domain_checkup { domain } → live. Your own domain gets the full
check; any other hostname gets only
the passive checks
get_vuln_scan_latest → most recent completed scan
start_vuln_scan → quick | standard | port-scan |
deep | extreme
get_vuln_scan_results { scan_id } → poll for findings
Active scanning first needs proof of control:
get_scan_ownership_verification → creates or returns the challenge
↓ user publishes the DNS record or file
verify_scan_ownership → records the proofOwnership verification is not paperwork. Deep and extreme profiles send real traffic at a real server, and the proof is what separates scanning your own site from scanning someone else's.
Prove the work worked
Every engagement should end here, against the baseline taken at the start.
get_seo_history → score movement over the period get_local_rank_history → position per phrase, per check get_geogrid_scans → the map before, and the map after get_domain_history → everything else that changed get_uptime_snapshots + get_downtime_events → availability
Cite the dates, and say plainly where nothing moved. Positions move for reasons nobody controls; a report claiming every change was caused by the work is the one nobody believes twice.
Tool reference
All 180 tools: 99 that read stored data and 81 that change something, run a live lookup, or spend credits. 4 of the reads are operator-only, so a customer key can be offered at most 176. A key is only offered the subset its scopes and plan allow, so the list your client shows may be shorter than this one.
Fields marked ? are optional. Every tool runs through the same ownership checks as the dashboard, so a key reaches its own account and nothing else.
Domains, DNS, and deliverability
scope: domainsThe inventory and everything recorded about it. list_domains is the entry point to the whole server — its numeric ids are what the other tools take.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| list_domains | read | detail? | Every domain on the account, one compact row each: health score, last scan and scan state, SSL and registration expiry, SEO score, paused checks. detail: true returns the full stored scans. Start here. |
| get_domain_history | read | domain_id | Recorded results for one domain over time — when something regressed and by how much. |
| get_dns_change_events | read | — | DNS changes across the account, so a new change can be told apart from a long-standing finding. |
| get_domain_events | read | — | Hijack Guard events with severity and lifecycle: DNS, registrar, certificate, and hosting changes. |
| get_uptime_snapshots | read | — | Recent uptime probe observations across the portfolio. |
| get_downtime_events | read | — | Recorded outage windows and whether they recovered. |
| get_dmarc_summary | read | domain | Thirty-day aggregate report volume and alignment rate for one monitored domain. |
| get_dmarc_diagnostics | read | domain | Ranked deliverability diagnostics joining DNS, reports, and sender attribution. |
| get_dmarc_setup_status | read | domain | The authoritative SPF, DKIM, DMARC, report-arrival, and inbox-routing checklist. |
| add_domain | write | domain | Start monitoring a new domain. Returns 403 once the plan's domain limit is reached. |
| update_domain | write | domain_id, domain_group?, notes?, ignore_mail?, ignore_autoscan? | Change one domain's group, notes, or which checks it skips. Only the fields passed are changed. |
| delete_domain | destructive | domain_id | Permanently removes the domain and its entire scan history. Re-adding does not recover it. |
| get_domain_email_security | write | domain_id | Fresh SPF, DKIM, DMARC, MX, MTA-STS, and TLS-RPT evidence. Performs live DNS lookups, so it counts as a write. |
| discover_domain_subdomains | write | domain_id | Passive subdomain discovery from certificate transparency and DNS. Does not probe the hosts it finds. |
Scans, checkups, and dark web
scope: scansSecurity checkups and breach monitoring. The stored history is almost always enough — reach for a live run only when something has just changed.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_checkup_history | read | domain_id | Past security checkups with every individual finding and its severity. The richest single answer to “what is wrong with this site”. |
| get_dark_web_status | read | — | Cached breach-monitoring results for the account, with no upstream lookup. |
| run_domain_scan | write | domain_id | Domain health, security checkup, and single-page SEO in one pass. Does not run vulnerability, whole-site, dark-web, or audit-grade accessibility scans. |
| run_domain_checkup | write | domain | A live checkup. On your own domain this includes the exposed-file probe; on any other hostname only the passive checks run. |
| run_dark_web_scan | write | — | Refresh every monitored email address against the breach index. Spends the manual-scan allowance. |
SEO — one page and the whole site
scope: seoWhat a page says about itself, and what a crawl of every page finds. The site audit is asynchronous: start it, poll the status, then read the results.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_seo_results | read | domain_id | The latest SEO and accessibility scan: page titles, meta descriptions, heading structure, and violations. |
| get_seo_history | read | domain_id | SEO scores over time, so a change can be shown to have moved the number. |
| get_site_audit_status | read | audit_id | Progress of a whole-site crawl. |
| get_site_audit_results | read | audit_id | Page-level findings from a whole-site crawl. |
| get_seo_intent_conflicts | read | domain_id | Which pages compete for one search, with the page to keep and the steps for the other. Read from the newest crawl; free. |
| run_seo_scan | write | domain_id | Re-score a site after publishing changes. Free accounts get one manual scan a day; a second returns 429. |
| start_site_audit | write | domain_id, max_pages? | Crawl the sitemap or same-site links and check every discovered page. Returns an audit id immediately. |
Local SEO — rankings, maps, reviews, competitors
scope: seoThe suite behind local search. A saved business profile is the prerequisite for everything that measures a position, and the catalog is the authority on what each action needs and costs.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_local_seo_profile | read | domain_id | The saved business profile: name, location, service radius, and targeting mode. |
| get_local_keyword_targets | read | domain_id | The phrases currently tracked for this domain. |
| get_local_rank_history | read | domain_id | Recorded positions for each tracked phrase on each past check. |
| get_geogrid_scans | read | domain_id | Past map-grid scans showing where across the service area rankings hold and where they fall away. |
| get_local_seo_catalog | read | — | All 33 catalogued actions with their exact inputs, prerequisites, price basis, and caching rules. Call this before running one. |
| get_local_seo_operation | read | request_key | Recover a paid action after a timeout instead of paying for it twice. |
| get_local_seo_credits | read | — | Remaining rank-check and map-scan credits on the account. |
| save_local_seo_profile | write | domain_id, business_name, location, targeting?, radius_miles?, is_service_business?, street_address?, phone? | Create or update the business profile. Required before rank checks and map scans will run. |
| add_local_keyword_target | write | domain_id, keyword, targeting? | Track a phrase. Up to 25 per domain; adding the same phrase twice does nothing. |
| delete_local_keyword_target | destructive | keyword_id | Stop tracking a phrase. The rank history already recorded is kept. |
| check_local_ranks | write | domain_id, request_key | Live positions for every tracked phrase, measuring the local pack. Costs one credit per phrase per surface. |
| run_geogrid_scan | write | domain_id, keyword, grid_size?, request_key | Check one phrase from many points across the service area on a 5×5, 7×7, or 9×9 grid. |
| run_local_seo_action | write | action_id, request_key, input | Run any action from the catalog — reviews, citations, keyword research, competitor gap, backlinks, AI visibility, full audits. |
Accessibility
scope: accessibilityAudit-grade WCAG 2.2 AA evaluation in a real browser. Separate from the accessibility violations that come back with an ordinary SEO scan, and separately scoped so a key can have one without the other.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| quote_accessibility_audit | read | domain_id | Cost, available credits, page cap, and whether an audit can start right now. |
| list_accessibility_audits | read | domain_id?, limit? | Recent evaluations, optionally narrowed to one domain. |
| get_accessibility_audit | read | audit_id | Pages, axe findings, WCAG rollups, manual-review coverage, and regression data for one audit. |
| start_accessibility_audit | write | domain_id, request_key, max_pages?, viewport? | Queue a browser evaluation. Spends accessibility credits, so the request key matters. |
Vulnerability and exposure
scope: vulnscanActive scanning needs proof that the account controls the domain. Get the challenge, publish it, verify it, then scan.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_vuln_scan_latest | read | domain_id | The most recent completed scan with its findings and severities. |
| get_vuln_scan_results | read | scan_id | Findings for one scan by id. This is how you poll a scan you just started. |
| get_vuln_scan_history | read | domain_id | The last twenty scans with status, profile, timing, and severity summary. |
| get_vulnerability_assets | read | domain_id | Every public hostname discovered for a domain and the finding counts attached to each. |
| get_open_ports | read | domain_id | Per-host port, service, banner, version, and risk observations. |
| list_lookalikes | read | domain_id | Domains registered by other people to imitate yours, each with a risk band and the evidence behind it — plus how much of the variation space the scan actually covered. |
| get_lookalike_events | read | domain_id | What changed about those impostors over time: one appearing, one gaining the ability to receive email, one becoming more dangerous. |
| run_lookalike_scan | write | domain_id | Generate variations of a domain and check which are registered. Takes up to a minute; prefer the stored list unless a fresh check is wanted. |
| set_lookalike_verdict | write | domain_id, lookalike, verdict? | Say what one impostor is so it stops being reported: ours, unrelated, or known. Omit the verdict to clear it. |
| get_observed_components | read | — | The portfolio's technology inventory with version confidence where a version was actually observed. |
| get_scan_ownership_verification | write | domain_id | Return the DNS, file, or email proof required before active scanning, creating a challenge if none exists. |
| verify_scan_ownership | write | domain_id | Re-check the published proof and record verification when it is found. |
| start_vuln_scan | write | domain_id, scan_type?, scan_profile?, target_hosts? | Quick through extreme, optionally limited to selected hosts. Returns a scan id; the scan runs in the background. |
Compliance evidence
scope: compliancePCI, HIPAA, and cyber-insurance controls. These records are audit evidence — notes should quote the account owner's own wording rather than paraphrase it.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_compliance_records | read | — | The compliance checklist with each control's current status and notes. |
| update_compliance_item | write | record_id, status, notes? | Set one control to unanswered, met, partial, not met, or n/a. "Not met" is a recorded gap, not a blank. Uses the record id, not the item id. |
Uptime and dependencies
scope: statusThe live status of third-party providers — the fastest way to separate a problem with your site from an incident at your host.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_status_services | read | — | Current status of the third-party providers this account tracks. |
Alerts
scope: domainsThe account's alert feed. Alerts are read through the domain tools; this is the one write against them.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| mark_alerts_read | write | ids (optional) | Mark every open alert as read, or only the ids given. Read alerts cannot be reopened; the History log keeps the record. |
Tell us when we are wrong
scope: scansThe assistant reading these results is the best reviewer the scanner has. A false positive, a missed problem, a number that contradicts what it can verify — report it, and the operators get one message per domain review. Enterprise only; 30 reports a day.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| report_scanner_issue | write | domain_id, category, title, detail | One thing the scanner got wrong: what it said, what is true, how you know, what to change. Collects into a review of that domain. |
| submit_scanner_review | write | domain_id | Close the review and send it to the operators as one message. Call it once per domain, when you are done. |
Operator reads (administrator only)
scope: adminWhat the nightly fix routine reads. The admin scope is not one a customer key can carry: it is minted only from the administrator's own signed-in session and reaches only these four reads.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| list_error_events | read | days, source (optional) | The Worker's own error log, grouped by fingerprint with occurrence counts and the accounts affected, plus the newest raw rows. |
| get_self_check_status | read | — | Every self-check invariant's last verdict, failing first, and when each cron scope last completed a pass. |
| list_scan_failures | read | days (optional) | Domain scans, exposure scans, crawls and accessibility audits that did not finish, with their reason, plus exposure scans stuck for over two hours. |
| list_scanner_reports | read | days, status (optional) | What customers' assistants reported wrong with the scanner, open ones first — the claim, the evidence, the suggested fix, who sent it. |
Guided onboarding and setup
scope: domainsThe way in. plan_domain_onboarding returns the interview; onboard_domain applies the answers in one write without spending; the setup guide says what is left. add_domain still works for a bare add.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| plan_domain_onboarding | read | domain? | Call this FIRST whenever the user wants to add or set up a domain. |
| onboard_domain | write | domain, sends_email?, lookalikes?, subdomains?, vulnerabilities?, accessibility?, exposure_monitoring?, group?, notes?, scan_client_id?, local_business?, ownership_email?, dark_web_emails? | The one write that puts a domain on the account fully configured, from the answers plan_domain_onboarding told you to collect. |
| get_domain_setup_guide | read | domain_id | The server-computed checklist for one domain: first scan, alerts, email authentication, DMARC reports, ownership proof, monitoring, local business, lookalikes, vulnerability scan, exposure monitoring — each done, todo, running, blocked by plan, skipped or not applicable, with how to finish it. |
| skip_domain_setup_step | write | domain_id, step, skipped? | Record that a setup step does not apply to this domain (or un-skip it). Only optional steps can be skipped; the required ones cannot. |
| add_domains_bulk | write | domains | Add up to 100 domains in one call. Returns which were added and which were skipped (already here, invalid). |
Account: alerts, settings, groups, clients
scope: domainsThe account-wide plumbing: the alert feed and history, alert and scan settings, group labels, and the client records a consultancy files domains under.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| list_alerts | read | limit?, before? | The alert feed: every alert the account was sent, newest first, with the domain, event type, channel, and whether it has been read. Read it when the user asks "what have you told me about" or before mark_alerts_read. |
| get_alert_activity | read | limit?, before? | The History log: alerts, scans, changes and events across the account merged into one timeline. Broader than list_alerts, which is only what was sent. |
| get_account_settings | read | — | The account's alert and scanning settings: which alerts are on, how often domains are scanned, and the thresholds. Every alert lands in the alert feed and is pushed to the account's iPhone. Read before changing anything so you change only what the user asked. |
| update_account_settings | write | scan_frequency?, notify_expiry_days?, notify_ssl_days?, security_threshold?, notify_expiry?, notify_ssl_expiry?, notify_dns_issues?, notify_security_issues?, notify_dns_record_changes?, notify_status_push?, notify_uptime_push?, notify_dmarc_alerts?, notify_scan_complete?, notify_scan_results_email? | Change the account's alert and scan settings. Pass only the fields the user asked to change; everything else is kept. Alerts go to the alert feed and the account's iPhone; there is no alert email. notify_scan_results_email only emails the report of a scan the user started. |
| list_domain_groups | read | — | The group labels used to organise the portfolio (clients, brands, regions) with how many domains sit in each. |
| create_domain_group | write | name | Create an empty group label. Assigning domains to it is assign_domains_to_group; update_domain also takes a group name. |
| delete_domain_group | destructive | group_id | Remove a group label. The domains in it are kept and become ungrouped. |
| assign_domains_to_group | write | domain_ids, group_name? | Put up to 50 domains under one group name in a single call, creating the group if needed. Pass an empty group_name to ungroup them. |
| list_scan_clients | read | — | Clients are the optional grouping a consultancy uses to hold domains and network targets per customer. Returns each with its id. |
| create_scan_client | write | name | Create a client record to assign domains and IP targets to. Names are unique per account. |
| delete_scan_client | destructive | client_id | Remove a client record. Domains and targets assigned to it are kept, unassigned. |
| list_support_conversations | read | — | The account's support threads with NHM, newest first, with status. |
| get_support_conversation | read | conversation_id | One support thread in full, including replies from NHM. |
| reply_to_support_conversation | write | conversation_id, message | Add the user's message to an existing support thread. Opening a new thread needs the dashboard; say so if there is none. |
| update_support_conversation | write | conversation_id, status?, archived? | Set a thread's status to open or closed, or archive/unarchive it. |
Hijack Guard: change events
scope: domainsDNS, registrar, certificate and hosting changes, per domain and account-wide, and the answer to each: ours (authorize), not ours (deny), or just moving it along.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_domain_hijack_events | read | domain_id | Hijack Guard events for one domain: DNS, registrar, certificate and hosting changes with severity and lifecycle. get_domain_events is the account-wide view. |
| get_domain_event | read | event_id | One Hijack Guard event in full: what changed, from what to what, when, and what has been done about it. |
| act_on_domain_event | write | event_id, action | Move a Hijack Guard event through its lifecycle. authorize means "we made this change" and promotes it to the new baseline so it stops alerting; deny means "we did not" and keeps it flagged. |
Email: DMARC reports, senders, managed records
scope: domainsThe aggregate-report stream behind the DMARC score: who is sending as the domain, whether they align, the report address to publish, and the option to let DomainGuard publish and advance the records.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| list_dmarc_reports | read | domain?, limit?, offset? | The aggregate reports received, newest first, optionally for one monitored domain. Each names the reporter, the window, the published policy and the message counts. |
| get_dmarc_senders | read | domain, days? | Sender attribution from the aggregate reports: each sending source, its volume, and whether it passed SPF and DKIM alignment. The list to read before tightening a DMARC policy. |
| get_dmarc_inbox | read | — | The rua= address DomainGuard receives aggregate reports at for this account, whether it is provisioned, and any delivery error. This is the address to put in the DMARC record. |
| rotate_dmarc_inbox | write | — | Issue a new report address and retire the old one. Every domain's DMARC record then has to be updated, so confirm with the user; there is a cooldown between rotations. |
| get_managed_email_auth | read | — | Which domains have delegated their DMARC or SPF record to DomainGuard to publish and maintain, and whether the delegation is live. |
| enable_managed_email_auth | write | domain, service, source_record? | Start managing one record: returns the CNAME the user publishes (for DMARC) or takes the SPF record to flatten and serve. After they publish, check_managed_email_auth confirms it. |
| disable_managed_email_auth | write | domain, service | End a delegation. The user must publish their own record again or the domain will have none. |
| check_managed_email_auth | write | domain | Look up DNS to confirm the delegation CNAME is published and serving. A live lookup; call it after the user says they published. |
| set_managed_dmarc_policy | write | domain, action | For a managed DMARC record: advance moves the policy one step towards reject (none → quarantine → reject) when the reports show every legitimate sender aligned; revert steps it back. |
Scan clearance and authorisation
scope: vulnscanActive scanning needs one proven domain per account. The email link is the easiest proof; the operator queue covers a client's domain the account cannot prove.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_scan_clearance_status | read | — | Account-wide view of ownership proof: how many domains carry a current proof and which. |
| verify_scan_ownership_bulk | write | domain_ids? | Re-check the published DNS or file proof for the domains given (or every domain when omitted) and record what is found. |
| request_ownership_email_link | write | domain_id, address | The easiest ownership proof: send a one-click link to an address AT the domain (postmaster@, admin@, the owner's own mailbox — never a gmail or yahoo address). When they click it the whole account is cleared for active scanning for 90 days. |
| request_scan_authorization | write | domain_id, reason, contact_email? | For a domain the account cannot prove ownership of but is authorised to test (a client engagement), file a request with the reason and a contact; an operator approves or declines. Check get_scan_authorization for the answer. |
| get_scan_authorization | read | domain_id | The latest operator authorisation for one domain and whether it is currently active. |
| list_scan_authorizations | read | limit?, offset? | Every authorisation request the account has filed, newest first, with its status. |
Lookalike controls
scope: vulnscanTurning the watch on and off, the evidence pack for a filing, and how lookalike alerts are delivered.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| set_lookalike_watch | write | domain_id, enabled | Continuous impostor-domain monitoring for one domain. Turning it on needs the plan capability; turning it off is always allowed. |
| get_lookalike_evidence | read | domain_id, lookalike | Everything recorded about one lookalike domain — registration, DNS, mail capability, certificates, timeline — assembled for a registrar abuse report or a takedown filing. |
| act_on_lookalike_event | write | event_id, action | Mark one lookalike change event as seen (acknowledge) or dealt with (resolve). Event ids come from get_lookalike_events. |
| get_lookalike_alert_settings | read | — | Whether lookalike alerts are on. When they are, a lookalike that needs action lands in the alert feed and is pushed to the account's iPhone. |
| update_lookalike_alert_settings | write | push | Turn lookalike alerts on or off. On means a feed entry and a push when a lookalike gains mail or turns into an active threat. |
Software, advisories, dismissals
scope: vulnscanWhat the portfolio runs on, by vendor and by product; the KEV and CVE alerts raised against it with an exposure verdict; the products the account chose to track; and the findings it has silenced with a reason.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| list_vuln_dismissals | read | kind? | Findings, inventory readings and software alerts the account has permanently silenced, with the reason given. |
| dismiss_vuln_finding | write | kind, target_key, reason, domain_id?, label?, note? | Silence one thing for good, with a reason: false_positive, accepted_risk, fixed or not_applicable. target_key is the finding's stable key from the scan result (what it IS, not its row id), so the dismissal survives re-scans. |
| restore_vuln_dismissal | write | dismissal_id | Remove a dismissal so the finding, component or alert is reported again. Use the id from list_vuln_dismissals. |
| list_software_vendors | read | — | The observed technology inventory rolled up by company: every vendor seen across the account's domains with component counts and open advisories. |
| get_software_vendor | read | vendor | Every component, version and domain observed for one vendor, with its advisories. vendor is the lowercase slug from list_software_vendors. |
| get_vulnerability_feed | read | — | The global feed of recently ingested vulnerabilities (CISA KEV and NVD), not filtered to this account. Use get_software_advisory_history for one product. |
| get_software_advisory_history | read | product, vendor? | Every known advisory for one software product, matched the same way the alert ingest matches it. Use it when a component has no status feed: "CVEs and KEVs only". |
| list_tracked_software | read | — | The software products the account watches for new vulnerabilities, with alert preferences and open alert counts. |
| search_software_catalog | read | q?, category? | Find a product to track by name or category. Returns slugs to pass to track_software. |
| get_software_catalog | read | — | Every product in the catalog. Large; prefer search_software_catalog. |
| get_software_catalog_categories | read | — | The categories the software catalog is organised by. |
| track_software | write | app_name, app_slug?, vendor?, sources?, severity_filter? | Start watching one product for new KEV and CVE entries. Each new entry lands in the alert feed and as a push when it is found; severity_filter narrows what counts. |
| update_tracked_software | write | app_id, is_active?, severity_filter?, sources? | Change which severities and feeds one tracked product alerts on, or pause it with is_active false. Only the fields passed change. |
| untrack_software | destructive | app_id | Remove a tracked product and its alerts. Confirm the product name first. |
| list_software_alerts | read | status?, source?, limit?, offset? | KEV and CVE alerts raised for tracked and observed software, each with severity and an exposure verdict: confirmed (the vulnerable version was observed), possible, not_affected or unconfirmed. Lead with confirmed ones. |
| get_software_alert_summary | read | — | Counts of unread, critical, KEV and confirmed-exposure software alerts, per tracked product and in total. |
| update_software_alert | write | alert_id, alert_status | Set the status of one software alert. remediated records that the fix was applied; dismissed means it will not be. |
IP and network targets
scope: vulnscanDaily port scans of public IPs and small ranges the account owns or is authorised to test. Adding one records the attestation.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| list_ip_targets | read | — | The IP addresses and CIDR ranges the account port-scans daily, with their client and last result. |
| add_ip_target | write | target, attestation, label?, client_id?, daily_scan? | Start daily port scanning of a public IP or small CIDR. The user must confirm they own or are authorised to scan it; pass attestation true only after they say so in words. |
| update_ip_target | write | target_id, client_id?, daily_scan? | Move a target to another client or turn its daily scan on or off. |
| delete_ip_target | destructive | target_id | Stop scanning a target and remove it with its history. Confirm first. |
| list_ip_target_scans | read | target_id | Past port scans of one target with the open ports and services seen each time. |
| scan_ip_target | write | target_id | Run the port scan of one target immediately instead of waiting for the daily run. Sends traffic to the target. |
Dark web: addresses
scope: scansWhich addresses are watched in breach data, and one breach in detail. New hits land in the alert feed and are pushed to the app.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_dark_web_breach | read | breach | Details of one breach that a monitored address appeared in: what was exposed, when, and which monitored addresses it touched. |
| add_dark_web_email | write | email, cadence? | Watch one address in breach data. Only addresses at the account's own domains or its verified sign-in address; cadence weekly and daily need a paid plan. |
| remove_dark_web_email | destructive | email_hash | Remove one monitored address by its hash, as get_dark_web_status lists it. |
Deep crawls, single-URL scoring, reports
scope: seoCrawl chosen pages rather than the whole site, score any public URL on demand, and send the branded report.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_seo_policy | read | — | The account's SEO allowances: manual scans per day, crawl page caps, whether deep crawls and reports are on the plan. |
| list_manual_url_scans | read | — | Past one-off SEO scans of individual URLs saved on the account. |
| fetch_sitemap | write | domain | Read the sitemap of one monitored domain and return its URLs — the input for start_deep_seo_job. A live fetch of the site. |
| start_deep_seo_job | write | domain_id, domain, pages, sitemap_url? | Crawl and score a list of specific pages on a monitored domain (from fetch_sitemap or the user). The plan caps how many; the response says if the list was trimmed. |
| list_deep_seo_jobs | read | — | Every queued, running and finished deep crawl on the account with progress. |
| get_deep_seo_job | read | job_id | Progress and status of one crawl job by id. |
| cancel_deep_seo_job | write | job_id | Stop a queued or running crawl. Pages already scored are kept. |
| get_deep_seo_scans | read | domain_id | The finished deep crawls for one domain with their scores and page counts. Each has a scan id for get_deep_seo_scan_pages. |
| get_deep_seo_scan_pages | read | scan_id | Every page in one deep crawl with its title, description, headings, score and defects. |
| check_page_seo | write | url | Fetch any public page and return its on-page SEO checks: title, description, headings, images, links, canonical. Works on any URL, not only monitored domains. |
| assess_website | write | url | A quick combined read of any public URL: performance, accessibility and SEO signals in one result. A live fetch. |
| score_page_accessibility | write | url | A quick static accessibility score of any public page. Not audit grade — start_accessibility_audit is. |
| score_page_performance | write | url | A quick performance read of any public page: weight, requests, render-blocking resources. A live fetch. |
| email_seo_report | write | domain_id, to, note? | Send the branded PDF SEO report for one monitored domain to an address, with an optional note. Needs a completed SEO scan. |
| get_report_branding | read | — | The white-label branding applied to emailed reports (company name, logo, accent colour, footer) and whether the plan allows changing it. |
| save_report_branding | write | company_name?, logo_url?, accent_hex?, footer_text? | Set the white-label branding on emailed reports. Enterprise only. |
Local SEO setup: quick start, discovery, quotes
scope: seoThe short road into local search. Read the quick start, let discovery pre-fill the business from the homepage, confirm the city, quote, then one run takes the first local-pack and organic measurement.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_local_seo_quickstart | read | domain_id | One read that shows where a domain stands in local search setup: the saved business (name, city, whether the city was confirmed), tracked phrases, suggested phrases from the homepage when nothing is saved yet, the newest local-pack and organic positions per phrase, the last Google Business Profile read, the credit balance, and the exact price of a run. |
| run_local_seo_quickstart | write | domain_id, business_name, location, keywords, request_key | The whole first step in one call: saves the business name and city, saves up to 5 phrases, confirms the city against the gazetteer, then checks the local-pack AND organic position for every phrase and reads the Google Business Profile. |
| discover_local_seo_details | write | domain_id | Read one monitored domain's public homepage and return candidate business name, city, street address, phone, whether it looks like a service-area business, and up to 12 candidate phrases — each with its source and a confidence. |
| verify_local_seo_location | write | domain_id, location | Check a "City, ST" against the rank-data gazetteer before saving it, and get the resolved name or close matches. Free. |
| get_local_seo_access | read | — | Says whether this account can use the Local SEO suite and on which plan. Check it before promising a rank check. |
| quote_local_ranks | read | domain_id, keywords?, surfaces? | What check_local_ranks or a rank_check action would cost for this domain: per phrase per surface, against the current balance. Free. |
| quote_geogrid_scan | read | domain_id, keyword, grid_size?, location? | What run_geogrid_scan would cost for one phrase and grid size, and whether the balance covers it. Free. |
| get_geogrid_entitlement | read | domain_id | Free map scans remaining this period for a domain, and what more would cost. |
| quote_local_seo_endpoint | read | domain_id, endpoint | The credit cost of one catalogued endpoint for a domain before running it. get_local_seo_catalog gives the price basis; this gives the number. |
| search_locations | read | q, state?, country?, limit? | Find the exact "City, ST" the rank data covers, from a partial name. Use it when verify_local_seo_location or the quick start says the city was not found. |
| list_covered_locations | read | prefix?, state?, country?, limit?, offset? | Page through the cities the rank data covers, optionally by state or name prefix. |
| get_local_keyword_ideas | read | domain_id, limit? | Phrase ideas already gathered for a domain by earlier keyword research, without spending anything. |
| get_backlink_snapshots | read | domain_id | Past backlink summaries recorded for a domain, so link growth can be shown over time without a new lookup. |
| get_local_seo_audit_history | read | domain_id, kind? | Saved local, citation and reputation audits for a domain — reopening one is free. |
| list_local_seo_reports | read | domain_id | Saved profile-health, reviews, AI-visibility, competitor-gap and citation reports for a domain. Reopening is free; the catalog actions refresh them. |
| quote_local_seo_report | read | domain_id, type, refresh? | Whether a report of this type is already saved (free to reopen) and what a refresh would cost. |
Accessibility review and export
scope: accessibilityThe human half of an audit: record the manual WCAG judgements, close the audit, export the evidence pack.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| record_accessibility_manual_review | write | audit_id, wcag_criterion, status, notes?, evidence_reference? | Record the human judgement on one WCAG criterion the automated audit cannot decide. Only after the automated checks finish. |
| complete_accessibility_audit | write | audit_id | Mark an audit complete once the manual reviews are recorded. It can then be exported. |
| export_accessibility_audit | read | audit_id | The full audit as a JSON evidence pack: pages, findings, manual reviews, WCAG rollups. Automated results do not establish conformance; say so when handing it over. |
Providers and subscriptions
scope: statusWhich providers the site depends on and which the account is subscribed to.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| get_status_incidents | read | — | Open incidents across every tracked provider right now. Read with get_status_services when a user reports an outage. |
| get_status_history | read | limit?, offset? | Past status changes across tracked providers, newest first. |
| list_status_subscriptions | read | — | Which third-party providers this account is subscribed to for status alerts, and at what level. |
| get_status_subscription_suggestions | read | — | Providers the account's domains were observed depending on (host, DNS, email, CDN) but is not yet subscribed to. Offer these during onboarding. |
| update_status_subscriptions | write | subscriptions | Subscribe or unsubscribe from providers. Each entry names a service_id from get_status_services and a notify_level: all, major, or none. |
| subscribe_all_status_services | write | — | Subscribe to every provider the account's domains depend on, in one call. |
| unsubscribe_all_status_services | write | — | Clear every provider subscription. Confirm first. |
| suggest_status_service | write | status_page_url, service_name?, notes? | Ask for a provider status page to be added to the catalogue. Needs its https:// status page URL. |
Compliance seeding
scope: complianceSeed or bulk-update the control list.
| Tool | Access | Inputs | What it returns |
|---|---|---|---|
| initialize_compliance_records | write | items | Create compliance control records from a list of items (item_id, framework, category, requirement, optional status and notes). Existing records are kept. |
| bulk_update_compliance | write | items | Set status and notes on many controls in one call, by item_id. Same status vocabulary as update_compliance_item, same rule: never write not_met for a control nobody assessed. |
The Local SEO catalog
The local-search tools cover setup and measurement directly. Behind run_local_seo_action sit another 33 catalogued actions, and get_local_seo_catalog is the authority on all of them: exact required inputs, prerequisites, price basis, and whether a saved result can be reopened for free. Call it before running an action rather than assuming a shape — the catalog is the contract, and it can gain entries between deploys of this page.
# The shape of a catalogued run
run_local_seo_action {
action_id: "competitor_gap", // from get_local_seo_catalog
request_key: "dg-compgap-20260829-x1", // fresh, 8-100 chars [A-Za-z0-9._-]
input: { domain_id: 12 } // exactly the catalog's fields
}| Area | Action ids |
|---|---|
| Profile and reputation | profile_health · business_profile · reviews · review_velocity · multi_platform_reviews · reputation_audit |
| Rankings and maps | rank_check · geogrid · serp_local_finder · serp_maps |
| Keyword research | keyword_search_volume · keyword_opportunities · keywords_for_site · keywords_related · keyword_trends |
| AI search visibility | ai_visibility · serp_ai_mode · ai_mentions · ai_top_pages · ai_top_sources · ai_compare · ai_keyword_data · ai_llm_response · ai_scraper |
| Authority and links | backlinks_summary · backlinks_gap · local_authority · brand_mentions |
| Competition | competitor_gap · brand_mentions |
| Audits | local_audit · citation_audit · citations · page_audit |
Some actions cache: reopening a saved result is free and refreshing buys a new one. The catalog says which, per action. Prefer the saved result unless the question genuinely needs current data.
rank_check deserves a note
check_local_ranks is the quick path and measures the local pack only. The catalogued action measures more and is priced accordingly — one credit per phrase per surface. surfaces chooses where to measure, and keywords narrows the run; omit it and every saved phrase is checked, so twenty tracked phrases across both surfaces is forty credits. Quote it first, and narrow the list when the user asked about one phrase.
run_local_seo_action {
action_id: "rank_check",
request_key: "dg-ranks-20260829-b2",
input: {
domain_id: 12,
surfaces: ["local_pack", "organic"], // both, or just one
keywords: ["emergency plumber akron"] // omit to check every saved phrase
}
}When something goes wrong
A tool that fails comes back as an ordinary result carrying isError and a readable reason, not as a protocol error — so an assistant can read why and adapt instead of ending the turn. Only transport-level problems return a non-200 status.
| What you see | What it means | What to do |
|---|---|---|
| 401 | Key missing, malformed, revoked, expired, or the plan does not include API access. | Check Settings, API Keys, and the plan. Do not retry. |
| 401 “requires a DomainGuard API key” | A dashboard session token was sent instead of a key. | MCP takes an nhm_ key only. Sessions are refused so a stolen browser token cannot drive it. |
| 403 Forbidden origin | The Origin header failed validation. | A client configuration problem, not a credential problem. |
| 405 on an authenticated GET | Correct. There is no SSE stream to open. An unauthenticated GET answers 401 first, because the key is checked before the method. | Use POST. |
| 429 | The key's hourly rate limit, or a per-plan manual-scan limit. | Wait. Free plans allow one manual SEO scan per day. |
| isError: “missing the … scope” | The key does not carry that resource scope. | Mint a key with the scope. Retrying will not help. |
| isError: “read-only” | The tool changes state and the key has no write scope. | Use the read equivalent, or upgrade to Enterprise. |
| 409 idempotency_mismatch | That request_key was already used for a different request. | Generate a fresh key for a genuinely new run. |
| Timeout on a paid action | Whether it ran is unknown. | Call get_local_seo_operation with the same request_key. Never re-run blind. |
| A scan returns with no findings | It is asynchronous and still running. | Poll the matching results tool. |
| “requires ownership verification” | Active scanning needs one proven domain on the account. | request_ownership_email_link is the easiest proof; or get_scan_ownership_verification, publish, then verify_scan_ownership. |
| 402 plan_upgrade_required | The feature is not on the account's plan. | Say which plan has it. Do not retry. |
| 403 on report_scanner_issue | Reports are accepted only from the account's own API keys carrying scans and write, on Enterprise. | Carry on without reporting. |
| 400 location_not_found | The city is not in the rank-data gazetteer as written. | Offer the suggestions in the response, or search_locations, then call again. |
Protocol notes
One endpoint
POST /api/mcp, speaking JSON-RPC 2.0. GET answers 405 with Allow: POST — there is no event stream to open, and saying so is more honest than holding one open with nothing to send.
Both protocol eras
The stateless 2026-07-28 revision and the handshake revisions 2025-11-25, 2025-06-18, and 2025-03-26 are all served, from the same code path. Holding no session state is what makes supporting both cheap.
Methods
initialize, server/discover, tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/read, resources/templates/list (empty) and ping. prompts/list returns the five prompts and resources/list the four resources below.
No sessions
No Mcp-Session-Id, nothing remembered between calls. Each request stands alone and carries its own authentication.
No batching
JSON-RPC batching was removed from the spec in the 2025-06-18 revision and is rejected here. Notifications receive 202 with no body.
Cacheable tool lists
tools/list results are marked private and scope-dependent. A shared intermediary must never serve one key's tool list to another.
Give this to Claude
Everything on this page is also published as one plain-text file written for an assistant to read. Point Claude at the URL, or paste the block below into your project's CLAUDE.md so every session starts knowing the rules instead of discovering them by spending credits.
The full operating guide
https://nhmohio.com/mcp/claude-guide.md# Paste into CLAUDE.md
## DomainGuard MCP
The `domainguard` MCP server is connected. It reaches every part of the
account: DNS, SSL, mail authentication and DMARC reports, security checkups,
Hijack Guard, lookalikes, vulnerabilities and ports, software CVE/KEV alerts,
dark web, on-page and site-wide SEO, accessibility, local rankings and the
Google Business Profile, provider status, compliance and incident records.
Rules:
- Start with `list_domains`; every other tool needs the numeric `domain_id`.
- Read stored results (`get_checkup_history`, `get_seo_results`,
`get_vuln_scan_latest`, `get_local_rank_history`, `get_domain_setup_guide`)
before running any scan.
- To add or set up a domain: `plan_domain_onboarding` first, ask its questions
in plain words, then `onboard_domain` once. It spends nothing. Paid steps
(`run_local_seo_quickstart`, `start_accessibility_audit`) come after, on a
quote the user agreed to.
- Verify what you can before presenting it (resolve the record, fetch the
headers, count the tags). When the scanner is wrong, `report_scanner_issue`
with what it said, what is true, how you know, what to change; then
`submit_scanner_review` once per domain. Report only what you verified.
- `run_*`, `start_*`, `check_*`, `verify_*`, `scan_*`, `fetch_*`,
`discover_*` and `assess_*` cost time or credits. So do
`get_domain_email_security` and `get_scan_ownership_verification`. Say the
cost before spending.
- Call `get_local_seo_catalog` before `run_local_seo_action`; it is the
authority on inputs and price.
- Every paid action needs a fresh `request_key` (8-100 chars, [A-Za-z0-9._-]).
After a timeout use `get_local_seo_operation`, never a re-run.
- `start_vuln_scan`, `start_site_audit`, `start_deep_seo_job` and
`start_accessibility_audit` are asynchronous — poll their results tools.
- Confirm every `delete_*`, `remove_*`, `untrack_*` and `unsubscribe_*` in
words before calling it.
- DomainGuard monitors these domains, it does not host them. Deliver the
diagnosis plus the exact change, and say where the user applies it —
registrar, DNS provider, host, CMS, or Google Business Profile.
- Never estimate a score, position, volume, or price that a tool did not
return; never invent a business name, city, phrase or email address.
Full guide: https://nhmohio.com/mcp/claude-guide.md (also the MCP resource domainguard://guide).
Prompts: /mcp__domainguard__onboard_domain, set_up_local_seo, review_domain,
portfolio_triage, fix_email_deliverability.How access is limited
Your account only
Every tool runs through the same ownership checks as the dashboard. A key reaches the domains on its own account and nothing else.
Scopes decide the tool list
Tools outside a key's scopes are never offered and cannot be called. Mint one key per use and give each only what it needs.
Read-only means read-only
Without the write scope, an assistant cannot start live lookups or scans, spend credits, or change configuration. Write is a separate tick box, and only on Enterprise.
Keys cannot mint keys
An API key is refused at the key-management endpoints, so a narrow key handed to an agent can never issue itself a broader one. That stays a dashboard action.
Sessions are refused
The MCP endpoint takes an API key and nothing else. A browser token obtained through XSS cannot drive it.
Revocable and rate limited
Revoke a key and it stops working immediately. Each key carries its own hourly limit, and you can set an expiry date when you create it.
Point Claude at your domains
Pro reads your account. Enterprise reads it and acts on it. Both take about a minute to set up.
