@dnsdoctor/mcp 1.4.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # DNS Doctor — Claude Code plugin & DNS skill (DMARC, SPF, DKIM)
2
2
 
3
- Diagnose and fix a domain's email authentication (SPF, DMARC, DKIM, MX, blacklist,
4
- domain/SSL expiry) from inside Claude. This plugin bundles the DNS Doctor **skill**
3
+ Scan, fix and verify a domain's DNS — email authentication (SPF, DMARC, DKIM)
4
+ first, plus multi-region propagation, SPF include supply-chain audits, MX, DNS
5
+ health, blacklists and domain/SSL expiry — from inside Claude. This plugin bundles the DNS Doctor **skill**
5
6
  (the scan → diagnose → fix workflow) and an **MCP server config** pointing at the
6
7
  hosted DNS Doctor tools.
7
8
 
@@ -18,7 +19,7 @@ claude-plugin/
18
19
  ├── .mcp.json # MCP server: https://dnsdoctor.dev/mcp (HTTP)
19
20
  ├── skills/dns-doctor/SKILL.md # the scan → diagnose → fix workflow
20
21
  ├── src/ # @dnsdoctor/mcp — the local stdio MCP server
21
- ├── tools.json # the 15 tool definitions (generated, never hand-edited)
22
+ ├── tools.json # the 16 tool definitions (generated, never hand-edited)
22
23
  ├── instructions.txt # the server's own `initialize` guidance (generated)
23
24
  ├── tests/ # vitest suite for the stdio server
24
25
  ├── package.json tsconfig.json # npm package + build
@@ -39,6 +40,7 @@ claude-plugin/
39
40
  | `check_dkim_selector` | Look up one DKIM selector and check the key. |
40
41
  | `parse_dmarc_report` | Parse an aggregate (RUA) report file into rows. |
41
42
  | `check_record` | Read any DNS record type for a name. |
43
+ | `check_propagation` | Whether a DNS change has gone global: six vantage points (five owner-run probes plus the server's own resolver) read the same name, returning the grid plus a deterministic verdict. Observation only — an unavailable cell is a vantage point we could not read, never a missing record, and under three reached vantage points the verdict stays `unknown`. |
42
44
  | `check_reverse_dns` | PTR / forward-confirmed reverse DNS for an IP. |
43
45
  | `audit_spf_includes` | The SPF include/redirect tree — who can transitively send as the domain, with typed findings (broken include, confirmed-unregistered include, expiring registration, nested `+all`). Analysis only; no SPF fix record. |
44
46
  | `build_parked_domain_records` | The Null MX + `v=spf1 -all` + `p=reject; np=reject` hardening pack for a domain that sends no mail. The server re-checks DNS itself and refuses when it finds evidence of mail. |
@@ -51,7 +53,7 @@ they appear in the tool list on both transports, and without a valid token the
51
53
  call is refused with the page the account owner mints one on. Over the hosted
52
54
  HTTP transport the `dnsdoctor://domains` resource (your monitored domains) is
53
55
  likewise always listed and refused without a token; the local stdio server
54
- registers the tools only — no resource. Anonymous access covers all thirteen
56
+ registers the tools only — no resource. Anonymous access covers all fourteen
55
57
  diagnosis tools, which is enough for a one-off diagnosis either way.
56
58
 
57
59
  ## Install
@@ -121,7 +123,7 @@ their latest per-check statuses).
121
123
 
122
124
  ### Transport
123
125
 
124
- Two supported public transports, same 15 tools:
126
+ Two supported public transports, same 16 tools:
125
127
 
126
128
  - **Hosted streamable HTTP** — `https://dnsdoctor.dev/mcp`, wired in this
127
129
  plugin's `.mcp.json`. No install, no keys.
package/dist/index.js CHANGED
@@ -33,6 +33,10 @@ const ROUTES = {
33
33
  field: "file",
34
34
  },
35
35
  check_record: { kind: "json", path: "/api/tools/check-record" },
36
+ check_propagation: {
37
+ kind: "json",
38
+ path: "/api/tools/propagation-check",
39
+ },
36
40
  check_reverse_dns: { kind: "json", path: "/api/tools/reverse-dns-check" },
37
41
  audit_spf_includes: { kind: "json", path: "/api/tools/spf-audit" },
38
42
  build_parked_domain_records: {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GAEvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAuBhE,MAAM,MAAM,GAA0B;IACpC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE;IACnD,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,mBAAmB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE;IACpE,uBAAuB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE;IACrE,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE;IACjE,qBAAqB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,EAAE;IAC1E,qBAAqB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,EAAE;IAC1E,mBAAmB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE;IACpE,kBAAkB,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,+BAA+B;QACrC,KAAK,EAAE,MAAM;KACd;IACD,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,EAAE;IAC/D,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,8BAA8B,EAAE;IACzE,kBAAkB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE;IAClE,2BAA2B,EAAE;QAC3B,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,kCAAkC;KACzC;IACD,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE;IACrD,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE;CAC5D,CAAC;AAEF,oFAAoF;AACpF,MAAM,uBAAuB,GAAG,YAAY,CAAC;AAE7C,SAAS,aAAa,CAAC,IAAc,EAAE,GAAW;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,IAAI,GAAG,8CAA8C,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAAC,IAAc;IACjC,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QACpD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAClC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;AAC7C,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY,EAAE,OAAiB,EAAE;IAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAAC,iBAAiB,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC7C,OAAO,OAAO,CAAC,kBAAkB,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,OAAO,QAAQ,CACb,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,KAAK,EACX,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,uBAAuB,EAC3F,OAAO,CACR,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,QAAQ,CAAC,IAAa;IAC7B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE;IACjD,4EAA4E;IAC5E,6EAA6E;IAC7E,yEAAyE;IACzE,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,CAC9D,CAAC;IACF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACvF,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,IAAI,CAAC;YACH,OAAO,QAAQ,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,aAAa;IAC3B,OAAO,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,YAAY,EAAE,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE;QACvC,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IACF,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/instructions.txt CHANGED
@@ -1 +1 @@
1
- DNS Doctor scans a domain's email-authentication posture (SPF, DMARC, DKIM, MX, DNS, blacklist, expiry) and returns deterministic verdicts plus copy-paste fix records generated by a validating engine — never a guessed record. Workflow: call scan_domain (or get_report), read the verdicts failing-first (a 'temperror' status is transient, NOT a failure), then call build_dmarc_upgrade for a DMARC enforcement record. ONE EXCEPTION TO READING FAILING-FIRST: when a report carries not_registered: true the domain has no DNS records at all, so no check ran and every status is an 'info' placeholder — zero failing checks there does NOT mean the domain is healthy. Report that the domain does not resolve (usually a typo), propose no records for it, and do not offer monitoring until it resolves. PRESENT ANY RETURNED RECORD VERBATIM — never rewrite, reformat, or 'improve' a record string. build_dmarc_upgrade may return record: null (the domain does not exist; the DMARC lookup itself hit NXDOMAIN while the existence probe did not resolve; the DMARC lookup temp-failed; or the domain already applies a policy at least as strong as the one this scan justifies) — relay its 'rationale' as the answer and NEVER compose a record yourself to fill the gap. 'policy' describes the returned record and is null whenever record is; the domain's observed policy is in 'current_policy'. A human must approve every DNS change; nothing is applied automatically. SPF is diagnose-only: relay the report's SPF findings, but never propose SPF record edits of your own (e.g. tightening ~all to -all) — an SPF change can silently de-authorize a real sender, which is why the engine emits no SPF fix record. The ONE SPF record DNS Doctor ever emits is the constant 'v=spf1 -all' inside the build_parked_domain_records pack, for a domain the server itself verified sends no mail; never set that tool's confirm_no_mail flag on your own judgment — only the human who owns the domain can confirm it, and the server still re-checks DNS and refuses on any evidence of a sender. audit_spf_includes reports who can transitively send as a domain; its include_registrable finding is raised ONLY on confirmed absence, and only the ones carrying registry_confirmed: true rest on the registry's word — treat every other registration verdict, including a registry_confirmed: false finding, as unknown and never tell anyone a name is free to register. Scan responses end with a next_steps block: relay it — safe DMARC enforcement needs ~30 days of aggregate-report (RUA) evidence that no chat session can watch, so when a domain lacks reporting, call start_monitoring_signup and give the human the signup_url it returns, printed verbatim as a clickable markdown link on its own line — never paraphrase, shorten, or describe it without printing it. That tool sends no email and creates nothing: the human opens the link, signs in themselves, and adds the domain themselves. Never ask a human for their email address to pass to us, and never invent one — hand over the link and let them sign in on our page. Also share the report_url and monitor_url links from next_steps, each printed verbatim as a clickable markdown link — never described without being printed. Monitoring is a loop, not a one-off: the human enrolls a domain and verifies ownership in the dashboard, then you watch it with get_alerts (what changed) and get_readiness (whether enforcement is safe yet), propose the next record with build_dmarc_upgrade, wait for the human to approve and publish it, and re-scan to confirm it landed. Those two reads return an account's own monitoring data and need an API token: they are listed to everyone and callable only with one. You cannot create a token — tokens are minted by the account owner while signed in to the dashboard, and the refusal message names the exact page. Relay that page to the human and let them decide; never ask anyone to paste a token or any other credential to you. Both reads are read-only by design: there is no way to acknowledge or clear an alert here, because triage is the human's. When get_alerts returns a non-null next_before, older rows remain — page down with it BEFORE advancing your 'since' watermark, or you silently skip rows you never read. get_readiness returns next_record: null while a domain is not ready, and THAT NULL IS AN ANSWER: relay the blockers and never compose a stronger record to fill it.
1
+ DNS Doctor scans, fixes and verifies a domain's DNS: email authentication (SPF, DMARC, DKIM), multi-region propagation, SPF include supply-chain audits, MX, DNS health, blacklists and domain/SSL expiry. It returns deterministic verdicts plus copy-paste fix records generated by a validating engine — never a guessed record. Workflow: call scan_domain (or get_report), read the verdicts failing-first (a 'temperror' status is transient, NOT a failure), then call build_dmarc_upgrade for a DMARC enforcement record. ONE EXCEPTION TO READING FAILING-FIRST: when a report carries not_registered: true the domain has no DNS records at all, so no check ran and every status is an 'info' placeholder — zero failing checks there does NOT mean the domain is healthy. Report that the domain does not resolve (usually a typo), propose no records for it, and do not offer monitoring until it resolves. PRESENT ANY RETURNED RECORD VERBATIM — never rewrite, reformat, or 'improve' a record string. build_dmarc_upgrade may return record: null (the domain does not exist; the DMARC lookup itself hit NXDOMAIN while the existence probe did not resolve; the DMARC lookup temp-failed; or the domain already applies a policy at least as strong as the one this scan justifies) — relay its 'rationale' as the answer and NEVER compose a record yourself to fill the gap. 'policy' describes the returned record and is null whenever record is; the domain's observed policy is in 'current_policy'. A human must approve every DNS change; nothing is applied automatically. SPF is diagnose-only: relay the report's SPF findings, but never propose SPF record edits of your own (e.g. tightening ~all to -all) — an SPF change can silently de-authorize a real sender, which is why the engine emits no SPF fix record. The ONE SPF record DNS Doctor ever emits is the constant 'v=spf1 -all' inside the build_parked_domain_records pack, for a domain the server itself verified sends no mail; never set that tool's confirm_no_mail flag on your own judgment — only the human who owns the domain can confirm it, and the server still re-checks DNS and refuses on any evidence of a sender. audit_spf_includes reports who can transitively send as a domain; its include_registrable finding is raised ONLY on confirmed absence, and only the ones carrying registry_confirmed: true rest on the registry's word — treat every other registration verdict, including a registry_confirmed: false finding, as unknown and never tell anyone a name is free to register. Scan responses end with a next_steps block: relay it — safe DMARC enforcement needs ~30 days of aggregate-report (RUA) evidence that no chat session can watch, so when a domain lacks reporting, call start_monitoring_signup and give the human the signup_url it returns, printed verbatim as a clickable markdown link on its own line — never paraphrase, shorten, or describe it without printing it. That tool sends no email and creates nothing: the human opens the link, signs in themselves, and adds the domain themselves. Never ask a human for their email address to pass to us, and never invent one — hand over the link and let them sign in on our page. Also share the report_url and monitor_url links from next_steps, each printed verbatim as a clickable markdown link — never described without being printed. Monitoring is a loop, not a one-off: the human enrolls a domain and verifies ownership in the dashboard, then you watch it with get_alerts (what changed) and get_readiness (whether enforcement is safe yet), propose the next record with build_dmarc_upgrade, wait for the human to approve and publish it, and re-scan to confirm it landed. Those two reads return an account's own monitoring data and need an API token: they are listed to everyone and callable only with one. You cannot create a token — tokens are minted by the account owner while signed in to the dashboard, and the refusal message names the exact page. Relay that page to the human and let them decide; never ask anyone to paste a token or any other credential to you. Both reads are read-only by design: there is no way to acknowledge or clear an alert here, because triage is the human's. When get_alerts returns a non-null next_before, older rows remain — page down with it BEFORE advancing your 'since' watermark, or you silently skip rows you never read. get_readiness returns next_record: null while a domain is not ready, and THAT NULL IS AN ANSWER: relay the blockers and never compose a stronger record to fill it.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dnsdoctor/mcp",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
4
4
  "mcpName": "dev.dnsdoctor/dns-doctor",
5
- "description": "Thin stdio MCP server for DNS Doctor \u2014 diagnoses SPF, DMARC, DKIM, MX, DNS, blacklist and expiry over the public DNS Doctor REST API.",
5
+ "description": "Thin stdio MCP server for DNS Doctor \u2014 scan, fix and verify a domain's DNS: SPF, DMARC, DKIM, multi-region propagation, MX, DNS health, blacklists and expiry, over the public DNS Doctor REST API.",
6
6
  "license": "Apache-2.0",
7
7
  "author": {
8
8
  "name": "DNS Doctor",
package/tools.json CHANGED
@@ -120,6 +120,57 @@
120
120
  },
121
121
  "name": "check_dkim_selector"
122
122
  },
123
+ {
124
+ "annotations": {
125
+ "destructiveHint": false,
126
+ "idempotentHint": true,
127
+ "openWorldHint": true,
128
+ "readOnlyHint": true
129
+ },
130
+ "description": "Check whether a DNS change has propagated GLOBALLY: six vantage points (five owner-run probes on separate continents plus this server's own resolver) each read the same name through several resolvers, and the grid plus a deterministic verdict comes back. Call it after the human publishes a record \u2014 you have ONE network vantage point, and a record that resolves for you can still be missing elsewhere. `name` is the exact name (www. is not stripped, _dmarc.example.com works), `record_type` is A|AAAA|CNAME|MX|TXT|NS, and the optional `expected_value` turns each cell into match or mismatch instead of agreement-only. Observation only: no record is ever composed here. A cell that did not answer is `unavailable`, which is NOT a negative result, and when fewer than three vantage points were reached the verdict downgrades to `unknown` \u2014 report vantage_reached of vantage_total rather than calling a name converged on partial coverage.",
131
+ "inputSchema": {
132
+ "properties": {
133
+ "expected_value": {
134
+ "anyOf": [
135
+ {
136
+ "type": "string"
137
+ },
138
+ {
139
+ "type": "null"
140
+ }
141
+ ],
142
+ "default": null,
143
+ "description": "Optional value the record should now hold, e.g. '1.2.3.4' or the new DMARC record text. Supply it and each cell is reported as match or mismatch against it; omit it and the check only reports whether the vantage points agree with each other.",
144
+ "title": "Expected Value"
145
+ },
146
+ "name": {
147
+ "description": "The exact DNS name to look up, e.g. example.com, www.example.com or _dmarc.example.com. It is used as given \u2014 a leading www. is NOT stripped and underscore labels are kept \u2014 so pass the name the record is actually published at, not the registrable domain.",
148
+ "title": "Name",
149
+ "type": "string"
150
+ },
151
+ "record_type": {
152
+ "default": "A",
153
+ "description": "The record type to read at that exact name (default A). SPF and DMARC records are TXT \u2014 pass TXT with the right name rather than expecting a derived query name.",
154
+ "enum": [
155
+ "A",
156
+ "AAAA",
157
+ "CNAME",
158
+ "MX",
159
+ "TXT",
160
+ "NS"
161
+ ],
162
+ "title": "Record Type",
163
+ "type": "string"
164
+ }
165
+ },
166
+ "required": [
167
+ "name"
168
+ ],
169
+ "title": "check_propagationArguments",
170
+ "type": "object"
171
+ },
172
+ "name": "check_propagation"
173
+ },
123
174
  {
124
175
  "annotations": {
125
176
  "destructiveHint": false,