Repository navigation
fix(validators): harden mcp-name matching (PyPI/NuGet anchoring, comment-form safe) + cargo follow-ups - #1331
Merged
Merged
Conversation
rdimitrov
force-pushed
the
followup/cargo-hardening
branch
from
June 4, 2026 20:08
bc87b70 to
d796057
Compare
rdimitrov
force-pushed
the
followup/registry-anchoring
branch
from
June 4, 2026 20:08
5b3e6b5 to
57aacf9
Compare
rdimitrov
marked this pull request as ready for review
June 4, 2026 21:35
…uGet Stacked on the cargo follow-up (introduces containsMCPNameToken). This extends the boundary-anchored ownership-token match to the PyPI and NuGet validators, replacing their bare strings.Contains checks so a README declaring a longer name (e.g. io.github.acme/widget-pro) no longer satisfies a claim for a shorter prefix (io.github.acme/widget).⚠️ BEHAVIOR CHANGE for PyPI/NuGet (not just additive): The new match is strictly stricter — it can only flip a previously-passing publish to failing, never the reverse. The realistic case that flips is a README whose ONLY occurrence of the token is immediately followed by a server-name character [A-Za-z0-9._/-], e.g. a trailing period in prose ("...published as mcp-name: io.github.acme/widget."). The token on its own line, in backticks, or followed by whitespace/newline/HTML-tag is unaffected. Re-validation runs only at publish time (CreateServer); edits/status updates do not re-check ownership and there is no background re-validation, so already- stored servers are not affected — but an existing PyPI/NuGet publisher pushing a NEW VERSION with the token in the glued form would fail where it previously passed. Given the v0.1 API freeze, this should land deliberately and not be promoted to prod without sign-off. Live positive tests (time-mcp-pypi, TimeMcpServer) still pass. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The boundary-anchored matcher rejected the documented hidden-comment form when the trailing space was omitted: `<!-- mcp-name: NAME-->` / `<!--mcp-name: NAME-->` fail because the byte after NAME is `-` (a server-name char). Since PyPI/NuGet publishers commonly hide the token in an HTML comment, this would break the recommended form on the next publish or edit. Add isMCPNameBoundary, which treats the HTML comment close (`-->` / `--!>`) immediately after the name as a boundary, so all spacing variants of the comment form validate while a genuine longer name (e.g. `…/widget--pro`) still does not. Tests: comment-form cases (spaced/unspaced/legacy `--!>`) and a double-hyphen longer-name negative; plus FuzzContainsMCPNameToken pinning the safety property that the matcher is strictly stricter than strings.Contains (can only flip pass→fail, never fail→pass) — verified over 2.3M executions. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…en 403 Three follow-up hardening fixes to the cargo validator (review of #1330): - SSRF: the README host pin and the redirect policy keyed on Hostname() only, so any port/scheme on crates.io/static.crates.io was accepted. cargoURLAllowed now additionally requires https + the default port for the real crates.io base (test/mock bases still match on host only, so httptest fixtures keep working). - Transient 403 disambiguation: when the README CDN 403s, the crate-version existence probe previously reported "not found" if the probe itself failed (429/5xx/network) — the same misclassification the 5xx handling fixed one layer up. probeCargoVersion now returns a four-state result and a transient probe yields a retryable message instead of "not found". - A 403 with the crate present no longer flatly asserts "no rendered README" (a 403 isn't definitive proof — could be a CDN/WAF block); the message now says the README could not be retrieved and gives the actionable next step. Tests: TestCargoURLAllowed (https/port/userinfo/foreign-host matrix) and a combined-fixture case where the 403 existence-probe is rate-limited (429) and must report transient, not "not found". Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Note in the PyPI and NuGet ownership sections that the token must be followed by a newline, whitespace, an HTML tag, or the comment close `-->`, and must not be glued to trailing punctuation (e.g. a sentence-ending period). The matcher fix handles the comment-close case; this documents the remaining boundary rule. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
rdimitrov
force-pushed
the
followup/registry-anchoring
branch
from
June 5, 2026 06:09
55f72ed to
165c1bf
Compare
If a publisher's README contains `mcp-name: NAME` but the boundary-anchored match rejects it (the token is glued to a trailing character such as a sentence-ending period or `/`), the previous error said the name "must appear as 'mcp-name: NAME' in the package README" — which the publisher sees that it already does, giving no clue what's wrong. This is the failure mode of the registry-wide anchoring change, so the message needs to name the cause. Add mcpNameTokenGluedTrailing, which reports the offending trailing character when the literal token is present but unterminated, and use it in the PyPI, Cargo, and NuGet validators to emit an actionable message: "found 'mcp-name: NAME' but it is immediately followed by 'X' — put it on its own line and republish". NuGet gains a GluedReadme state to distinguish this from a genuinely-absent token. Tests: TestMCPNameTokenGluedTrailing for the helper, and a cargo combined-fixture case asserting the explanatory message on a glued trailing period. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
rdimitrov
added a commit
that referenced
this pull request
Jul 13, 2026
Promotes production to the [`v1.8.0`](https://github.com/modelcontextprotocol/registry/releases/tag/v1.8.0) release. - `deploy/Pulumi.gcpProd.yaml` → `imageTag: 1.8.0` (one line). - The release build for `v1.8.0` completed successfully, so the image is present in ghcr. - **Merging this triggers `deploy-production.yml`** (it's path-filtered to `deploy/Pulumi.gcpProd.yaml`); the PR itself doesn't deploy. ### Notable behavior changes in v1.8.0 - **Org-namespace publishing now requires org Owner** (via GitHub login / PAT). Member-level publishers lose org publish — intended tightening. - Stricter validation may reject some previously-accepted package metadata / names (#1310, #1331, #1411). - New: cargo (crates.io) package registry support (#1207). ### After merge - Confirm prod `/v0/version` reports `1.8.0` before treating the rollout as done. Co-authored-by: Claude Opus 4.8 <[email protected]>
social4hyq
pushed a commit
to social4hyq/homebrew-core
that referenced
this pull request
Sep 20, 2026
mcp-publisher 1.8.0 Created-by: HarmonybrewBot Commit-by: HarmonybrewBot Merged-by: HarmonybrewBot Description: Created by `brew bump` --- Created with `brew bump-formula-pr`.<details> <summary>release notes</summary> <pre>## What's Changed * deploy: update prod to v1.7.9 by @rdimitrov in modelcontextprotocol/registry#1282 * build(deps): bump github.com/pulumi/pulumi/sdk/v3 from 3.237.0 to 3.238.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1283 * chore(deps): reduce dependabot frequency from daily to weekly by @rdimitrov in modelcontextprotocol/registry#1284 * build(deps): bump github.com/go-git/go-git/v5 from 5.19.0 to 5.19.1 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1292 * build(deps): bump github.com/google/go-containerregistry from 0.21.5 to 0.21.6 by @dependabot[bot] in modelcontextprotocol/registry#1295 * build(deps): bump github.com/danielgtaylor/huma/v2 from 2.37.3 to 2.38.0 by @dependabot[bot] in modelcontextprotocol/registry#1296 * build(deps): bump github.com/pulumi/pulumi/sdk/v3 from 3.238.0 to 3.242.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1297 * build(deps): bump goreleaser/goreleaser-action from 7.2.1 to 7.2.2 in the actions group by @dependabot[bot] in modelcontextprotocol/registry#1299 * build(deps): bump golang.org/x/net to v0.55.0 by @rdimitrov in modelcontextprotocol/registry#1308 * build(deps): bump github.com/pulumi/pulumi-kubernetes/sdk/v4 from 4.30.0 to 4.31.1 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1298 * docs: update maintainer/collaborator matrix in README by @tadasant in modelcontextprotocol/registry#1324 * build(deps): bump the actions group with 2 updates by @dependabot[bot] in modelcontextprotocol/registry#1314 * build(deps): bump github.com/pulumi/pulumi/sdk/v3 from 3.242.0 to 3.243.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1313 * build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys from 1.4.0 to 1.5.0 by @dependabot[bot] in modelcontextprotocol/registry#1312 * build(deps): bump the opentelemetry group with 5 updates by @dependabot[bot] in modelcontextprotocol/registry#1311 * feat: add cargo (crates.io) as a package registry type by @Wolfe-Jam in modelcontextprotocol/registry#1207 * build(deps): bump go.opentelemetry.io/contrib/instrumentation/runtime from 0.68.0 to 0.69.0 in the opentelemetry group by @dependabot[bot] in modelcontextprotocol/registry#1332 * build(deps): bump github.com/pulumi/pulumi/sdk/v3 from 3.243.0 to 3.244.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1333 * build(deps): bump github.com/jackc/pgx/v5 from 5.9.2 to 5.10.0 by @dependabot[bot] in modelcontextprotocol/registry#1334 * fix(cargo): harden README fetch, clarify status handling, strengthen tests by @rdimitrov in modelcontextprotocol/registry#1330 * fix(validators): harden mcp-name matching (PyPI/NuGet anchoring, comment-form safe) + cargo follow-ups by @rdimitrov in modelcontextprotocol/registry#1331 * fix: client-cancelled GET /v0/servers returns 499 without error log by @advancedresearcharray in modelcontextprotocol/registry#1335 * fix: don't leak internal error detail in GET /v0/servers 500 response by @rdimitrov in modelcontextprotocol/registry#1338 * build(deps): bump the actions group with 2 updates by @dependabot[bot] in modelcontextprotocol/registry#1392 * build(deps): bump github.com/pulumi/pulumi/sdk/v3 from 3.244.0 to 3.248.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1391 * build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity from 1.13.1 to 1.14.0 by @dependabot[bot] in modelcontextprotocol/registry#1375 * build(deps): bump github.com/coreos/go-oidc/v3 from 3.18.0 to 3.19.0 by @dependabot[bot] in modelcontextprotocol/registry#1374 * build(deps): bump github.com/google/go-containerregistry from 0.21.6 to 0.21.7 by @dependabot[bot] in modelcontextprotocol/registry#1373 * Fix broken reference documentation links by @kriptoburak in modelcontextprotocol/registry#1387 * build(deps): bump github.com/pulumi/pulumi-kubernetes/sdk/v4 from 4.31.1 to 4.32.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1353 * ci: auto-close PRs that try to publish servers via the repo by @rdimitrov in modelcontextprotocol/registry#1393 * build(deps): bump golang.org/x/mod from 0.37.0 to 0.38.0 by @dependabot[bot] in modelcontextprotocol/registry#1428 * build(deps): bump github.com/coreos/go-oidc/v3 from 3.19.0 to 3.20.0 by @dependabot[bot] in modelcontextprotocol/registry#1429 * build(deps): bump the actions group across 1 directory with 4 updates by @dependabot[bot] in modelcontextprotocol/registry#1430 * build(deps): bump github.com/pulumi/pulumi/sdk/v3 from 3.248.0 to 3.251.0 in /deploy by @dependabot[bot] in modelcontextprotocol/registry#1431 * fix(auth): grant org namespace only to org Owners, not all members by @tadasant in modelcontextprotocol/registry#1383 * build(deps): bump golang.org/x/crypto from 0.51.0 to 0.52.0 by @dependabot[bot] in modelcontextprotocol/registry#1433 * docs(auth): document fine-grained PAT permission for org publishing (+ pagination-cap test) by @rdimitrov in modelcontextprotocol/registry#1435 * docs: add polygraph to community projects by @RubenSousaDinis in modelcontextprotocol/registry#1421 * fix(ci): re-validate author association via REST in close-invalid-publish-prs by @rdimitrov in modelcontextprotocol/registry#1440 * fix: reject mangled publisher metadata by @he-yufeng in modelcontextprotocol/registry#1310 * fix(api): allow PATCH in CORS so browsers can call the status endpoints by @JosephDoUrden in modelcontextprotocol/registry#1436 * test(api): remove placeholder CORS test superseded by #1436 by @rdimitrov in modelcontextprotocol/registry#1441 * fix(validators): distinguish a missing package from a missing version on PyPI/NPM by @sronix in modelcontextprotocol/registry#1411 ## New Contributors * @Wolfe-Jam made their first contribution in modelcontextprotocol/registry#1207 * @advancedresearcharray made their first contribution in modelcontextprotocol/registry#1335 * @kriptoburak made their first contribution in modelcontextprotocol/registry#1387 * @RubenSousaDinis made their first contribution in modelcontextprotocol/registry#1421 * @he-yufeng made their first contribution in modelcontextprotocol/registry#1310 * @sronix made their first contribution in modelcontextprotocol/registry#1411 **Full Changelog**: https://github.com/modelcontextprotocol/registry/compare/v1.7.9...v1.8.0</pre> <p>View the full release notes at <a href="https://github.com/modelcontextprotocol/registry/releases/tag/v1.8.0">https://github.com/modelcontextprotocol/registry/releases/tag/v1.8.0</a>.</p> </details> <hr> See merge request: Harmonybrew/homebrew-core!14181
dsp-ant
added a commit
to modelcontextprotocol/modelcontextprotocol
that referenced
this pull request
Nov 12, 2026
<!-- claude-slack-attribution --> _Requested by **Den Delimarsky, David Soria Parra** · [Slack thread](https://ant-mcp-oss.slack.com/archives/C0BDGKP4PC6/p1793430342149349)_ Squash-merge with this description as the commit message. The two trailers on the last lines must stay in it, so that the next subtree pull starts from this one. **Before:** `docs/registry/` reflects the registry docs as of 2026-01-22 (registry commit 817e1216), plus six local edits made since. The FAQ says servers cannot be deleted, the P-384 login command lacks the `--algorithm` flag, the organization namespace rules are missing, and the remote servers page says a remote URL can be used by only one server name. **After:** `docs/registry/` is byte-identical to `docs/modelcontextprotocol-io` in modelcontextprotocol/registry at tag `v1.8.9` (commit ba94f657). Seven pages change (+308/-25): authentication, faq, github-actions, package-types, quickstart, registry-aggregators and remote-servers. ## How `scripts/pull-registry-docs.sh` was run against a clone of the registry, first at `v1.8.7` and then at `v1.8.9`. The script splits `docs/modelcontextprotocol-io` and squash-pulls the result into `docs/registry/`. `npm run prep` passes, link check included. The `v1.8.9` pull adds two upstream changes. Since `v1.8.8` several server names can share one remote URL (modelcontextprotocol/registry#1806), which replaces the sentence at remote-servers.mdx L41. The aggregators page gains an "Efficient synchronization" section (modelcontextprotocol/registry#1798). A later run of the script was tested against a single squash commit that carries these trailers. It merged cleanly and changed only `docs/registry/`. ## Conflict resolutions Three files conflicted in the `v1.8.7` pull. In each case upstream had added content next to a line that a local commit had also touched, and upstream already carried the local change, so upstream's side was taken. The `v1.8.9` pull had no conflicts. - `github-actions.mdx`: new troubleshooting row for the `"invalid audience"` error. - `package-types.mdx`: new paragraph on the `mcp-name:` token boundary, placed before the Cargo section that both sides carry identically. - `quickstart.mdx`: the npm account prerequisite reworded to apply to package-based servers only. ## Local commits since January All six are carried by upstream, so nothing needs to go upstream. - 5abdf44 removed `.mdx` suffixes from internal links. Upstream did the same in modelcontextprotocol/registry#1052, and no suffixed links remain there. - 31dae91 pointed the server.json schema link at `draft/server.schema.json`. Upstream has it from modelcontextprotocol/registry#884. - 91dcd51 added the SSE deprecation note to remote-servers. It was superseded locally by 85bbf0e. - 85bbf0e set the final SSE deprecation wording. Upstream has the identical sentence from modelcontextprotocol/registry#1590. - 0d17c43 changed `PyPi` to `PyPI` in the FAQ. Upstream has it at faq.mdx L55. - c0b3579 added the Cargo (Rust) package type section. Upstream has the identical section from modelcontextprotocol/registry#1207 and modelcontextprotocol/registry#1331. ## Upstream follow-ups for the registry repo (not fixed here) 1. `authentication.mdx` contradicts itself on stale DNS records. L81 says a stale TXT record left behind "will be tried first and cause verification to fail". L404 says the registry tries all published keys and a different key in another record does not take precedence. The code agrees with L404: `VerifySignatureWithKeys` in `internal/api/handlers/v0/auth/common.go` accepts the first matching key and fails only when none match. 2. `quickstart.mdx` L130 and L134 install `mcp-publisher` from `releases/latest`, while `github-actions.mdx` L14-17 tells readers not to use that URL. The three workflow examples in `github-actions.mdx` (L35, L115, L191) pin `MCP_PUBLISHER_VERSION: v1.8.1`, behind the `v1.8.9` tag. 3. `registry-aggregators.mdx` L78 still says a `"deleted"` status typically indicates a moderation takedown. Since publisher-managed deletion (modelcontextprotocol/registry#893) publishers set that status themselves. `moderation-policy.mdx` also needs attention. Neither passage has changed upstream. This PR was drafted with AI assistance (Claude Code). git-subtree-dir: docs/registry git-subtree-split: 5d7060497a1d4535dc448dd56ec699ea03c89efd --------- Co-authored-by: Claude <[email protected]> Co-authored-by: David Soria Parra <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Registry-wide hardening of the
mcp-name:ownership-token match, plus the cargo follow-up fixes from the #1330 review — consolidated here per maintainer request. Rebased onto currentmain(the merged #1330 cargo commit drops out), so the diff is just the net-new work.Changes
strings.Contains(readme, "mcp-name: "+name)with the sharedcontainsMCPNameToken, so a README declaring a longer name (…/widget-pro) no longer satisfies an ownership claim for a shorter prefix (…/widget). (Cargo already got this in fix(cargo): harden README fetch, clarify status handling, strengthen tests #1330; NPM is unaffected — it compares an exact metadata field.)<!-- mcp-name: NAME-->/<!--mcp-name: NAME-->(any spacing) validate again — the documented hidden-comment form for PyPI/NuGet — while a genuine longer name (…/widget--pro) still does not.The match is strictly stricter (verified by fuzz, 2.3M execs: it can only flip pass→fail, never fail→pass). After the comment-close fix, the only forms that newly fail are unusual inline ones: the token ending a sentence (
…/my-mcp.) or glued to a trailing/. The documented forms (own line, in<!-- … -->) are unaffected.Correcting an earlier claim: this re-validates on edits too, not just new versions —
edit.go → UpdateServer → ValidateUpdateRequest → validateRegistryOwnershipruns the token check on any edit of a live server (only delete-transitions skip it). So an existing PyPI/NuGet server whose README uses one of the breaking inline forms would fail on its next publish or edit. Given the v0.1 API-freeze posture, this should land as a deliberate, noted change.Testing
go build,vet,gofmt,golangci-lint(prod files),check-schema,validate-examplesall clean. Hermetic + live PyPI/NuGet/cargo suite passes. New:TestCargoURLAllowed, a combined-fixture probe-429→transient case, comment-form matcher cases, andFuzzContainsMCPNameToken(the strictly-stricter property, 2.3M execs).🤖 Generated with Claude Code