Commit graph registry/internal/validators/registries
Author SHA1 Message Date
Thomas Thomsen
d813d2b805
fix(validators): distinguish a missing package from a missing version on PyPI/NPM (#1411)
Fixes #553.

The PyPI and npm validators fetch the version-specific metadata endpoint
(`/pypi/{name}/{version}/json` and the npm equivalent) and report any
non-200 as `<pkg> not found`. That endpoint 404s for two different
reasons though: the package doesn't exist, or it exists and only the
*version* is missing (e.g. a release that hasn't propagated yet). So a
valid package reads as gone:

```diff
- PyPI package 'requests' not found (status: 404)
+ PyPI package 'requests' exists, but version '99.99.99' was not found (status: 404). A newly published release can take a moment to appear on PyPI. Wait and retry, or publish version '99.99.99' before registering it
```

On a version 404 it probes the package-level endpoint
(`/pypi/{name}/json`, `/{name}`) with a `HEAD` to tell the two apart,
and reports 429/5xx as transient rather than "not found". `HEAD` so it
reads a status without pulling the whole packument. The probe carries
its own 3s deadline: it only refines the error message, so a hung probe
must not stretch the validator past the ~10s-per-registry budget the
publish path assumes. Same probe-and-classify shape as the existing
cargo validator.

The fetch is split into `validatePyPIPackage` / `validateNPMPackage`
behind `export_test.go` so the branches are testable with `httptest`,
the same seam cargo uses.

### Testing
- [x] hermetic `httptest` for the status matrix (version-missing,
package-missing, 5xx, 429, inconclusive probe, positive path, scoped
npm); the mocks pin the expected method per endpoint (`GET` fetch,
`HEAD` probe)
- [x] hermetic deadline test: a hung probe is cut off at ~3s and
reported as inconclusive, not "not found"
- [x] existing live package tests still pass, now also hitting the HEAD
probe; on a live 429/5xx they now `t.Skip` as inconclusive instead of
flaking (deliberate: CI runs these against the real registries with no
short-mode gating)
- [x] `gofmt` / `go vet` / `golangci-lint` clean

Out of scope: auto-retry for the propagation race, and SSRF
redirect-pinning parity for the pypi/npm clients (pre-existing, shared
with nuget).
2026-07-12 15:24:34 -04:00
Radoslav Dimitrov
04623ed925
fix(validators): harden mcp-name matching (PyPI/NuGet anchoring, comment-form safe) + cargo follow-ups (#1331)
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 current `main` (the merged #1330 cargo
commit drops out), so the diff is just the net-new work.

### Changes
- **PyPI/NuGet: boundary-anchored token match.** Replace
`strings.Contains(readme, "mcp-name: "+name)` with the shared
`containsMCPNameToken`, 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 #1330; NPM is unaffected
— it compares an exact metadata field.)
- **Matcher: treat the HTML comment close as a boundary.** `<!--
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.
- **Cargo hardening (review of #1330):** pin scheme+port (not just host)
on the README fetch and redirects; a rate-limited/failed crate-version
existence probe now reports *transient* instead of "not found"; a 403
with the crate present no longer flatly asserts "no README".
- **Docs:** note the token must be followed by a boundary.

### ⚠️ Behavior change for PyPI/NuGet — read before merging
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 →
validateRegistryOwnership` runs 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-examples` all clean. Hermetic + **live**
PyPI/NuGet/cargo suite passes. New: `TestCargoURLAllowed`, a
combined-fixture probe-429→transient case, comment-form matcher cases,
and `FuzzContainsMCPNameToken` (the strictly-stricter property, 2.3M
execs).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:28:41 -04:00
Radoslav Dimitrov
7a6d1bc30a
fix(cargo): harden README fetch, clarify status handling, strengthen tests (#1330)
Follow-up to #1207, addressing items from the cargo validator review.
**Scoped to cargo + shared helpers — no behavior change for existing
npm/pypi/nuget/oci/mcpb publishers.** Safe to merge and promote.

### Changes
**SSRF hardening of the two-call README retrieval**
- Pin the step-2 README URL to an allowed host (`static.crates.io` in
prod; the base host under test) *before* fetching, so a metadata
response can't steer the validator at an internal/attacker host.
- `CheckRedirect` policy pins every redirect hop to the same allowlist
(the initial URL being pinned isn't enough if an upstream 3xx is
followed).
- Cap the README body with `io.LimitReader` (5 MiB).

> Not publisher-exploitable today (the URL comes from crates.io), so
this is defense-in-depth — but it makes the "host is pinned" guarantee
hold in code rather than rely on crates.io's behavior.

**Clearer status handling** (previously any non-5xx/non-200 collapsed to
"not found")
- **429** → reported as transient/retryable, at both the metadata and
README steps.
- **403** → disambiguated via the crate-version endpoint:
genuinely-missing stays "not found"; *exists-but-no-README* gets an
actionable "add a README with `mcp-name` and republish" message (mirrors
the NuGet validator). This continues the 5xx-vs-403 diagnostic split
@P4ST4S started in the #1207 review — thanks!

**Shared `containsMCPNameToken` helper**
Boundary-anchored ownership-token match (prevents prefix confusion, e.g.
a README declaring `io.github.acme/widget-pro` satisfying a claim for
`io.github.acme/widget`), used here **by the cargo validator only**.
Adopting it in PyPI/NuGet is a separate follow-up because it's a
behavior change for those already-live registries.

**Tests & docs**
Hermetic positive `ServerNameFormats`; combined fixture gains a
version-existence endpoint + cases for 403-missing vs 403-no-README,
429, and prefix-confusion rejection; new foreign-README-host (SSRF)
test; internal test for the helper. Docs: list `crates.io` in the
registry-requirements list + the `Package` model doc.

### Testing
`go build`, `go vet`, full `./internal/validators/...` suite (incl. live
cargo positive), `make check-schema`, `validate-examples` all pass. No
new `golangci-lint` findings.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:34:54 -04:00
Wolfe-James
685e354b2f
feat: add cargo (crates.io) as a package registry type (#1207)
> **Update — 2026-06-01 (commit 1f3be53)**
>
> - **All @P4ST4S review feedback addressed** — positive-path hermetic
mock + live anchor against `rust-faf-mcp v0.3.1`; 5xx routed as
transient; doc comment qualified. See the [close-out
comment](https://github.com/modelcontextprotocol/registry/pull/1207#issuecomment-4589711057)
for the per-item breakdown.
> - **Runtime-model question RESOLVED** — `package-types.mdx` documents
cargo and MCPB as both first-class Rust MCP distribution paths (cargo
for source-distributed, MCPB for prebuilt-binary). The "Open question"
section below was the original draft framing; the doc now ships the
resolution.
> - **Cargo-specific gotcha discovered and documented** — crates.io
strips HTML comments during README rendering, unlike PyPI/NuGet.
`package-types.mdx` now spells this out so cargo authors don't ship
invisible mcp-name tokens (which is exactly what happened to
`rust-faf-mcp v0.3.0`; fixed in v0.3.1).
> - **Stale reference removed in this update** — the "Out of scope" line
mentioning `docs/guides/publishing/publish-cargo.md` pointed at a path
that doesn't exist. The canonical per-type doc is
`docs/modelcontextprotocol-io/package-types.mdx`; the Cargo section has
been added there directly.
> - **Two real-world Rust MCP servers queued for day-one register**:
`rust-faf-mcp v0.3.1` (live, end-to-end validated by the new live test)
and `perfetto-mcp-rs` ([@0xZOne](https://github.com/0xZOne) in #1055).
> - **Tests:** 19/19 cargo tests pass (16 existing + 2 hermetic + 1 live
anchor). `make validate` clean. `golangci-lint` 0 issues.

---

Adds support for `registryType: cargo` so Rust MCP servers published to
crates.io can be registered through the
documented validation flow rather than the MCPB binary-packaging
workaround. Closes #1055.

  ## Motivation and Context

#1055 noted that crates.io has ~1,800 MCP-related packages with no
direct path into the registry — only the MCPB
binary-packaging workaround. This PR adds first-class support for cargo
as a package registry type: schema +
API surface, validator, integration tests, and a documented example. The
publishing guide
(`docs/guides/publishing/publish-cargo.md`) follows on this branch once
the runtime-model direction in
  **Additional context** is settled.

  ## How Has This Been Tested?

- **`make validate`** — clean. Schema-vs-openapi sync verified; 17/17
examples in `generic-server-json.md`
validate against the schema; `expectedServerJSONCount` bumped 16 → 17 to
match the new Cargo example.
- **`go test ./internal/validators/...`** — passes. 16 cargo validator
sub-cases across 4 test functions, run
  against real crates.io (~2.5s wall):
    - Input validation rejection paths
- Registry-baseURL rejection (4 variants: different host, trailing
slash, http-not-https, subdomain typo)
- Ownership validation against real crates (`serde`, `tokio`, `rand`) —
all correctly rejected for missing
  `mcp-name` token
- Server-name format variations (canonical `io.github.OWNER/REPO`,
multi-hyphen, underscore, numeric suffix)
- **Positive-path test** — gated on `rust-faf-mcp` v0.2.3+ being
published with `mcp-name:
io.github.Wolfe-Jam/rust-faf-mcp` in its README. Reserved as a TODO in
`cargo_test.go`; uncomments to become the
   live anchor once that publish lands.
- **Local `make test-unit`** — not run due to a Go toolchain version
quirk on my workstation (project
auto-downloads 1.26.0; my local `go` is 1.25.6, causing compile
mismatch). CI will exercise the full
  PostgreSQL-backed suite.

  ## Breaking Changes

None. This is an additive change — `cargo` joins the supported
`registryType` enum alongside
`npm`/`pypi`/`nuget`/`oci`/`mcpb`. Existing publishes continue to work
unchanged.

  ## Types of changes

  - [x] New feature (non-breaking change which adds functionality)
- [x] Documentation update (new Cargo example in
`generic-server-json.md`)

  ## Checklist

- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
  - [x] My code follows the repository's style guidelines
  - [x] New and existing tests pass locally
  - [x] I have added appropriate error handling
  - [x] I have added or updated documentation as needed

  ## Additional context

  ### Validator design — two-call retrieval on the documented API

`internal/validators/registries/cargo.go` mirrors the PyPI validator's
README-token approach (substring-match
`mcp-name: <serverName>`), with a two-call retrieval pattern to stay on
the documented public crates.io API:

1. `GET /api/v1/crates/{name}/{version}/readme` returns 200 OK with a
JSON pointer (`{"url":
"https://static.crates.io/readmes/.../...html"}`) — crates.io hands us
the URL rather than emitting a 302.
2. Follow the pointer to the rendered HTML; substring-match for the
token.

The two-call pattern stays on the documented public surface. The CDN URL
layout is observed-stable, but treating
it as the entry point would mean depending on an undocumented path. With
two calls, crates.io controls where
the README lives — if they move it, the metadata endpoint hands us the
new URL.

Missing crates and missing versions surface as **403** from the CDN
(S3's default for missing keys), not 404.
The validator treats any non-200 as "not found" and surfaces the actual
status code in the error message.

  ### Open question — runtime model

Cargo's runtime model is genuinely different from npm/PyPI/NuGet. `cargo
install` is one-time (binary lands on
PATH at `~/.cargo/bin`), not per-invocation like `npx`, `uvx`, or `dnx`
(the new one in .NET 10 SDK Preview 6+).
The new Cargo example in `generic-server-json.md` annotates this
honestly and omits `runtimeHint`.

If a different framing is preferred — e.g. recommending **MCPB**
(prebuilt binary distribution via GitHub
Releases) as the primary path for Rust MCP servers instead of cargo —
happy to adjust the documentation
accordingly. The schema + validator code in this PR is **additive** and
doesn't force a recommendation either
way; a Rust author who chooses cargo can use it, and one who prefers
MCPB still can.

  ### Out of scope (deferred)

- **`docs/guides/publishing/publish-cargo.md`** — follows on this branch
once the runtime-model direction above
  is settled.
- **Publisher CLI `Cargo.toml` autodetect** in
`cmd/publisher/commands/init.go` — separate concern, separate PR.
- **Positive-path validator test** — gated on `rust-faf-mcp` v0.2.3+
publishing as noted in *How Has This Been
  Tested?*.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2026-06-03 10:56:43 -04:00
Radoslav Dimitrov
35b20719ee
fix(validators/oci): fail closed on upstream rate limiting (#1281)
## Summary

When the OCI validator's anonymous manifest fetch returns HTTP 429,
return a retryable error instead of `nil`. This aligns OCI with the
fail-closed behavior of the npm / PyPI / NuGet / MCPB validators, and
ensures the `io.modelcontextprotocol.server.name` label-match always
runs before a publish is accepted.

## Test plan

- [x] `go test ./internal/validators/registries/ -run TestValidateOCI`
passes locally
- [x] `gofmt` + `golangci-lint` clean on the changed file
- [ ] CI green

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:59:59 -04:00
Radoslav Dimitrov
1201cbd82b
security: fix open redirect and add small hardening (#1227)
## What this does

Fixes an open redirect plus a handful of small, mostly-invisible
hardening changes around it.

## What's in it

- **The fix**: trailing-slash redirects no longer let `//evil.com/`
redirect off-host.
- **SSRF block** on the DNS/HTTP namespace verification — refuses to
dial loopback, RFC1918, link-local, multicast, CGNAT, or
IP-literal/single-label \"domains\".
- **Slow-client guard**: `ReadTimeout` and `IdleTimeout` on the HTTP
server (no `WriteTimeout` — would risk cutting off legitimate
multi-package publishes).
- **Defence-in-depth headers** on the UI (`/`) and 404 responses.
- **5xx responses** stop leaking raw DB error text to clients (logged
server-side instead).
- **Search filter** stops treating `%` and `_` as wildcards.
- **Validators**: PyPI/NuGet identifiers URL-escaped before fetching;
MCPB no longer follows redirects.
- **Misc**: `Version` field bounded to 255 chars; logs quote user input;
`BlockedNamespaces` denylist now catches subdomain claimants.

Each change is its own commit so they can be reviewed (or reverted)
independently.

## Test plan

- [ ] CI green
- [ ] Synthetic publish in staging for each package type (npm, pypi,
nuget, oci, mcpb) — confirms validator changes don't regress
- [ ] Quick browser check of `/` after deploy to confirm UI still loads
with the new CSP
- [ ] `curl https://<host>//evil.com/` — confirm the open redirect is
closed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:14:00 -04:00
Mikel Olasagasti Uranga
7c446e75ad
feat(oci): add support for Quay.io registry (#1150)
Adds support for the Quay.io container registry to the OCI validator.
This allows MCP servers hosted on Quay.io to be registered and validated
by the official registry.

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->

Quay.io is a major, trusted public OCI registry widely used by the
cloud-native and open-source communities. Currently, attempting to
publish a server using a Quay image results in an unsupported OCI
registry error. This change enables developers to use Quay.io as a
first-class hosting option for their MCP server images.

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
- **Unit Tests:** Updated `internal/validators/registries/oci_test.go`
to transition Quay.io from a rejected test case to an allowed one.
- **Pattern Matching:** Verified that the allowlist correctly handles
both specific image identifiers and general pattern matching for the
`quay.io` host.
- **Local Validation:** Ran `make check` to ensure no regressions in the
existing OCI validation logic.

## Breaking Changes
<!-- Will users need to update their code or configurations? -->
No. This is an additive change that expands the allowlist of supported
registries. Existing configurations are unaffected.

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
The implementation follows the existing pattern for OCI registries:
1. Added a `RegistryURLQuay` constant to `pkg/model` for consistency.
2. Hardcoded `quay.io` into the `allowedOCIRegistries` map in the
validator to minimize architectural complexity.
3. Updated all relevant documentation files (`package-types.mdx`,
`generic-server-json.md`, and `official-registry-requirements.md`) to
ensure the source of truth is updated across the project.
4. Validation remains scoped to public images; no private registry
authentication was added for this host.

Signed-off-by: Mikel Olasagasti Uranga <mikel@olasagasti.info>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2026-04-24 06:11:46 -04:00
Radoslav Dimitrov
c54a5ac277
Ensure tests are failing CI (#840) 2025-12-11 09:56:57 +02:00
Joel Verhagen
80cb03ff7a
Fix NuGet.org base URL default, improve API integration (#813)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->

This resolves
https://github.com/modelcontextprotocol/registry/issues/738. There are
two issues with the NuGet.org integration today:

1. Change the `registryBaseUrl` for NuGet to
`https://api.nuget.org/v3/index.json`. `https://api.nuget.org` is not
the entry point for our service and is not specific enough.
`https://api.nuget.org/v3/index.json` is the value that users put into
their tooling to connect to our public registry (it is installed by
default in .NET client tooling).
2. Use the service index API to get the URL pattern for package READMEs.
- This URL is not guaranteed to be stable. The service index URL
(`registryBaseUrl`) is, however.

Additionally, I polished up the error messages so various error modes
are clearer. When packages are published to NuGet.org, they take 2-5
minutes to become available due to validations we run.

Some possible states that are now handled and described more helpfully
are:
- Version exists, but the package has no README
- The ID exists, but the version does not
- The ID does not exist at all

There are only 3 authors (me, another MSFT person, and a community
member) explicitly setting the old value. The DB migration will fix the
issue. A future publish will fail with the validation error but the
error message is clear.

`registry type and base URL do not match: 'https://api.nuget.org' is not
valid for registry type 'nuget'. Expected:
https://api.nuget.org/v3/index.json`

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

- Unit tests, new and existing
- Manual test locally
- Set old data in the DB, `docker compose up`, confirm the migration
updates the specific package

## Breaking Changes

Yes, but minimal impact expected, unless there is a dependency on the
specific `https://api.nuget.org` value downstream. I am not aware of any
such dependency, and the new value is more proper and specific.

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

For more information about the NuGet API see:
https://learn.microsoft.com/en-us/nuget/api/overview#service-index (I
wrote most of the docs).
2025-12-09 07:19:38 -08:00
Joel Verhagen
2ca4ae9977
Add support for ACR (Azure Container Registry) and MCR (Microsoft Container Registry) (#754)
<!-- Provide a brief summary of your changes -->

## Motivation and Context

Microsoft uses `mcr.microsoft.com` to host our public containers. These
are syndicated to Docker Hub but image references use
`mcr.microsoft.com` which is not currently supported by the Official MCP
Registry.

See https://github.com/modelcontextprotocol/registry/issues/753.

Microsoft teams want to use MCR-based images for Microsoft MCP servers.
I believe we should be adding ACR support as well, so that our customers
using Azure Container Registry can use the MCP Registry as well.

In short, MCR is used for Microsoft managed containers. ACR is a
solution for Azure customers to host their own containers.

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

Unit tests.

Manual test: local push of a server.json with
`mcr.microsoft.com/azure-sdk/azure-mcp:2.0.0-beta.1` and
`azurearcjumpstart.azurecr.io/hello-arc:latest` in it. The error is now
about missing annotation not unsupported OCI registry.

## Breaking Changes
<!-- Will users need to update their code or configurations? -->

No.

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
2025-11-18 09:58:10 +02:00
Radoslav Dimitrov
913147be3a
Add support for Google Artifact Registry (GAR) (#692)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR:
* Deprecates the existing OCI reference parser
* Deprecates the existing OCI way of authenticating and accessing the
image manifest
* Replaces the above by leveraging go-containerregistry
* Introduces support for Google Artifact Registry
* Enables us to easily add support for other OCI-compliant registries in
the future
* Introduces better error handling
* Introduces custom context timeouts for validation 

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

## Breaking Changes
<!-- Will users need to update their code or configurations? -->

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
Related to #427

---------

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-28 15:00:06 +02:00
Radoslav Dimitrov
0828bc483c
Make mcpb package version optional and fix schema validation (#686)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR:
* Makes mcpb package version property optional
* Bumps the schema and reflects that in the code
* Makes sure the publisher tests are being covered by doing `make check`

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

## Breaking Changes
<!-- Will users need to update their code or configurations? -->

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

---------

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-17 01:35:29 +03:00
Radoslav Dimitrov
6d110bf99e
Use distribution/reference for parsing OCI references (#651)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR addresses the feedback from #634 around:
* Use a proper library - https://github.com/distribution/reference,
instead of the implementing the OCI parsing ourselves (note: left the
tests so we can ensure we are not breaking any existing behavior)
* Updating one of the error messages to now assume MCPB.

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

## Breaking Changes
<!-- Will users need to update their code or configurations? -->

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-14 15:27:57 +03:00
Radoslav Dimitrov
632cbce815
Refactor package types to use dedicated schema types per package type (NPM, PyPI, NuGet, OCI, MCPB) (#634) 2025-10-11 10:53:45 +03:00
Avish Porwal
31e16a18ec
Extra validation for registries (#484)
<!-- Provide a brief summary of your changes -->
Package identifier and version are required for some of the registries
else we won't be able to pull the meta data to validate the packages.

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
This is in continuation to
https://github.com/modelcontextprotocol/registry/pull/424

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
Unit test.

## Breaking Changes
<!-- Will users need to update their code or configurations? -->
No

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

Co-authored-by: Avish Porwal <avishporwal@microsoft.com>
2025-09-23 23:05:10 +01:00
Tadas Antanavicius
56e8b4879b
Introduce serverId, rename id to versionId (#465)
Wielded Claude Code to do this; across many iterations and have reviewed
all the code + largely rewritten the PR commentary here.

This PR implements the server ID consistency changes from
[#396](https://github.com/modelcontextprotocol/registry/issues/396). Our
goal is to:
- Rename `id` to `versionId` to make it clear that it is the ID for a
specific version, not a server
- Introduce `serverId` as a net new concept. This will be 1:1 with
`name`. While not strictly necessary (we could just use name as a PK),
the convention of having a UUID as an ID on our objects is just a
helpful subjective architecture decision. Plus also leaves open the door
to reconsidering the "should we allow renames" question.

## Key Changes

### 🗄️ Database Schema Changes
- **Renamed**: `id` column → `version_id` (unique identifier for each
server version)

### 📋 Metadata Structure Updates
- **JSON Schema**: Updated
`_meta.io.modelcontextprotocol.registry/official` to include:
  - `serverId`: Consistent ID across all versions of a server
  - `versionId`: Unique ID for this specific version (renamed from `id`)

### 🔧 API Endpoint Updates
- **New endpoint structure**: Changed from version-ID-based to
server-ID-based URLs:
  - `GET /v0/servers/{server_id}` - Get latest version of server
  - `GET /v0/servers/{server_id}?version=X.X.X` - Get specific version  
- `GET /v0/servers/{server_id}/versions` - List all versions of a server
  - `PUT /v0/servers/{server_id}?version=X.X.X` - Edit specific version
- **Consistent URL patterns**: All server endpoints now use `server_id`
as the primary identifier
 
### Database Migration Strategy
The migration (`005_add_server_id_rename_version_id.sql`):
1. Adds new `version_id` column  
2. Generates deterministic UUIDs for servers with the same name
3. Updates JSON metadata to add `serverId` and rename `id` to
`versionId`
4. Copies existing `id` values to `version_id` column
5. Drops old `id` column and makes `version_id` the new primary key

## Testing

Besides of course automated tests passing, I:
1. Set up my local db with the pre-005-migration state, imported all the
production data
2. Ran the migration
3. Spot checked some diffs of the data that resulted --

```json
// before
{
  "name": "io.github.ruvnet/ruv-swarm",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "id": "0a9cda7f-2b73-4839-844b-6582f335de42",
      "is_latest": false,
      "updated_at": "2025-09-10T17:21:34.92137012Z",
      "published_at": "2025-09-10T17:15:32.69690433Z"
    }
  },
  "version": "1.0.18",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "Neural network swarm orchestration with WebAssembly acceleration and MCP integration"
}

// after
{
  "name": "io.github.ruvnet/ruv-swarm",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "serverId": "e8538952-96af-4a33-9e31-7fbed012521e",
      "is_latest": false,
      "versionId": "0a9cda7f-2b73-4839-844b-6582f335de42",
      "updated_at": "2025-09-10T17:21:34.92137012Z",
      "published_at": "2025-09-10T17:15:32.69690433Z"
    }
  },
  "version": "1.0.18",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "Neural network swarm orchestration with WebAssembly acceleration and MCP integration"
}

```

Servers that have multiple versions stayed consistent in their
`serverId`:

```json
// First version
{
  "name": "app.getdialer/dialer",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "serverId": "80f08bdb-f3e4-4f19-b072-1676ae49b663",
      "is_latest": false,
      "versionId": "04f25b17-331d-48d7-b08b-37baece208a7",
      "updated_at": "2025-09-09T00:16:49.162620072Z",
      "published_at": "2025-09-08T23:47:09.452137971Z"
    }
  },
  "status": "active",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
  "remotes": [
    {
      "url": "https://getdialer.app/mcp",
      "type": "streamable-http"
    },
    {
      "url": "https://getdialer.app/sse",
      "type": "sse"
    }
  ],
  "version": "1.0.0",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "An MCP server that provides your you make outbound phone calls using your own phone number"
}

// Second version
{
  "name": "app.getdialer/dialer",
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "serverId": "80f08bdb-f3e4-4f19-b072-1676ae49b663",
      "is_latest": true,
      "versionId": "faa26cb9-3aad-415e-aece-f715fff5fe5e",
      "updated_at": "2025-09-09T00:16:49.107842808Z",
      "published_at": "2025-09-09T00:16:49.107842808Z"
    }
  },
  "status": "active",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
  "remotes": [
    {
      "url": "https://getdialer.app/mcp",
      "type": "streamable-http"
    },
    {
      "url": "https://getdialer.app/sse",
      "type": "sse"
    }
  ],
  "version": "1.0.1",
  "repository": {
    "url": "",
    "source": ""
  },
  "description": "An MCP server that provides lets you make outbound phone calls using your own phone number"
}
```

And I also tried publishing a test server anew; looked good:

```bash
⏺ Bash(curl -s "http://localhost:8080/v0/servers/102dc4e4-6ca8-40e6-aa11-cd45763d3352" | jq '.')
  ⎿  {                                                                                             
       "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
       "name": "io.modelcontextprotocol.anonymous/test-claude-migration-server",
       "description": "A test server to verify the new migration and API endpoints work correctly",
       "status": "active",
       "repository": {
         "url": "https://github.com/example/test-claude-migration-server",
         "source": "github",
         "id": "example/test-claude-migration-server"
       },
       "version": "1.0.0",
       "_meta": {
         "io.modelcontextprotocol.registry/official": {
           "serverId": "102dc4e4-6ca8-40e6-aa11-cd45763d3352",
           "versionId": "48c9cd55-7bc4-4ac2-896e-59e81eb653e4",
           "published_at": "2025-09-12T18:33:23.264203-07:00",
           "updated_at": "2025-09-12T18:33:23.264203-07:00",
           "is_latest": true
         }
       }
     }

⏺ Bash(curl -s "http://localhost:8080/v0/servers/102dc4e4-6ca8-40e6-aa11-cd45763d3352/versions" | jq '.')
  ⎿  {                                                                                                 
       "servers": [
         {
           "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
           "name": "io.modelcontextprotocol.anonymous/test-claude-migration-server",
           "description": "A test server to verify the new migration and API endpoints work correctly",
           "status": "active",
           "repository": {
             "url": "https://github.com/example/test-claude-migration-server",
             "source": "github",
             "id": "example/test-claude-migration-server"
           },
           "version": "1.0.0",
           "_meta": {
             "io.modelcontextprotocol.registry/official": {
               "serverId": "102dc4e4-6ca8-40e6-aa11-cd45763d3352",
               "versionId": "48c9cd55-7bc4-4ac2-896e-59e81eb653e4",
               "published_at": "2025-09-12T18:33:23.264203-07:00",
               "updated_at": "2025-09-12T18:33:23.264203-07:00",
               "is_latest": true
             }
           }
         }
       ],
       "metadata": {
         "count": 1
       }
     }
```

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Preeti Dewani <preetidewani1990@gmail.com>
Co-authored-by: adam jones <adamj+git@anthropic.com>
Co-authored-by: Adam Jones <adamj@anthropic.com>
2025-09-21 15:00:09 +03:00
Gaurav Gosain
2280e4da38
Add GitHub Container Registry (GHCR) support for MCP servers (#439)
This commit implements support for publishing MCP servers to GitHub
Container
Registry (ghcr.io) in addition to the existing Docker Hub support,
addressing
Docker Hub rate limiting concerns as requested in #393.

Key changes:
- Add RegistryURLGHCR constant for https://ghcr.io
- Refactor OCI validation to support multiple container registries with
extensible RegistryConfig system
- Implement registry-agnostic authentication system that handles:
  - Docker Hub token-based authentication via auth.docker.io
  - GHCR token-based authentication via ghcr.io/token service
- Update Accept headers to support OCI image indexes for multi-arch
images
- Extract helper functions to reduce cyclomatic complexity and improve
code maintainability
- Add comprehensive test coverage for both Docker Hub and GHCR scenarios
- Include real image testing with github/github-mcp-server and
nkapila6/mcp-local-rag
- Update publishing documentation with GHCR examples and authentication
details

The implementation maintains backward compatibility with existing Docker
Hub
usage while providing a future-proof architecture for adding additional
container registries. All authentication is now handled through proper
token
services for both registries.

Fixes #393

## Motivation and Context
Docker Hub rate limiting has been causing issues for the MCP registry
when validating OCI images. Adding GHCR support provides an alternative
container registry that many MCP server authors are already using,
reducing the load on Docker Hub and providing more options for the
community.

## How Has This Been Tested?
- All existing unit tests continue to pass
- Added comprehensive test coverage for GHCR scenarios including:
  - Non-existent images (proper error handling)
  - Real images without MCP annotations (github/github-mcp-server)  
  - Real images with proper MCP annotations (nkapila6/mcp-local-rag)
  - Registry URL validation for both supported registries
- Tested authentication flow manually using curl commands
- Verified multi-arch image support with OCI index manifests

## Breaking Changes
None. This is a purely additive change that maintains full backward
compatibility with existing Docker Hub configurations.

## Types of changes
- [x] New feature (non-breaking change which adds functionality)
- [x] Documentation update

## Checklist
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed

## Additional context
The implementation uses a RegistryConfig struct to abstract
registry-specific details like API endpoints, authentication URLs, and
token services. This design makes it straightforward to add support for
additional container registries in the future (such as AWS ECR, Azure
Container Registry, etc.) by simply adding new cases to the
getRegistryConfig function.

The authentication system properly handles the different token services
used by each registry:
- Docker Hub: auth.docker.io with service=registry.docker.io
- GHCR: ghcr.io/token with service=ghcr.io

Both registries now support the full OCI specification including
multi-arch images via OCI image indexes.
2025-09-17 11:56:52 -07:00
adam jones
a100367461
Fix NPM package validation for scoped packages (#429)
## Summary
- Add URL encoding for package identifiers and versions in NPM registry
requests
- Fixes issue where scoped packages like @hellocoop/admin-mcp failed
validation due to unencoded @ and / symbols
- Add unit tests for scoped NPM packages to prevent regression

Fixes #409

Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-09-11 09:34:57 -07:00
Avish Porwal
adfbfafcf8
npm package validation: require identifier and version (#424)
<!-- Provide a brief summary of your changes -->
Adding validation for version and identifier for packages.

## Motivation and Context
While debugging a bug, I found out that we need exact version to be
there in package to fetch from npm, otherwise we won't be able to parse
the response as it returns all versions. Even if we do, we still need to
go and figure out which version have the mcpname associated with while
iterating over all versions. So, we should make mandatory to have
identifier and version present in the given package object if we want to
validate package association with registries.


## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
UTs.

## Breaking Changes
<!-- Will users need to update their code or configurations? -->

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->

Co-authored-by: Avish Porwal <avishporwal@microsoft.com>
2025-09-10 13:21:04 -07:00
adam jones
8f5e17d0fe
Enforce and document file hash requirement for MCPB packages (#368)
## Summary
Implements mandatory file hash validation for MCPB (MCP Bundle) packages
to ensure file integrity and security.

## Changes
- **Validator enforcement**: MCPB packages now require `file_sha256`
field at publish time
- **Test coverage**: Updated all MCPB test cases to include valid
SHA-256 hashes
- **Documentation**: Updated publisher guide and schema to reflect hash
requirement
- **Error handling**: Clear error message when hash is missing

## Security Impact
This ensures the complete security workflow for MCPB packages:
1. Authors generate and provide SHA-256 hashes when publishing
2. Registry validates hash is present (but doesn't verify content)
3. MCP clients validate hash matches downloaded file before installation

Fixes #351
2025-09-08 11:38:25 -07:00
adam jones
694a4d8879
Add registry validators (#350)
Fixes #96

## Motivation and Context

See #96

## How Has This Been Tested?

Has some unit and integration tests, still needs proper testing of the
different registry types

## Breaking Changes

Yes, new package uploads will need extra verification

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
2025-09-04 22:45:59 +01:00