Guide
How to verify a DNS namespace for mcp-publisher
Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2
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-publisherCLI installed and on yourPATH.
example.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.
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.
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.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.
| Field | Value |
|---|---|
| Type | TXT |
| Name / Host | @ |
| Value | v=MCPv1; k=ed25519; p=YOUR_PUBLIC_KEY |
# ✗ 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.
# 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.
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}"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}"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-002Authentication grants a namespace; publishing fails separately if server.json asks for a different one. Verifying example.com grants com.example.*/*:
{ "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.”
Then publish. Full walkthrough of the surrounding steps in publishing to the registry.
mcp-publisher publishRotating 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 MCPv1returns 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.
v=MCPv1; k=ed25519; p=${PUBLIC_KEY}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.
- 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 publishio.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:
- registry/docs/modelcontextprotocol-io/authentication.mdx — key generation, record format, apex placement, login commands
- registry/docs/reference/cli/commands.md — the full CLI flag reference
- registry issue #385 — where the apex-vs-selector confusion was resolved by a maintainer