createmcps.com

Guide

How to verify a DNS namespace for mcp-publisher

Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2

Verifying a domain lets you publish MCP servers under your own namespace instead of io.github.*. Three steps: generate a key pair, publish a TXT record on the apex of your domain, then run mcp-publisher login dns. Verifying example.com grants you com.example.*/*.

Before you start

  • Ability to add a TXT record on the domain apex. If you cannot, skip to HTTP verification.
  • OpenSSL 3.0 or later for the Ed25519 path. macOS ships LibreSSL, which cannot do it — see step 1.
  • The mcp-publisher CLI installed and on your PATH.
The one thing that trips almost everyone: The TXT record goes on the apexexample.com — not under a selector like _mcp-registry.example.com. MCP DNS auth follows SPF-style placement, not DKIM-style. A record under a selector is silently never read, and you get a signature error that looks like a key problem. This is step 2, and it is the step to read twice.

1. Generate a key pair

The registry verifies a signature made with your private key against a public key you publish in DNS. Generate the pair first, and keep key.pem out of version control.

Ed25519 — the default
export MY_DOMAIN="example.com"

openssl genpkey -algorithm Ed25519 -out key.pem

PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)"
echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ed25519; p=${PUBLIC_KEY}\""

That last line prints the exact record to publish. Copy it — you need it in step 2.

On macOS: Algorithm Ed25519 not found: The system openssl on macOS is LibreSSL, which does not implement Ed25519 in genpkey. Either install OpenSSL 3 (brew install openssl@3) and call it by full path — /opt/homebrew/opt/openssl@3/bin/openssl on Apple Silicon, /usr/local/opt/openssl@3/bin/openssl on Intel — or use the ECDSA path below, which works on LibreSSL.
ECDSA P-384 — works on LibreSSL
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem

PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed \
  | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)"
echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}\""

Note the k= value differs: ed25519 or ecdsap384. Whichever you choose here must match what you pass to the login command in step 4.

2. Publish the TXT record on the apex

In your DNS provider's control panel, create a TXT record. The field usually labelled Name or Host must be @ — the convention meaning “the domain itself”. Do not type a subdomain into it.

FieldValue
TypeTXT
Name / Host@
Valuev=MCPv1; k=ed25519; p=YOUR_PUBLIC_KEY
Apex vs selector — this is the failure
# ✗ Never read by the registry
_mcp-registry.example.com.  IN TXT  "v=MCPv1; k=ed25519; p=…"
_mcp-auth.example.com.      IN TXT  "v=MCPv1; k=ed25519; p=…"

# ✓ Apex
example.com.                IN TXT  "v=MCPv1; k=ed25519; p=…"

Adding a TXT record to the apex does not disturb an existing SPF or verification record. A domain may hold many TXT records; the registry reads the one beginning v=MCPv1.

3. Confirm the record actually resolves

Do not skip this. A control panel showing the record saved is not the same as the world being able to read it, and this check costs one command.

Check, then widen the check
# 1. Through your normal resolver
dig +short TXT example.com | grep MCPv1

# 2. Through a public resolver, bypassing local cache
dig +short TXT example.com @1.1.1.1 | grep MCPv1

# 3. Ground truth — your domain's own authoritative nameserver
dig +short NS example.com
dig +short TXT example.com @<one-of-those-nameservers>
  • Nothing returned anywhere — the record is not on the apex. Back to step 2.
  • Authoritative has it, public resolvers do not — you are waiting on TTL. Nothing to fix; wait.
  • More than one MCPv1 line — a stale record is present and will be tried first. Delete it.

4. Log in

Extract the private key in the hex form the CLI expects, then authenticate. The private key never leaves your machine — it signs a challenge; it is not uploaded.

Ed25519
PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text \
  | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')"

mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
ECDSA P-384
PRIVATE_KEY="$(openssl ec -in key.pem -noout -text \
  | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')"

mcp-publisher login dns --algorithm ecdsap384 \
  --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
If this fails with a signature error: In order of likelihood: the record is on a selector rather than the apex; a stale record is shadowing the new one; the k= in DNS does not match --algorithm; or propagation has not finished. Each of these is walked through on the troubleshooting page.

5. Make server.json match the namespace you earned

MCP-REG-002

Authentication grants a namespace; publishing fails separately if server.json asks for a different one. Verifying example.com grants com.example.*/*:

