Follow Discord
Sweep 12 Nov 2026 · 23:00Z Build v2.1.323 540 read Stable v2.1.311 Latest v2.1.323 Next v2.1.323 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · mcp

One read of Model Context Protocolmcp-20261112T133708Z

7 pages moved out of 360 read.

Pages moved 7 significant first
Pages read 360 in this capture
Captured 13:37 UTC
Corpus hash cdc8debecf4c corpus-hash

What this read moved

1-7 of 7

registry/authentication Changed · +120 / -2 lines

### Personal vs. organization namespaces ### Troubleshooting failed HTTP verification ## Troubleshooting DNS and HTTP authentication ### Compare the "Expected proof record" against what is actually published ### DNS: verify the TXT record before logging in ### HTTP: verify the well-known endpoint before logging in ### Confirm the algorithm matches the key

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 

registry/faq Changed · +15 / -1 lines

from line 15
1515 
1616### Can I delete/unpublish my server?
1717 
18Currently, no. At the time of writing, there is [open discussion](https://github.com/modelcontextprotocol/registry/issues/104).
18Yes, you can change your server's status to `deleted` using the `mcp-publisher status` command:
19 
20```bash theme={null}
21# Delete a specific version
22mcp-publisher status --status deleted --message "No longer maintained" \
23 io.github.my-username/my-server 1.0.0
24 
25# Delete all versions
26mcp-publisher status --status deleted --all-versions --message "Project archived" \
27 io.github.my-username/my-server
28```
29 
30Deleted servers are hidden from default API listings but can still be retrieved with `include_deleted=true`. You can restore a deleted server by setting its status back to `active`.
31 
32**Note**: Server metadata is never permanently removed from the registry. The `deleted` status hides the server from discovery but preserves the historical record.
1933 
2034### How do I update my server metadata?
2135 

registry/github-actions Changed · +97 / -8 lines

### Securing your registry token in CI

from line 8
88 
99In your server project directory, create a `.github/workflows/publish-mcp.yml` file. Here is an example for npm-based local server, but the MCP Registry publishing steps are the same for all package types:
1010 
11The examples pin `mcp-publisher` to a specific release and verify its Sigstore
12bundle before extracting it. When upgrading, update `MCP_PUBLISHER_VERSION` to
13the intended release tag deliberately; do not switch back to the mutable
14`releases/latest` URL.
15 
1116```yaml OIDC authentication (recommended) theme={null}
1217name: Publish to MCP Registry
1318 
from line 26
2126 permissions:
2227 id-token: write # Required for OIDC authentication
2328 contents: read
29 env:
30 MCP_PUBLISHER_VERSION: v1.8.1
2431 
2532 steps:
2633 - name: Checkout code
from line 56
4956 
5057 ### Publish MCP server:
5158 
52 - name: Install mcp-publisher
59 - name: Install cosign
60 uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
61 
62 - name: Download and verify mcp-publisher
5363 run: |
54 curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
64 set -euo pipefail
65 os="$(uname -s | tr '[:upper:]' '[:lower:]')"
66 arch="$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
67 archive="mcp-publisher_${os}_${arch}.tar.gz"
68 base_url="https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}"
5569 
70 curl --fail --location --silent --show-error --output "$archive" "$base_url/$archive"
71 curl --fail --location --silent --show-error --output "$archive.sigstore.json" "$base_url/$archive.sigstore.json"
72 cosign verify-blob \
73 --bundle "$archive.sigstore.json" \
74 --certificate-identity "https://github.com/modelcontextprotocol/registry/.github/workflows/release.yml@refs/tags/${MCP_PUBLISHER_VERSION}" \
75 --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
76 "$archive"
77 tar xzf "$archive" mcp-publisher
78 rm -f "$archive" "$archive.sigstore.json"
79 
5680 - name: Authenticate to MCP Registry
5781 run: ./mcp-publisher login github-oidc
5882 
from line 100
76100jobs:
77101 publish:
78102 runs-on: ubuntu-latest
103 # Source MCP_GITHUB_TOKEN from a protected Environment, not a plain repo
104 # secret, and restrict that environment to your default branch / release
105 # tags. See "Securing your registry token in CI" below for why this matters.
106 environment: mcp-registry-publish
79107 permissions:
80108 contents: read
109 env:
110 MCP_PUBLISHER_VERSION: v1.8.1
81111 
82112 steps:
83113 - name: Checkout code
from line 136
106136 
107137 ### Publish MCP server:
108138 
109 - name: Install mcp-publisher
139 - name: Install cosign
140 uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
141 
142 - name: Download and verify mcp-publisher
110143 run: |
111 curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
144 set -euo pipefail
145 os="$(uname -s | tr '[:upper:]' '[:lower:]')"
146 arch="$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
147 archive="mcp-publisher_${os}_${arch}.tar.gz"
148 base_url="https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}"
112149 
150 curl --fail --location --silent --show-error --output "$archive" "$base_url/$archive"
151 curl --fail --location --silent --show-error --output "$archive.sigstore.json" "$base_url/$archive.sigstore.json"
152 cosign verify-blob \
153 --bundle "$archive.sigstore.json" \
154 --certificate-identity "https://github.com/modelcontextprotocol/registry/.github/workflows/release.yml@refs/tags/${MCP_PUBLISHER_VERSION}" \
155 --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
156 "$archive"
157 tar xzf "$archive" mcp-publisher
158 rm -f "$archive" "$archive.sigstore.json"
159 
113160 - name: Authenticate to MCP Registry
114161 run: ./mcp-publisher login github --token ${{ secrets.MCP_GITHUB_TOKEN }}
115162 
from line 182
135182 runs-on: ubuntu-latest
136183 permissions:
137184 contents: read
185 env:
186 MCP_PUBLISHER_VERSION: v1.8.1
138187 
139188 steps:
140189 - name: Checkout code
from line 212
163212 
164213 ### Publish MCP server:
165214 
166 - name: Install mcp-publisher
215 - name: Install cosign
216 uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
217 
218 - name: Download and verify mcp-publisher
167219 run: |
168 curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
220 set -euo pipefail
221 os="$(uname -s | tr '[:upper:]' '[:lower:]')"
222 arch="$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
223 archive="mcp-publisher_${os}_${arch}.tar.gz"
224 base_url="https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}"
169225 
226 curl --fail --location --silent --show-error --output "$archive" "$base_url/$archive"
227 curl --fail --location --silent --show-error --output "$archive.sigstore.json" "$base_url/$archive.sigstore.json"
228 cosign verify-blob \
229 --bundle "$archive.sigstore.json" \
230 --certificate-identity "https://github.com/modelcontextprotocol/registry/.github/workflows/release.yml@refs/tags/${MCP_PUBLISHER_VERSION}" \
231 --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
232 "$archive"
233 tar xzf "$archive" mcp-publisher
234 rm -f "$archive" "$archive.sigstore.json"
235 
170236 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
171237 # TODO: Replace `example.com` with your domain name
172238 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
from line 251
185251 
186252## Step 2: Add Secrets
187253 
188You may need to add a secret to the repository depending on which authentication method you choose:
254You may need to add a secret depending on which authentication method you choose:
189255 
190256* **GitHub OIDC Authentication**: No dedicated secret necessary.
191* **GitHub PAT Authentication**: Add a `MCP_GITHUB_TOKEN` secret with a GitHub Personal Access Token (PAT) that has `read:org` and `read:user` scopes.
257* **GitHub PAT Authentication**: Add a `MCP_GITHUB_TOKEN` secret with a GitHub Personal Access Token. The registry only needs to read your organization role to confirm you are an **Owner** before granting its namespace, so grant the minimum for that and **no** repository scopes (the registry never reads or writes your code):
258 
259 * **Classic PAT**: the `read:org` scope (`read:user` is *not* needed; a bare token still authenticates and publishes to your personal namespace).
260 * **Fine-grained PAT**: the **Organization permissions → Members → Read-only** permission (the equivalent of `read:org`). Without it you'll get your personal namespace only. A fine-grained PAT is scoped to one resource owner, so create it for the organization you're publishing to.
261 
262 For an **organization** token, store it as an **Environment secret** on the `mcp-registry-publish` environment rather than a plain repository secret — see "Securing your registry token in CI" below for why this matters and how to configure the environment.
192263* **DNS Authentication**: Add a `MCP_PRIVATE_KEY` secret with your Ed25519 private key.
193264 
194265You may also need to add secrets for your package registry. For example, the workflow above needs an `NPM_TOKEN` secret with your npm token.
from line 266
195266 
196267For information about how to add secrets to a repository, see [Using secrets in GitHub Actions](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).
197268 
269### Securing your registry token in CI
270 
271Whichever token you use to authenticate, treat it as a high-value credential. When you authenticate to an **organization** namespace, the resulting registry token can publish — and overwrite — **any** server under `io.github.<org>/*`, not just the one in this repository. The relevant question for your threat model is therefore: *who can cause code to run in a job where this secret is exposed?* By default that is **every repository writer** (via branch or tag pushes), not only org Owners — so a plain repo secret quietly widens publish access to everyone with write access.
272 
273GitHub gives you the tools to close that gap. We recommend, in increasing order of strength:
274 
2751. **Store the token as an [Environment secret](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments), not a repository or organization secret.** A job can only read an environment secret when it declares `environment:` (as the PAT example above does). Note that the `environment:` key alone protects nothing: if the environment does not exist yet, GitHub **auto-creates it with no protection rules** on first run, and a job that declares `environment:` still also receives ordinary repository and organization secrets. The protection comes from storing the token *as a secret on that environment* and configuring the environment (steps 2–3 below) — not from the `environment:` key by itself.
2762. **Restrict that environment to your default branch and/or release tags** with a [deployment branch rule](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments#deployment-branches-and-tags), and protect that branch with [required pull request reviews](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches). Now only reviewed, maintainer-approved code can ever reach the token.
2773. **(Strongest) Add a [required reviewer](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments#required-reviewers) to the environment** so each publish pauses for an explicit human approval.
278 
279Two pitfalls to avoid regardless of the above:
280 
281* **Never publish from a `pull_request_target` workflow that checks out PR-head code.** That trigger runs with your secrets in the base-repo context, so an untrusted fork PR could exfiltrate the token. Environment branch rules do **not** protect you here (the ref is still your base branch) — only required reviewers do.
282* **Avoid self-hosted runners for the publish job on public repositories**, where fork PRs may be able to schedule jobs onto them.
283 
284By default, pull requests **from forks** receive no secrets and cannot push branches, so external contributors cannot reach the token without one of the misconfigurations above.
285 
198286## Step 3: Tag and Release
199287 
200288Create and push a version tag to trigger the workflow:
from line 299
211299| Error Message | Action |
212300| - | - |
213301| "Authentication failed" | Ensure `id-token: write` permission is set for OIDC, or check secrets. |
302| "invalid audience" | Your `mcp-publisher` binary is too old for this registry deployment. Update `MCP_PUBLISHER_VERSION` to a compatible release and rerun the workflow. |
214303| "Package validation failed" | Verify your package successfully published to the package registry (e.g., npm, PyPI), and that your package has the [necessary verification information](https://modelcontextprotocol.io/registry/package-types.md). |
215304 

registry/package-types Changed · +6 / -1 lines

from line 84
8484<!-- mcp-name: io.github.username/database-query-mcp -->
8585```
8686 
87The `mcp-name:` token must be followed by a boundary — a newline, whitespace, an HTML tag, or the comment close `-->`. Keep it on its own line or inside `<!-- … -->`; do not glue it directly to trailing characters such as a sentence-ending period (`…/database-query-mcp.`), which prevents the match.
88 
8789## NuGet Packages
8890 
8991For NuGet packages, the MCP Registry currently supports the official NuGet registry (`https://api.nuget.org/v3/index.json`) only.
from line 124
122124<!-- mcp-name: io.github.username/azure-devops-mcp -->
123125```
124126 
127The `mcp-name:` token must be followed by a boundary — a newline, whitespace, an HTML tag, or the comment close `-->`. Keep it on its own line or inside `<!-- … -->`; do not glue it directly to trailing characters such as a sentence-ending period (`…/azure-devops-mcp.`), which prevents the match.
128 
125129## Cargo (Rust) Packages
126130 
127131For Cargo packages, the MCP Registry currently supports the official crates.io registry (`https://crates.io`) only.
from line 183
179183 
180184* Docker Hub (`docker.io`)
181185* GitHub Container Registry (`ghcr.io`)
186* Quay.io (`quay.io`)
182187* Google Artifact Registry (any `*.pkg.dev` domain)
183188* Azure Container Registry (`*.azurecr.io`)
184189* Microsoft Container Registry (`mcr.microsoft.com`)
from line 209
204209}
205210```
206211 
207The format of `identifier` is `registry/namespace/repository:tag`. For example, `docker.io/user/app:1.0.0` or `ghcr.io/user/app:1.0.0`. The tag can also be specified as a digest.
212The format of `identifier` is `registry/namespace/repository:tag`. For example, `docker.io/user/app:1.0.0`, `ghcr.io/user/app:1.0.0`, or `quay.io/myorg/my-mcp-server:1.0.0`. The tag can also be specified as a digest.
208213 
209214### Ownership Verification
210215 

registry/quickstart Changed · +50 / -13 lines

## Remote-only servers

from line 6
66 
77This tutorial will show you how to publish an MCP server written in TypeScript to the MCP Registry using the official `mcp-publisher` CLI tool.
88 
9> [!NOTE]
10>
11> If you are publishing a non-npm package (PyPI, NuGet, OCI, MCPB), the overall flow is identical, but the ownership-verification step in [Step 1](#step-1-add-verification-information-to-the-package) is different per package type. See [Package Types](https://modelcontextprotocol.io/registry/package-types.md) for the verification mechanism that applies to your package, then return here and follow the remaining steps.
12 
13## Remote-only servers
14 
15If your MCP server is available only at a public remote endpoint, you can publish its metadata without a package. Skip the Node.js and npm prerequisites and Steps 1–2, then continue with [Step 3](#step-3-install-mcp-publisher). Your `server.json` must include a `remotes` entry and can omit `packages`:
16 
17```json server.json theme={null}
18{
19 "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
20 "name": "io.github.my-username/weather",
21 "title": "Weather",
22 "description": "A remote MCP server for weather information.",
23 "repository": {
24 "url": "https://github.com/my-username/mcp-weather-server",
25 "source": "github"
26 },
27 "version": "1.0.0",
28 "remotes": [
29 {
30 "type": "streamable-http",
31 "url": "https://weather.example.com/mcp"
32 }
33 ]
34}
35```
36 
37The remote URL must be publicly accessible. See [Publishing Remote Servers](https://modelcontextprotocol.io/registry/remote-servers.md) for transport, URL variable, and header options.
38 
39With any authentication method, the Registry verifies that you control the namespace in `name`; it does not verify that you control the remote URL. See [Ownership Verification](https://modelcontextprotocol.io/registry/remote-servers.md#ownership-verification) for details.
40 
941## Prerequisites
1042 
1143* **Node.js** — This tutorial assumes the MCP server is written in TypeScript.
12* **npm account** — The MCP Registry only hosts metadata, not artifacts. Before publishing to the MCP Registry, we will publish the MCP server's package to npm, so you will need an [npm](https://www.npmjs.com) account.
44* **npm account (package-based servers only)** — The MCP Registry only hosts metadata, not artifacts. Before publishing a package-based MCP server to the MCP Registry, we will publish the package to npm, so you will need an [npm](https://www.npmjs.com) account.
1345* **GitHub account** — The MCP Registry supports [multiple authentication methods](https://modelcontextprotocol.io/registry/authentication.md). For simplicity, this tutorial will use GitHub-based authentication, so you will need a [GitHub](https://github.com/) account.
1446 
1547If you do not have an MCP server written in TypeScript, you can copy the `weather-server-typescript` server from the [`modelcontextprotocol/quickstart-resources` repository](https://github.com/modelcontextprotocol/quickstart-resources) to follow along with this tutorial:
1648 
1749```bash theme={null}
18git clone --depth 1 [email protected]:modelcontextprotocol/quickstart-resources.git
50git clone --depth 1 https://github.com/modelcontextprotocol/quickstart-resources.git
1951cp -r quickstart-resources/weather-server-typescript .
2052rm -rf quickstart-resources
2153cd weather-server-typescript
from line 98
6698Ensure the distribution files are built:
6799 
68100```bash theme={null}
69# Navigate to project directory
70cd weather-server-typescript
71 
72101# Install dependencies
73102npm install
74103 
from line 130
101130# Move mcp-publisher.exe to a directory in your PATH
102131```
103132 
104```bash theme={null}
133```bash Homebrew theme={null}
105134brew install mcp-publisher
106135```
107136 
from line 153
124153 login Authenticate with the registry
125154 logout Clear saved authentication
126155 publish Publish server.json to the registry
156 status Update the status of a server version
157 validate Validate server.json without publishing
158 
159Use 'mcp-publisher <command> --help' for more information about a command.
127160```
128161 
129162## Step 4: Create `server.json`
from line 163
130163 
131164The `mcp-publisher init` command can generate a `server.json` template file with some information derived from your project.
132165 
166The Registry limits the `description` field to 100 characters. Keep the description focused on what the server does; longer descriptions fail validation.
167 
133168In your server project directory, run `mcp-publisher init`:
134169 
135170```bash theme={null}
from line 182
147182 "url": "https://github.com/my-username/mcp-weather-server",
148183 "source": "github"
149184 },
150 "version": "1.0.0",
185 "version": "1.0.1",
151186 "packages": [
152187 {
153188 "registryType": "npm",
154189 "identifier": "@my-username/mcp-weather-server",
155 "version": "1.0.0",
190 "version": "1.0.1",
156191 "transport": {
157192 "type": "stdio"
158193 },
from line 216
181216 "url": "https://github.com/my-username/mcp-weather-server",
182217 "source": "github"
183218 },
184- "version": "1.0.0",
185+ "version": "1.0.1",
219 "version": "1.0.1",
186220 "packages": [
187221 {
188222 "registryType": "npm",
189223 "identifier": "@my-username/mcp-weather-server",
190- "version": "1.0.0",
191+ "version": "1.0.1",
224 "version": "1.0.1",
192225 "transport": {
193226 "type": "stdio"
194227- },
from line 242
209242 
210243The `name` property in `server.json` **must** match the `mcpName` property in `package.json`.
211244 
245> [!NOTE]
246>
247> For a [remote-only server](#remote-only-servers), `mcp-publisher init` still generates a `packages` entry. Replace the `packages` array with a `remotes` array, as shown in the remote-only example above.
248 
212249## Step 5: Authenticate with the MCP Registry
213250 
214251For this tutorial, we will authenticate with the MCP Registry using GitHub-based authentication.
from line 307
270307 
271308| Error Message | Action |
272309| - | - |
273| "Registry validation failed for package" | Ensure your package includes the required validation information (e.g, `mcpName` property in `package.json`). |
310| "Registry validation failed for package" | Ensure your package includes the required ownership-verification marker for its package type. For npm this is `mcpName` in `package.json`; for PyPI and NuGet it is an `mcp-name: <server-name>` line (or HTML comment) in the package README; for other types see [Package Types](https://modelcontextprotocol.io/registry/package-types.md). |
274311| "Invalid or expired Registry JWT token" | Re-authenticate by running `mcp-publisher login github`. |
275312| "You do not have permission to publish this server" | Your authentication method doesn't match your server's namespace format. With GitHub auth, your server name must start with `io.github.your-username/`. |
276313 

registry/registry-aggregators Changed · +6 / -0 lines

### Efficient synchronization

from line 60
6060curl "https://registry.modelcontextprotocol.io/v0.1/servers?updated_since=2025-10-23T00:00:00.000Z"
6161```
6262 
63### Efficient synchronization
64 
65After the initial paginated download, use `updated_since` for subsequent syncs instead of downloading the entire registry again. Keep the same timestamp while following every `nextCursor`, and save a new checkpoint only after all pages have been applied successfully. Keep a small overlap between sync windows and deduplicate by server name and version so retries and timestamp boundaries do not lose updates. Incremental results include deleted servers; apply those status changes to your local copy too.
66 
67Use a client that negotiates and decodes HTTP compression, for example `curl --compressed`. Persist results locally and avoid repeating a full crawl for each user request. If a request fails or is rate limited, retry with backoff rather than restarting concurrent full downloads.
68 
6369## Server Status
6470 
6571Server metadata is generally immutable, except for the `status` field which may be updated to, e.g., `"deprecated"` or `"deleted"`. We recommend that aggregators keep their copy of each server's `status` up to date.

registry/remote-servers Changed · +15 / -0 lines

## Ownership Verification

from line 24
2424 
2525A remote server **MUST** be publicly accessible at its specified URL.
2626 
27A server with only `remotes` does not need a `packages` entry, so the package ownership verification described in [Package Types](https://modelcontextprotocol.io/registry/package-types.md) does not apply.
28 
29## Ownership Verification
30 
31When you publish, the Registry verifies that you control the namespace in the server `name` using your [authentication method](https://modelcontextprotocol.io/registry/authentication.md). It does **not** verify that you control the remote URL, regardless of authentication method:
32 
33* With [GitHub authentication](https://modelcontextprotocol.io/registry/authentication.md#github-authentication), a server named `io.github.my-username/weather` proves control of the `my-username` GitHub account, not of the domain serving the remote URL.
34* With [DNS authentication](https://modelcontextprotocol.io/registry/authentication.md#dns-authentication) or [HTTP authentication](https://modelcontextprotocol.io/registry/authentication.md#http-authentication), a server named `com.example/weather` proves control of `example.com`, but the Registry does not check that the remote URL is on `example.com`.
35 
36If your remote URL is on a domain you control, consider using DNS or HTTP authentication so the server name reflects that domain. This lets users see that the server name and remote URL belong to the same publisher.
37 
38Multiple server names can reference the same remote URL. An existing listing does not reserve that URL or prevent another publisher from listing it under a namespace they control. Sharing a URL does not grant permission to publish, edit, or change the status of another publisher's listing.
39 
40Consumers should identify registry records by server name and version, not by remote URL. A URL lookup may match multiple server names, each with its own metadata, headers, and template variables. Do not merge configurations or infer publisher ownership or endorsement from a shared URL.
41 
2742## Transport Type
2843 
2944Remote servers should use the Streamable HTTP transport. The SSE transport is [deprecated](https://modelcontextprotocol.io/specification/draft/deprecated.md), so publish an `"sse"` remote only to support existing clients. Remote servers can also support both transports simultaneously at different URLs.
Feedback