AstraNL AstraNL insights
MCPProtocolAuthentication

How to publish an MCP server to the official registry without GitHub OAuth

29 April 2026 · 6 min read

If you have built an MCP server and want it discoverable by Claude Desktop, Cursor, and every other MCP-aware tool, you need to publish it to registry.modelcontextprotocol.io. Most documentation walks you through GitHub OAuth, where the server name ends up under io.github.<your-username>/....

That works. But there is a second path most people miss: HTTP domain-ownership authentication. If you control a domain, you can claim a namespace under it (we use com.astranl/...) without any browser OAuth dance. This matters when you want a corporate-shaped namespace, when GitHub OAuth is friction, or when you are running fully autonomous publishing from a server with no browser available.

The flow at a glance

  1. Generate an ed25519 keypair on your server.
  2. Publish the public key as a TXT-style record at https://<your-domain>/.well-known/mcp-registry-auth (HTTP, not DNS).
  3. Run mcp-publisher login http --domain=<your-domain>. The CLI signs a challenge with your private key, the registry fetches the well-known URL, verifies the signature against the public key, and issues you a token.
  4. Edit your server.json so name uses your reverse-domain namespace (e.g. com.astranl/mcp).
  5. mcp-publisher publish.

Concrete commands

Generate the keypair:

openssl genpkey -algorithm Ed25519 -out key.pem
openssl pkey -in key.pem -outform DER 2>/dev/null \
  | tail -c 32 | xxd -p -c 32 > priv.hex

Extract the public key in the exact format the registry expects (base64, not hex — this caught us with a 401 the first time):

PUB_B64=$(openssl pkey -in key.pem -pubout -outform DER 2>/dev/null \
  | tail -c 32 | base64)
echo "v=MCPv1; k=ed25519; p=$PUB_B64" \
  > /var/www/.well-known/mcp-registry-auth

Verify it is reachable on HTTPS, then login:

curl https://your-domain.com/.well-known/mcp-registry-auth
# v=MCPv1; k=ed25519; p=U+fQxRqnI1WX0zs/...

PRIV=$(cat priv.hex)
mcp-publisher login http --domain=your-domain.com --private-key="$PRIV"
# ✓ Successfully logged in

The two namespace gotchas

Format of the public key. The registry accepts only base64 in the well-known record, even though many tutorials show hex. If you get "invalid Ed25519 public key size", you have hex where base64 belongs.

Namespace shape. HTTP auth gives you com.<domain>/..., not io.github.<user>/.... If you ported a server.json from a GitHub-OAuth setup, you will need to rewrite the name field. Description must also be ≤100 characters — the registry rejects longer ones with an HTTP 400 you will only see with verbose curl.

Why this matters operationally

Once you have HTTP-auth login working, your publishing flow is fully scriptable. No browser. No interactive consent. We run our publish step from a systemd unit on the same VPS that hosts the MCP server itself. New version of the protocol ships, the unit runs mcp-publisher publish server.json, the registry updates within seconds, and every Claude Desktop user who looks us up tomorrow sees the new version.

If your MCP server is a side project tied to your personal GitHub account, OAuth is fine. If it is a product or a piece of infrastructure with its own domain, HTTP auth is the cleaner ground.

References

More AstraNL insights