What is and isn't inside your namespace
{ "name": "com.example/weather-server" }         ✓
{ "name": "com.example.tools/weather-server" }   ✓ subdomain form
{ "name": "io.github.you/weather-server" }       ✗ needs GitHub auth
{ "name": "com.other/weather-server" }           ✗ different domain
Publishers must verify ownership of their namespace through GitHub, DNS, or HTTP challenges, preventing arbitrary spam submissions.
MCP-REG-002 MAY · spec 2026-07-28

Then publish. Full walkthrough of the surrounding steps in publishing to the registry.

Publish
mcp-publisher publish

Rotating keys later

Rotation is a replace, never an append. A previous record left on the apex is tried first and its failure ends the attempt — the registry does not fall through to your new key.

  • Generate the new pair and compute the new record.
  • Delete the old MCPv1 TXT record, then add the new one.
  • Confirm dig +short TXT example.com | grep MCPv1 returns exactly one line.
  • Log in again with the new private key.

If you can't touch DNS: HTTP verification

HTTP verification proves the same control and grants the same com.example.*/* namespace. It is often easier in practice, because the proof can live in the same repository as the code that deploys the site.

Host this at /.well-known/mcp-registry-auth
v=MCPv1; k=ed25519; p=${PUBLIC_KEY}
Then log in
PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text \
  | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')"

mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"

The file must be served over HTTPS at https://example.com/.well-known/mcp-registry-auth and must not redirect to a different host.

Doing this in CI

The DNS record is a one-time setup; only the private key needs to reach your pipeline. Store the hex private key as a secret and pass it straight through.

GitHub Actions
- name: Publish to MCP registry
  env:
    MCP_DNS_KEY: ${{ secrets.MCP_DNS_PRIVATE_KEY }}
  run: |
    mcp-publisher login dns \
      --domain example.com \
      --private-key "$MCP_DNS_KEY"
    mcp-publisher publish
Our recommendation, not a spec quote: if you publish under io.github.* rather than your own domain, prefer mcp-publisher login github-oidc in CI instead — it uses the workflow's own OIDC token, so there is no long-lived secret to store or rotate. DNS keys are the right tool for a custom namespace, not a default.

Then check the server you published

Namespace verification proves you own the name. It says nothing about whether the server behind it conforms to the spec — that is a separate check, and clients care about both.

Validate a server →

Frequently asked

How do I verify a DNS namespace for mcp-publisher?

Generate an Ed25519 key pair, publish a TXT record on your domain's apex containing v=MCPv1; k=ed25519; p=YOUR_PUBLIC_KEY, wait for propagation, then run mcp-publisher login dns --domain example.com --private-key HEX_KEY. Verifying example.com grants you the com.example.*/* namespace.

Where does the MCP DNS TXT record go?

On the apex of the domain — example.com itself. MCP DNS authentication uses SPF-style apex placement, not DKIM-style selector placement, so a record at _mcp-registry.example.com or _mcp-auth.example.com is never read and login fails with a generic signature error.

What namespace does DNS verification grant?

Verifying example.com grants com.example.*/* — the reverse-DNS form of your domain. So you can publish com.example/weather-server, and subdomain forms such as com.example.tools/weather-server. It does not grant io.github.* namespaces, which require GitHub authentication instead.

Do I need DNS access, or is there another way?

There is another way. HTTP verification proves the same thing and grants the same namespace: host a file at https://example.com/.well-known/mcp-registry-auth containing the same v=MCPv1 record value, then run mcp-publisher login http. It is the better option when DNS is managed by another team.

Can I use ECDSA instead of Ed25519?

Yes. ECDSA P-384 is supported and is the practical choice on macOS, where the system openssl is LibreSSL and cannot generate Ed25519 keys. Use k=ecdsap384 in the TXT record and pass --algorithm ecdsap384 to the login command — both, or the signature will not verify.

How long does DNS propagation take?

Usually minutes, but it depends on your provider and on the TTL of any previous record. Query your domain's authoritative nameserver directly rather than trusting your local resolver's cache; once the authoritative server returns the record, only TTL stands between you and a successful login.

Sources

Every command on this page was verified on 10 August 2026 against the MCP registry's own documentation: