from line 44
4444✓ Successfully logged in
4545```
4646
47### Personal vs. organization namespaces
48
49GitHub authentication always grants your personal namespace, `io.github.<your-username>/*`.
50
51To publish under an **organization** namespace (`io.github.<orgname>/*`), you must be an **Owner** of that organization. Ordinary org membership is no longer sufficient: the registry checks your membership role and only grants the org namespace to admins. This prevents anyone who merely belongs to an org from publishing — or overwriting — servers under the org's name.
52
53> [!WARNING]
54>
55> **`mcp-publisher login github` cannot currently publish to an organization namespace.**
56>
57> The interactive device flow authenticates against a GitHub App, and GitHub App user tokens cannot read organization memberships — GitHub returns an empty list, so the registry sees no organizations you own and grants only your personal namespace. This affects everyone, regardless of your role in the organization or whether your membership is public. Tracking issue: [#1468](https://github.com/modelcontextprotocol/registry/issues/1468).
58>
59> To publish an organization server today, use one of the two paths that can prove your organization role:
60>
61> * **[GitHub Actions](https://modelcontextprotocol.io/registry/github-actions.md)** — publishes via OIDC, which is unaffected.
62> * **A Personal Access Token** — `mcp-publisher login github --token <YOUR_TOKEN>`, meeting the requirements below.
63
64If you authenticate with a Personal Access Token (in CI, or to work around the limitation above), the token must let the registry read your organization role. A token that can't will still publish to your personal namespace, but org publishing will be silently unavailable. The exact requirement depends on the token type:
65
66* **Classic PAT**: grant the `read:org` scope.
67* **Fine-grained PAT**: grant the **Organization permissions → Members → Read-only** permission (the fine-grained equivalent of `read:org`). Without it, GitHub returns no organization membership for the token and you'll get your personal namespace only. Note that a fine-grained PAT is bound to a single resource owner, so it can only see the organization it was created for.
68
69Either way the token needs **no** repository scopes — the registry never reads or writes your code.
70
4771## DNS Authentication
4872
4973DNS authentication is a domain-based authentication method that relies on a DNS TXT record.
5074
75> [!WARNING]
76>
77> The TXT record must be placed on the **apex** of your domain (e.g. `example.com`), **not** under a selector like `_mcp-auth.example.com` or `_mcp-registry.example.com`. MCP DNS auth follows SPF-style placement (apex), not DKIM-style (selector). If you put the record under a selector, the registry will not see it and authentication will fail with a generic signature error.
78>
79> If you rotate keys, also remember to remove the previous TXT record from the apex — a stale record left behind will be tried first and cause verification to fail.
80
5181To perform DNS authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate a TXT record based on a public/private key pair:
5282
83> [!NOTE]
84>
85> The Ed25519 codepath requires **OpenSSL 3.0 or later**. macOS ships with LibreSSL by default (the system `openssl` binary), which does not implement Ed25519 in `genpkey` and fails with `Algorithm Ed25519 not found`. On macOS, install OpenSSL 3 (`brew install openssl@3`) and invoke it explicitly — for example, replace `openssl` with `/opt/homebrew/opt/openssl@3/bin/openssl` (Apple Silicon) or `/usr/local/opt/openssl@3/bin/openssl` (Intel) in the commands below. The ECDSA P-384 codepath works on LibreSSL.
86
5387```bash Ed25519 theme={null}
5488MY_DOMAIN="example.com"
5589
from line 176
142176MY_DOMAIN="example.com"
143177
144178PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')"
145mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
179mcp-publisher login dns --algorithm ecdsap384 --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
146180```
147181
148182```bash Google KMS theme={null}
from line 202
168202
169203To perform HTTP authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate an `mcp-registry-auth` file based on a public/private key pair:
170204
205> [!NOTE]
206>
207> As with DNS authentication, the Ed25519 codepath requires **OpenSSL 3.0 or later**. macOS's system LibreSSL does not support Ed25519 in `genpkey`. See the note in the [DNS Authentication](#dns-authentication) section for the macOS workaround.
208
171209```bash Ed25519 theme={null}
172210# Generate public/private key pair using Ed25519
173211openssl genpkey -algorithm Ed25519 -out key.pem
from line 292
254292```bash ECDSA P-384 theme={null}
255293MY_DOMAIN="example.com"
256294PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')"
257mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
295mcp-publisher login http --algorithm ecdsap384 --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
258296```
259297
260298```bash Google KMS theme={null}
from line 311
273311
274312mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}"
275313```
314
315### Troubleshooting failed HTTP verification
316
317The Registry fetches your verification file from its own infrastructure when you run `mcp-publisher login http`, so a `curl` that succeeds on your machine does not prove that login will work. The Registry itself must be able to reach the endpoint.
318
319> [!WARNING]
320>
321> If the Registry cannot use the endpoint, login fails with `token exchange failed with status 401` even though the file is served correctly to every other client.
322
323The fetch is a plain HTTPS request with these properties:
324
325| Property | Requirement |
326| - | - |
327| Request | `GET https://<domain>/.well-known/mcp-registry-auth`, sent with `Accept: text/plain` and `User-Agent: mcp-registry/1.0` |
328| Status | `200 OK`. Redirects are not followed, so a `3xx` fails verification |
329| Body | the proof record, at most 4096 bytes. Surrounding whitespace is ignored |
330| Timeout | 10 seconds for the whole request |
331| Origin | the hosted Registry runs on Google Cloud, so the request arrives from Google Cloud addresses rather than a fixed address you can allowlist per request |
332
333Because the request originates from cloud infrastructure rather than a browser on a residential network, the usual causes of failure are edge rules that block it:
334
335* a WAF, bot-management, or "under attack" rule that challenges or blocks non-browser clients
336* a geo-block or IP allowlist that excludes Google Cloud address ranges
337* an `AAAA` (IPv6) record pointing at an address that is not reachable. Verification fails if none of the domain's public addresses can be reached, even when IPv4 works in a browser
338* a `301`/`302` redirect to another host or path, which the Registry does not follow
339* `/.well-known/mcp-registry-auth` answering `200 OK` with an HTML error page instead of the proof record
340
341To see what the Registry sees, request the file the same way it does, ideally from a network other than your own:
342
343```bash theme={null}
344curl -sS -o - -w '\n%{http_code}\n' \
345 -H 'Accept: text/plain' \
346 -A 'mcp-registry/1.0' \
347 https://example.com/.well-known/mcp-registry-auth
348```
349
350The response body must be the proof record printed by the login command (`v=MCPv1; k=...; p=...`). When the Registry cannot use the endpoint, the login error carries the Registry's own fetch failure, such as `dial tcp <ip>:443: i/o timeout`, `HTTP 403: failed to fetch key from ...`, or `HTTP 301: failed to fetch key from ...`. Allow plain HTTPS clients through for `/.well-known/mcp-registry-auth`, serve the path without redirects, then retry.
351
352Addresses that resolve to loopback, private, link-local, or carrier-grade NAT ranges are refused by design, so the endpoint must be reachable at a public address.
353
354## Troubleshooting DNS and HTTP authentication
355
356If `mcp-publisher login dns` or `login http` fails, check the published proof record before changing keys or retrying. A fetch failure and a signature mismatch have different causes; read the detailed error returned by the Registry.
357
358### Compare the "Expected proof record" against what is actually published
359
360Each login invocation prints an `Expected proof record:` line, for example:
361
362```text Output theme={null}
363Expected proof record:
364v=MCPv1; k=ed25519; p=CmELkdW27Q+pySZRD1ezkXAG/SbeDhGWAjtuKKMKixg=
365```
366
367Check that the algorithm (`k=`) and public key (`p=`) in the published MCPv1 record match the current login command's output. Rerun the login command after rotating a key so you compare against the freshly derived public key. For HTTP verification, surrounding whitespace and trailing newlines are trimmed; their presence alone does not cause a signature mismatch. Avoid editing or wrapping the public-key data itself.
368
369When a signature does not match, the Registry's error includes fingerprints of the published keys it tried. Compare these with the key you intended to publish to help identify a stale record or a mismatch between the private key and published public key.
370
371### DNS: verify the TXT record before logging in
372
373Confirm an MCPv1 TXT record is published at the exact domain passed to `--domain`, rather than at a selector such as `_mcp-auth.<domain>`:
374
375```bash theme={null}
376dig TXT example.com +short
377# Expect a record like:
378# "v=MCPv1; k=ed25519; p=CmELkdW27Q+pySZRD1ezkXAG/SbeDhGWAjtuKKMKixg="
379```
380
381If no MCPv1 record appears, check the DNS provider's record name and value, then allow for DNS propagation and cached responses after correcting it. If `dig TXT _mcp-auth.example.com +short` returns the proof record but `dig TXT example.com +short` does not, it is published at the wrong name (see the apex warning above).
382
383When the Registry finds a well-formed MCPv1 record at a known wrong selector (`_mcp-auth.<domain>` or `_mcp-registry.<domain>`) but none at the requested domain, it returns a specific error identifying the misplaced record.
384
385The Registry tries all valid published MCP keys; a different key in another TXT record does not take precedence over the correct one. Ensure at least one record contains the public key for the private key used by the login command. Remove retired MCP authentication records when they are no longer needed, while preserving keys still in use and TXT records for other services.
386
387### HTTP: verify the well-known endpoint before logging in
388
389Follow [Troubleshooting failed HTTP verification](#troubleshooting-failed-http-verification) for the exact request, timeout, response-size limit, redirect policy, and network requirements. A successful request from your own machine does not establish that the Registry can reach the endpoint.
390
391### Confirm the algorithm matches the key
392
393The `--algorithm` flag (default `ed25519`) must match the algorithm declared in the published proof record's `k=` field. Signing an Ed25519 timestamp against an `ecdsap384` proof record (or vice versa) fails verification. If you regenerated the keypair with a different algorithm than what is published, update the TXT record or the `mcp-registry-auth` file before retrying.
276394
No line in this hunk matches that.