Commit graph registry/internal
Author SHA1 Message Date
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
Radoslav Dimitrov
736d376858
fix(db): raise pgxpool MaxConns + PG max_connections + set PG resources (#1221)
## Summary

Follow-up to #1215 and #1220. Both of those addressed *individual* slow
queries; this PR addresses *concurrency* — under today's scraper load
the cursor query is fast on average (mean 42ms) but the connection pool
saturates and queue depth blows up at the Go HTTP layer.

## Diagnosis

`pg_stat_statements` (added in #1215) made this possible to see:

```
max_ms   mean_ms  calls    template
10,755     41.8   94,009   plain cursor pagination, no filter
 7,277    214.7   14,770   ILIKE substring filter
 7,638    330.8    7,656   ILIKE substring + is_latest filter
```

Mean times are healthy. But `max_exec_time` of 7–10s on the cursor
query, combined with sustained ~15 req/s from scrapers (ServiceNow's
148.139.x.x range, anonymous `node` user-agent, etc) saturates
`MaxConns=30 × 2 pods = 60`. New requests queue at the Go HTTP layer;
nginx-side p99 hits 35s; eventually scrapers time out at 60s, retry, and
amplify the queue.

Today's two ongoing alerts are both this pattern:
- 17:35–17:40 UTC `Availability dropped below 95%` — 4,526 GET requests
in 5 min, hundreds of 504s
- 18:17 UTC `Publish Endpoint Latency` re-fire — same scraper
concurrency dragging the publish path

Critically, the symptoms today were also visible during yesterday's
incident, but yesterday's broken cursor (#1215) was the dominant cause.
After #1215 the cursor is fast individually; concurrency now becomes the
next bottleneck.

## Changes

### pgxpool (`internal/database/postgres.go`)

| Setting | Before | After |
|---------|-------:|------:|
| `MaxConns` | 30 | **60** per pod |
| `MinConns` | 5 | **10** per pod |
| `MaxConnIdleTime` | 30 min | (unchanged) |
| `MaxConnLifetime` | 2 h | (unchanged) |

2 pods × 60 = 120 total app connections. Cuts queue depth roughly in
half at current scraper load.

### PG cluster (`deploy/pkg/k8s/postgres.go`)

- `max_connections: 100 → 200` — required to support the larger pool.
120 (app) + ~10 (PG internals: autovacuum, replication, admin) + 70
headroom.
- Added explicit `resources:` block — previously the pod had no resource
limits, making node-level OOM behaviour unpredictable.

| | Request | Limit |
|---|--------:|------:|
| memory | 512Mi | **4Gi** |
| cpu | 200m | 1500m |

## Resource budget

| Node | Now | After |
|------|----:|------:|
| dy89 (PG node) | 39% mem (~2.4 GiB / 6 GiB) | ~65% mem worst-case
(~3.9 GiB) |
| 2yxm | 36% mem | unchanged |

Both nodes well within capacity. CPU usage <50% on both, plenty of
headroom.

PG worst-case memory math:
- 200 conns × ~15 MB per backend = 3 GiB
- + `shared_buffers` 128 MiB
- + `maintenance_work_mem`, OS overhead, etc.
- ≈ 3–4 GiB total

## Deployment caveat

`max_connections` is a postmaster-level setting → CNPG triggers a PG
restart on the change. Same in-pod restart shape as the
pg_stat_statements deploy yesterday — registry pods see ~30s of DB
unavailability, covered by v1.7.1's retry-with-backoff (8 attempts, 1→8s
capped). One registry pod may bounce once before recovering, like
yesterday.

**Time the merge for a low-traffic UTC window.**

Order of operations matters within the deploy itself:
1. Pulumi applies the new CNPG spec → PG restarts with
`max_connections=200`
2. Rolling deploy of registry pods picks up `MaxConns=60` config
3. New conns are accepted up to 200 limit

Pulumi's standard ordering does step 1 before step 2 in this scenario
(Pulumi resource graph: CNPG cluster precedes Deployment). If for any
reason it doesn't, the worst case is `too many connections` errors
during a small window — Self-correcting once the rollout completes.

## Test plan

- [x] `go build ./...` clean for app + deploy
- [x] `make lint` clean
- [x] `go test -race ./internal/database/...` green
- [ ] On merge: staging deploy applies the spec change cleanly; PG
restarts; pool reaches 60 max
- [ ] On prod deploy: same; verify no `too many connections` errors
during the rollout window
- [ ] After deploy: query `SHOW max_connections` returns 200; query
pg_stat_statements after a scraper burst, confirm `max_exec_time` for
cursor query no longer hits 10s

## Out of scope

- **ILIKE substring search** (`server_name ILIKE '%foo%'`) is
unindexable. Three pg_stat_statements variants run with means 141–333ms
and max 4–7s. Worth replacing with a `pg_trgm` GIN index or full-text
search in a separate PR.
- **Per-IP nginx rate limiting** — defends against scraper retry storms
regardless of pool size.
- **`Cache-Control` on `/v0/servers`** — let nginx absorb
scraper-repeated cursors.
- **Pre-existing `superfluous WriteHeader` warnings** — separate Huma
framework issue.

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:21:24 -04:00
Radoslav Dimitrov
54cbe2473c
fix(list): use JSONB containment for RemoteURL filter (GIN-indexable) (#1220)
## Summary

Same shape of bug as #1215's cursor fix, different filter. The RemoteURL
filter built its WHERE predicate using `jsonb_array_elements` + `->>`
extraction, which the planner can't translate into a GIN search.
Replaced with a JSONB containment predicate `@>` that uses
`idx_servers_json_remotes`.

## Diagnosis (from the per-phase publish slog #1215 added)

Today's first publish-latency alert at 17:08 UTC pointed straight at
this:

```
publish complete server_name=dev.storage/mcp version=1.10.1 total_ms=14822
  validate_ms=0 lock_ms=238 remotes_ms=10980 version_checks_ms=535 unmark_ms=3014 create_ms=53
```

`remotes_ms=10,980` on a server with **one** remote URL pointed at
`validateNoDuplicateRemoteURLs`, which runs the RemoteURL filter against
the database. EXPLAIN ANALYZE on prod confirmed:

```
Filter: (status != 'deleted') AND EXISTS(SubPlan 1)
Rows Removed by Filter: 21,092       ← scans the whole table
Buffers: shared hit=20,028
Execution Time: 10,005 ms
```

## Fix

`internal/database/postgres.go:99` rewritten:

```sql
-- before  (planner can't use GIN index for this)
EXISTS (SELECT 1 FROM jsonb_array_elements(value->'remotes') AS remote
        WHERE remote->>'url' = $1)

-- after   (GIN-indexable)
value -> 'remotes' @> jsonb_build_array(jsonb_build_object('url', $1::text))
```

Semantically equivalent for our schema (`url` is always a string, NOT
NULL).

## Local benchmark — 20K rows, prod-shaped JSONB (~1KB/row)

| Form | Buffer hits | Index used | Time |
|------|------------:|------------|-----:|
| OLD | 40,398 | none — full scan | 17.0 ms |
| NEW | **294** | **`idx_servers_json_remotes` GIN bitmap** | **1.6 ms**
|

Maps to prod's 10,005 ms cold-cache observation — same shape of speedup
we got from the cursor fix.

## Scope honesty

This PR addresses the **publish slowness path**
(`validateNoDuplicateRemoteURLs` in the publish transaction). It
directly resolves today's 17:08 UTC `Publish Endpoint Latency` alert.

It does **not** address:

- Today's 17:35–17:40 UTC `Availability dropped below 95%` alert.
Per-investigation: only 4 publishes happened in the entire 17:00–17:50
window and zero during the burst minutes — so my initial "concurrent
slow publishes starved the pool" hypothesis was wrong. The actual cause
was scraper-driven concurrency (4,500+ requests in 5 min) on plain
cursor pagination + ILIKE substring search, where individual queries are
fast (mean 42 ms) but tail latency under concurrency reaches 10 s
server-side and 20–35 s nginx-level due to pool queueing.
- The 18:17 UTC `Publish Endpoint Latency` re-fire (same scraper-driven
concurrency).

A follow-up PR will raise pool size + PG `max_connections` + add
explicit PG resource limits to absorb concurrency without queue blowup.
That fix has a different shape (config) and a different risk profile (PG
restart for `max_connections`); kept separate so this surgical SQL
change can ship cleanly.

## Test plan

- [x] `go build ./...` clean
- [x] `make lint` clean
- [x] `go test -race ./internal/database/... ./internal/service/...`
green — existing `TestPostgreSQL_ListServers/filter by remote URL`
covers semantic correctness
- [x] EXPLAIN ANALYZE comparison locally on 20K rows + prod-shaped JSONB
- [ ] After merge + deploy: verify on prod via `EXPLAIN ANALYZE`;
confirm `remotes_ms` in `publish complete` slog drops to single-digit ms

## Out of scope (separate follow-ups)

- pgxpool `MaxConns` / `MinConns` bump + PG `max_connections` raise + PG
`resources` limits — addresses the cursor-tail and ILIKE-search
concurrency that this PR doesn't touch
- Per-IP rate limiting at nginx — defends against scraper retry storms
regardless of query speed
- `Cache-Control` headers on `/v0/servers` — let nginx absorb scraper
repeats
- Pre-existing `superfluous WriteHeader` warnings — separate Huma
framework issue
- ILIKE substring search on `server_name` is unindexable — could be
replaced with `pg_trgm` GIN index or full-text search

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:03:21 -04:00
Radoslav Dimitrov
27af672c5e
fix(list): use row-constructor cursor + restore per-phase publish timings + enable pg_stat_statements (#1215)
## Summary

Three changes from the 2026-04-27 incident:

1. **Cursor SQL fix** — `addCursorCondition` rewritten to use a
row-constructor comparison `(server_name, version) > ($1, $2)` instead
of the OR-decomposed form. Postgres can index-seek on the row
constructor; it can't on the OR form, which scanned from the start of
the index and filtered rows before the cursor. Cost grew linearly with
cursor depth.
2. **Per-phase publish timings restored** — `createServerInTransaction`
was simplified during #1211 review to log only `validate_ms`. The
incident showed publishes spending 50+ s in `acquire_lock` /
`version_checks` / `db_create` while `validate_ms` reported a few
hundred ms — the diagnostic signal was hidden. Restored timings for
every phase, refactored into a small `runPhase` helper.
3. **`pg_stat_statements` enabled** in the CNPG cluster spec. We had no
aggregate query visibility during the incident — could only EXPLAIN
queries we happened to suspect.

## Cursor fix evidence

Local benchmark, 100K rows, cold cache:

| Form | Rows filtered | Buffer reads | Time |
|------|--------------:|-------------:|-----:|
| OLD | 80,001 | 7,679 | 31.6 ms |
| NEW | 2 | 1 | **0.05 ms** |

Maps to prod's measured 8,911 buffer hits → 760 ms. End-to-end API:
`/v0/servers?limit=100&cursor=…` returns in 4–7 ms regardless of depth.

Risk check: confirmed `(server_name, version)` index exists in that
column order (both `servers_pkey` and `idx_servers_name_version`), and
both columns are `NOT NULL` so row-constructor comparison can't silently
drop rows.

## Tests

- New
`TestPostgreSQL_PerformanceScenarios/compound_cursor_across_versions_of_same_server`
pins multi-version pagination semantics. The existing cursor tests only
exercised the fallback (single-component cursor) and degenerate (one
version per server) cases.
- `make lint` clean, `go test -race ./internal/... ./cmd/...` green.

## Deployment

The cursor + slog changes are zero-downtime. The CNPG spec change
triggers a brief PG restart (single-instance cluster). After the
restart, run once on prod:

```bash
kubectl exec -i registry-pg-1 -c postgres \
  --context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
  -- psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements"
```

Time the merge for a low-traffic UTC window. v1.7.1's DB
retry-with-backoff covers the brief PG restart.

## Out of scope

`MaxConns` bump, per-IP rate limiting at nginx, response caching, the
pre-existing `superfluous WriteHeader` warnings — separate follow-ups.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 15:35:59 -04:00
Radoslav Dimitrov
b24d6c7657
fix: harden registry availability and add publish phase timings (#1211)
## Summary

Daily ~17:00 UTC `Publish Endpoint Latency` and `Availability < 95%`
alerts are caused by both registry pods exit-0'ing within seconds of
each other and racing with the still-recovering `registry-pg-rw`
endpoint on restart. Separately, the default compute SA had no project
IAM roles, so `fluentbit-gke` and the GMP collector were silently
dropping every log line and time series — which is why none of this was
visible in Cloud Logging.

All Kubernetes/IAM changes apply uniformly to staging and prod.

## Changes

- **GCP IAM** (`deploy/pkg/providers/gcp/provider.go`) — grant the
default compute SA `logging.logWriter`, `monitoring.metricWriter`,
`monitoring.viewer`, and `stackdriver.resourceMetadata.writer`. Already
applied live; this just encodes it in Pulumi.
- **Pod resilience** (`deploy/pkg/k8s/registry.go`) —
- `PodDisruptionBudget` (`minAvailable: 1`) prevents a synchronized
voluntary disruption.
- `TopologySpreadConstraints` (`ScheduleAnyway`, hostname) keeps the PDB
from deadlocking node drains when both pods co-locate.
- `StartupProbe` (30 × 5s = 150s) protects the DB-retry budget from the
liveness probe.
  - Memory bump 128→256Mi request, 256→512Mi limit.
- **Startup retry** (`cmd/registry/main.go`) — replace silent `return`
on initial DB-ping failure with bounded retry-with-backoff (8 attempts,
1→8s capped, 10s per-attempt). A brief endpoint flap no longer cascades
into a restart loop.
- **Diagnostic log** (`internal/service/registry_service.go`) — emit
`validate_ms` on each publish (success and failure). Validate fans out
to npm/PyPI/OCI with 10s per-host timeouts and is the most likely
contributor to publish latency. Total request duration is already
covered by the existing `http.request.duration` histogram.

## Calibration

The IAM, PDB, topology-spread, startup-probe, and DB-retry pieces are
mitigations — they reduce blast radius regardless of root cause. The
`validate_ms` log is the diagnostic for identifying the trigger when the
alert fires next. Single-PG SPOF (`deploy/pkg/k8s/postgres.go:57`)
remains a follow-up; today's evidence shows PG was a victim, not the
cause.

## Test plan

- [x] `go build ./...` and `go vet ./...` clean for both modules
- [x] `make lint` clean (12 pre-existing issues under `deploy/` are not
in `make lint`)
- [x] `go test -race -count=1 ./internal/... ./cmd/...` green
- [x] IAM applied live; `fluentbit-gke` and GMP no longer hitting
`IAM_PERMISSION_DENIED`
- [ ] After deploy, verify the next 17:00 UTC firing surfaces
`validate_ms` in Cloud Logging

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 09:09:50 -04:00
Radoslav Dimitrov
0cb1f6bf52
fix: recalculate isLatest after status changes (#1081) (#1201)
## Summary

Fixes #1081. When the version flagged `is_latest` was soft-deleted, no
other version was promoted, and because `GetCurrentLatestVersion` didn't
filter deleted rows, subsequent publishes with a lower semver never
became latest — leaving `/v0/servers/{name}/versions/latest` permanently
404.

This PR has two parts:

### 1. Code fix — recalculate `is_latest` on every status change

- Recalculate `is_latest` after every status mutation
(`UpdateServerStatus`, `UpdateAllVersionsStatus`, and the inline status
change in `UpdateServer`), in the same transaction that already holds
the per-server advisory lock.
- Winner selection reuses the existing `CompareVersions` logic: highest
non-deleted version gets the flag; if every version is deleted, the
highest deleted version keeps it so admin lookups via
`includeDeleted=true` still resolve the server (important for restore
flows).
- New `SetLatestVersion` DB primitive issues two statements (clear old,
set new) — `idx_unique_latest_per_server` is a non-deferrable unique
partial index, so a single `UPDATE` that flips two rows trips the
constraint.

### 2. One-shot migration — heal servers already affected (no manual SQL
needed)

`014_heal_is_latest.sql` promotes the highest non-deleted version on
every server whose `is_latest` flag is on a deleted row (or absent
entirely). Picker parses `major.minor.patch` numerically and falls back
to `published_at` for non-semver or as a tiebreaker. Heals
`io.github.containers/kubernetes-mcp-server` to `0.0.61` automatically
and catches any other servers silently affected by the original bug,
without hardcoded version numbers.

Picker behavior vs. `CompareVersions`:
- Standard `M.m.p` semver: matches exactly, including the
backport-after-delete case (e.g. published 1.0.1 then 1.0.0 hotfix after
deleting 2.0.0 → picks 1.0.1, not 1.0.0).
- Non-semver: falls through to `published_at DESC`. Reasonable.
- Prereleases (`1.0.0-alpha` vs `1.0.0`): both parse to `(1,0,0)`,
tiebreak by `published_at`. Diverges from `CompareVersions` (which
always says `1.0.0 > 1.0.0-alpha`). Edge case requires (a) server hit by
this bug AND (b) prereleases at the top of the version list. Acceptable;
the publisher can flip it with one `mcp-publisher status` call (which
now triggers proper recalc thanks to part 1).

## Verification

End-to-end curl repro of the exact scenario from the issue, run against
both `main` and this branch:

**Before (main):**
```
publish 1.0.0 → 200
publish 0.0.59 → 200
PATCH .../1.0.0/status {deleted} → 200
GET /versions/latest → 404            ← bug
publish 0.0.60 → 200
GET /versions/latest → 404            ← bug persists, 0.0.60 has isLatest=False
```

**After (this branch):**
```
publish 1.0.0 → 200
publish 0.0.59 → 200
PATCH .../1.0.0/status {deleted} → 200
GET /versions/latest → 200 (0.0.59, isLatest=true)
publish 0.0.60 → 200
GET /versions/latest → 200 (0.0.60, isLatest=true)
```

## Test plan

- [x] Service tests covering: soft-delete latest → next highest active
promoted; the exact #1081 repro end-to-end through the service;
all-versions-deleted still keeps highest addressable for admin;
restore-to-higher-version reclaims latest.
- [x] Migration tests covering: kubernetes-mcp-server scenario (heals to
highest semver); backport scenario (semver beats most-recent-published);
no-is_latest-row defensive case; all-deleted server left untouched;
healthy server left untouched; non-semver versions fall back to
`published_at`.
- [x] Full unit test suite (`./internal/... ./cmd/...`) passes against a
real Postgres.
- [x] `make lint` (0 issues) and `make validate` pass.
- [x] Manual curl repro against a running registry confirms the fix
resolves the failure mode and `main` still reproduces it.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:59:28 -04:00
Radoslav Dimitrov
c5f50e817b
fix(auth): better DNS verification errors and wrong-selector hint (#1202)
## Summary

DNS auth users have repeatedly hit two failure modes that both produce
the same opaque "Ed25519 signature verification failed" error, making
them indistinguishable to debug. This PR makes both diagnosable
end-to-end without changing the auth protocol or any successful path.

The two recurring failure modes:

1. **Wrong placement** — TXT record put under a selector
(`_mcp-auth.<domain>` or `_mcp-registry.<domain>`) instead of the apex.
DKIM-style intuition, but MCP DNS auth uses SPF-style apex placement.
See #385 (PR #388 fixed the docs but the failure recurred), #1103,
#1126.
2. **Stale apex record** — old TXT record left behind after a key
rotation, silently tried first and rejected.

## Changes

**`internal/api/handlers/v0/auth/dns.go`** — when the apex has no
`v=MCPv1` record, probe `_mcp-auth.<domain>` and
`_mcp-registry.<domain>` in parallel (2s individual timeout). If a
record is found there, return a targeted error pointing the user at the
apex. Costs zero extra DNS work in the success path.

**`internal/api/handlers/v0/auth/common.go`** — when verification fails
against records that *are* present, include short fingerprints of every
key the registry tried (`ed25519:YJLsHFhu`-style 8-char prefix). A new
`ErrSignatureMismatch` sentinel keeps structural errors (wrong signature
size, unsupported algorithm) passing through unchanged via `errors.Is`,
so existing assertions on those messages still hold.

**`docs/modelcontextprotocol-io/authentication.mdx`** — anti-pattern
`<Warning>` callout in the DNS section explicitly warning against
selector placement and stale records.

**Tests** — two new tests:
- `TestDNSAuthHandler_WrongSelectorProbe` — verifies the probe surfaces
records found at `_mcp-auth.` / `_mcp-registry.` and falls through to
the standard error when nothing is found anywhere
- `TestDNSAuthHandler_StaleKeyFingerprintInError` — verifies the
fingerprint hint appears in the failure message for #1126's exact
scenario

One existing test assertion was updated from a generic `"signature
verification failed"` substring to the more specific `"invalid signature
size for ECDSA P-384"` it should always have been asserting; the new
sentinel-based logic surfaces structural errors verbatim.

## Test plan

- [x] `make lint` — clean
- [x] `make test-unit` — all auth tests pass (existing + 2 new)
- [x] Verified existing single-key and multi-key paths preserve their
original (more specific) error messages where they were already specific
- [ ] Reviewer to sanity-check the docs callout renders well in the
Mintlify theme

## Out of scope

The publisher-side preflight DNS check (cross-link in #845) is
intentionally **not** in this PR — that belongs in the broader CLI UX
work tracked by #845.

Refs #845, #385, #1103
Fixes #1126

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:22:06 -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
0fc4606be2
Stop pinning Go to patch versions, use stable in CI (#1137)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
We keep bumping `go 1.26.x` across the repo every time a new Go patch
drops
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
- **`go.mod` / `deploy/go.mod`**: `go 1.25.8` → `go 1.26` (minor only —
"this module needs Go 1.26")
- **All CI workflows**: `go-version-file: 'go.mod'` → `go-version:
'stable'` — CI always uses the latest stable Go, no more
  bump PRs
- **govulncheck**: removed hardcoded `go-version-input: '1.26.1'` →
`go-version-input: 'stable'`
## 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>
2026-04-09 11:57:21 -04:00
Preeti Dewani
bb84ea69e8
Publisher-managed "deleted" support (#893)
(Updated auto-generated PR description from @tadasant / Claude on Feb
22, 2026)

## Motivation and Context

Fixes #637

Server lifecycle management (deprecation and yanking) is a standard
requirement for package registries. Use cases like rebranding, security
vulnerabilities, and end-of-life servers demand the ability to mark
servers as deprecated or deleted, with optional messaging to guide
consumers toward alternatives.

## Changes

### New API endpoints

- **`PATCH /v0/servers/{serverName}/versions/{version}/status`** —
Update the lifecycle status of a single server version
- **`PATCH /v0/servers/{serverName}/status`** — Update the lifecycle
status of all versions of a server in a single transaction

Both endpoints accept:
```json
{
  "status": "active|deprecated|deleted",
  "statusMessage": "Optional message (max 500 chars) explaining the change"
}
```

### New CLI command

- **`mcp-publisher status <serverName> [version]`** — Update server
lifecycle status from the command line
  - `--status` (required): `active`, `deprecated`, or `deleted`
  - `--message`: Optional status message
- `--all-versions`: Apply to all versions of the server (replaces the
`<version>` argument)

### API response changes

`RegistryExtensions` now includes two new fields:
- `statusChangedAt` — Timestamp of the last status change
- `statusMessage` — Optional free-form message (e.g., deprecation
reason, migration guidance)

### List endpoint changes

- New `include_deleted` query parameter on `GET /v0/servers` — defaults
to `false`, hiding deleted servers from listings
- When `updated_since` is provided, `include_deleted` is automatically
set to `true` for incremental sync

### Database

- Migration `013_add_status_fields.sql`: Adds `status_changed_at` (NOT
NULL, initialized from `published_at`) and `status_message` columns to
the `servers` table

### Design decisions (from review discussion)

- **Dedicated status endpoints** rather than overloading the existing
edit endpoint
- **Bidirectional status transitions** — all transitions between
`active`, `deprecated`, and `deleted` are allowed
- **`statusMessage` as free-form text** — no structured `nextName` or
`alternativeUrl` fields (per maintainer feedback, to avoid complexity
around chaining, trust transfer, etc.)
- **Publish permission** grants status change access (not limited to
admin/edit)
- **Status message auto-cleared** when transitioning back to `active`
Good, now I have the full picture. Here's the addendum:

### Consumer-facing API changes

The new status fields live inside
`_meta["io.modelcontextprotocol.registry/official"]` on every
`ServerResponse`. This is the existing `RegistryExtensions` object — two
new fields are added alongside the existing ones:

**Before:**
```json
{
  "server": { "name": "com.example/my-server", "...": "..." },
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "status": "active",
      "publishedAt": "2025-06-01T00:00:00Z",
      "updatedAt": "2025-06-15T00:00:00Z",
      "isLatest": true
    }
  }
}
```

**After:**
```json
{
  "server": { "name": "com.example/my-server", "...": "..." },
  "_meta": {
    "io.modelcontextprotocol.registry/official": {
      "status": "deprecated",
      "statusChangedAt": "2026-02-01T12:00:00Z",
      "statusMessage": "Deprecated in favor of com.example/my-server-v2",
      "publishedAt": "2025-06-01T00:00:00Z",
      "updatedAt": "2026-02-01T12:00:00Z",
      "isLatest": true
    }
  }
}
```

| New field | Type | Present when |
|-----------|------|-------------|
| `statusChangedAt` | `string` (RFC3339) | Always — initialized to
`publishedAt` for existing records |
| `statusMessage` | `string` (max 500 chars) | Only when set —
`null`/omitted for active servers (auto-cleared on transition to active)
|

These fields appear on **every endpoint that returns a
`ServerResponse`**: `GET /v0/servers`, `GET /v0/servers/{name}`, `GET
/v0/servers/{name}/versions/{version}`, and the new PATCH status
endpoints.

### List endpoint filtering

`GET /v0/servers` gains an `include_deleted` query parameter:

| Scenario | Behavior |
|----------|----------|
| No `include_deleted` param | Deleted servers hidden (default `false`)
|
| `include_deleted=true` | Deleted servers included in results |
| `updated_since` provided | `include_deleted` forced to `true` for
incremental sync |

## How Has This Been Tested?

- Unit tests for status handler (`status_test.go`) covering status
transitions, permission checks, validation errors, and the all-versions
endpoint
- Unit tests for the CLI command (`status_test.go`)
- Integration with existing server/edit handler tests
- Database layer tests for the new status update and query methods
- Tested locally with `make dev-compose`

## Types of changes

- [x] New feature (non-breaking change which adds functionality)

## 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

## Open items from review

- [ ] `include_deleted` silent override: When `updated_since` is
provided and `include_deleted=false` is explicitly passed, should return
a 400 error rather than silently overriding
([thread](https://github.com/modelcontextprotocol/registry/pull/893#discussion_r2100879360))
— agreed upon but may not yet be implemented
2026-02-27 09:00:07 -08:00
deerajkumar18
b5f38fcc2a
update schema url point to the actual location (#884)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
This PR ensures that the draft URL points to the actual draft schema 
improvement suggested in
https://github.com/modelcontextprotocol/registry/issues/814


## 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: -->
- [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
-->
2026-02-10 13:32:58 -08:00
Tadas Antanavicius
70a27717fb
feat(schema): allow template variables in transport URL field (#910)
Authored by Claude Code Opus 4.5, steered/reviewed by me.

## Summary
- Updated the URL regex pattern in `StreamableHttpTransport` and
`SseTransport` to allow template variables like `{baseUrl}` as an
alternative to requiring the `https://` prefix
- This enables more flexible URL configuration in the `remotes` section
of server.json

## Changes
- Modified the `url` field pattern from `^https?://[^\s]+$` to
`^(https?://[^\s]+|\{[a-zA-Z_][a-zA-Z0-9_]*\}[^\s]*)$`
- Updated both `openapi.yaml` and the derived `server.schema.json`
- Added clarifying documentation that URL must start with `http://`,
`https://`, or a template variable

## Regex breakdown

```
^(https?://[^\s]+|\{[a-zA-Z_][a-zA-Z0-9_]*\}[^\s]*)$
```

| Part | Meaning |
|------|---------|
| `^` | Start of string |
| `(` | Begin alternation group |
| `https?://[^\s]+` | `http://` or `https://` followed by non-whitespace
|
| `\|` | OR |
| `\{[a-zA-Z_][a-zA-Z0-9_]*\}` | Template variable: `{` + valid
identifier + `}` |
| `[^\s]*` | Optional path/query after template variable |
| `)` | End alternation group |
| `$` | End of string |

## Test cases

**Should match ✓**
- `https://api.example.com/mcp`
- `http://localhost:8080/sse`
- `https://example.com/path?query=value`
- `{baseUrl}`
- `{baseUrl}/mcp`
- `{server_url}/api/v1`
- `{API_ENDPOINT}`

**Should NOT match ✗**
- `ftp://example.com` — wrong protocol
- `example.com` — missing protocol or variable
- `/relative/path` — relative paths not allowed
- `{invalid-name}/path` — hyphens not valid in variable names
- `{123invalid}` — variables can't start with numbers

---------

Co-authored-by: Agent Orchestrator <agent@ao.pulsemcp.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2026-02-09 11:51:17 +02:00
dependabot[bot]
541a6c61fa
build(deps): bump go.opentelemetry.io/contrib/instrumentation/runtime from 0.64.0 to 0.65.0 (#940)
Bumps
[go.opentelemetry.io/contrib/instrumentation/runtime](https://github.com/open-telemetry/opentelemetry-go-contrib)
from 0.64.0 to 0.65.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-go-contrib/releases">go.opentelemetry.io/contrib/instrumentation/runtime's
releases</a>.</em></p>
<blockquote>
<h2>Release
v1.40.0/v2.2.0/v0.65.0/v0.34.0/v0.20.0/v0.15.0/v0.13.0/v0.12.0</h2>
<h2>Overview</h2>
<h3>Added</h3>
<ul>
<li><code>WithMetricAttributesFn</code> option in
<code>go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc</code>
to define dynamic attributes on auto-instrumented metrics. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8191">#8191</a>)</li>
<li>Add support for configuring propagators in
<code>go.opentelemetry.io/contrib/otelconf</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8281">#8281</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/prometheus</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8401">#8401</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/otelconf</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8461">#8461</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/otellogr</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8477">#8477</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/otellogrus</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8485">#8485</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/otelslog</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8480">#8480</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fix panic when passing nil <code>TracerProvider</code> or
<code>MeterProvider</code> to <code>WithTracerProvider</code> or
<code>WithMeterProvider</code> in
<code>go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc</code>.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8323">#8323</a>)</li>
<li><code>Transport</code> in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp</code>
now supports reading request body multiple times for subsequent requests
that reuse <code>http.Request</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8352">#8352</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8142">#8142</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8302">#8302</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8317">#8317</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/zpages</code> has been replaced by
<code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8325">#8325</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8340">#8340</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8341">#8341</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/runtime</code> has
been replaced by <code>const Version</code> string. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8349">#8349</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8356">#8356</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8357">#8357</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/host</code> has been
replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8358">#8358</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8360">#8360</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/propagators/opencensus</code> has been
replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8361">#8361</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8365">#8365</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/samplers/probability/consistent</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8366">#8366</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/v2/mongo/otelmongo</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8370">#8370</a>)</li>
<li>Set <code>error.type</code> attribute instead of adding
<code>exception</code> span events in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp</code>.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8386">#8386</a>)</li>
<li>Set <code>error.type</code> attribute instead of adding
<code>exception</code> span events in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws</code>.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8386">#8386</a>)</li>
<li>Upgrade <code>go.opentelemetry.io/otel/semconv</code> to
<code>v1.39.0</code>, including updates across all instrumentation and
detector modules. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8404">#8404</a>)
<ul>
<li>The semantic conventions v1.39.0 release introduces breaking
changes, including:
<ul>
<li><code>rpc</code> span and metric attributes have been renamed to
align with naming guidelines:
<ul>
<li><code>rpc.system</code> → <code>rpc.system.name</code> (values:
<code>grpc</code>, <code>grpc_web</code>, <code>connectrpc</code>,
<code>thrift</code>, <code>dubbo</code>, etc.)</li>
<li><code>rpc.method</code> and <code>rpc.service</code> have been
merged into a fully-qualified <code>rpc.method</code> attribute</li>
<li><code>rpc.client|server.duration</code> →
<code>rpc.client|server.call.duration</code> (unit changed to
seconds)</li>

<li><code>rpc.grpc.request.metadata</code>/<code>rpc.grpc.response.metadata</code>
→
<code>rpc.request.metadata</code>/<code>rpc.response.metadata</code></li>
<li><code>rpc.grpc.status_code</code> → deprecated in favor of
<code>rpc.response.status_code</code></li>
<li><code>rpc.jsonrpc.request_id</code> →
<code>jsonrpc.request.id</code></li>
<li><code>rpc.jsonrpc.version</code> →
<code>jsonrpc.protocol.version</code></li>
</ul>
</li>
<li><code>system</code> and <code>process</code> metrics:
<ul>
<li><code>*.linux.memory</code> metrics renamed to
<code>*.memory.linux</code></li>
<li><code>system.process.status</code> → <code>process.state</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md">go.opentelemetry.io/contrib/instrumentation/runtime's
changelog</a>.</em></p>
<blockquote>
<h2>[1.40.0/2.2.0/0.65.0/0.34.0/0.20.0/0.15.0/0.13.0/0.12.0] -
2026-02-02</h2>
<h3>Added</h3>
<ul>
<li><code>WithMetricAttributesFn</code> option in
<code>go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc</code>
to define dynamic attributes on auto-instrumented metrics. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8191">#8191</a>)</li>
<li>Add support for configuring propagators in
<code>go.opentelemetry.io/contrib/otelconf</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8281">#8281</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/prometheus</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8401">#8401</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/otelconf</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8461">#8461</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/otellogr</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8477">#8477</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/otellogrus</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8485">#8485</a>)</li>
<li>Add <code>const Version</code> in
<code>go.opentelemetry.io/contrib/bridges/otelslog</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8480">#8480</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fix panic when passing nil <code>TracerProvider</code> or
<code>MeterProvider</code> to <code>WithTracerProvider</code> or
<code>WithMeterProvider</code> in
<code>go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc</code>.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8323">#8323</a>)</li>
<li><code>Transport</code> in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp</code>
now supports reading request body multiple times for subsequent requests
that reuse <code>http.Request</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8352">#8352</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8142">#8142</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8302">#8302</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8317">#8317</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/zpages</code> has been replaced by
<code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8325">#8325</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8340">#8340</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8341">#8341</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/runtime</code> has
been replaced by <code>const Version</code> string. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8349">#8349</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8356">#8356</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8357">#8357</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/host</code> has been
replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8358">#8358</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8360">#8360</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/propagators/opencensus</code> has been
replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8361">#8361</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8365">#8365</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/samplers/probability/consistent</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8366">#8366</a>)</li>
<li>The <code>Version()</code> function in
<code>go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/v2/mongo/otelmongo</code>
has been replaced by <code>const Version</code>. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8370">#8370</a>)</li>
<li>Set <code>error.type</code> attribute instead of adding
<code>exception</code> span events in
<code>go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp</code>.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8386">#8386</a>)</li>
<li>Set <code>error.type</code> attribute instead of adding
<code>exception</code> span events in
<code>go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws</code>.
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8386">#8386</a>)</li>
<li>Upgrade <code>go.opentelemetry.io/otel/semconv</code> to
<code>v1.39.0</code>, including updates across all instrumentation and
detector modules. (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8404">#8404</a>)
<ul>
<li>The semantic conventions v1.39.0 release introduces breaking
changes, including:
<ul>
<li><code>rpc</code> span and metric attributes have been renamed to
align with naming guidelines:
<ul>
<li><code>rpc.system</code> → <code>rpc.system.name</code> (values:
<code>grpc</code>, <code>grpc_web</code>, <code>connectrpc</code>,
<code>thrift</code>, <code>dubbo</code>, etc.)</li>
<li><code>rpc.method</code> and <code>rpc.service</code> have been
merged into a fully-qualified <code>rpc.method</code> attribute</li>
<li><code>rpc.client|server.duration</code> →
<code>rpc.client|server.call.duration</code> (unit changed to
seconds)</li>

<li><code>rpc.grpc.request.metadata</code>/<code>rpc.grpc.response.metadata</code>
→
<code>rpc.request.metadata</code>/<code>rpc.response.metadata</code></li>
<li><code>rpc.grpc.status_code</code> → deprecated in favor of
<code>rpc.response.status_code</code></li>
<li><code>rpc.jsonrpc.request_id</code> →
<code>jsonrpc.request.id</code></li>
<li><code>rpc.jsonrpc.version</code> →
<code>jsonrpc.protocol.version</code></li>
</ul>
</li>
<li><code>system</code> and <code>process</code> metrics:
<ul>
<li><code>*.linux.memory</code> metrics renamed to
<code>*.memory.linux</code></li>
<li><code>system.process.status</code> → <code>process.state</code></li>
<li><code>system.paging.type</code> and
<code>process.paging.fault_type</code> →
<code>system.paging.fault.type</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a89d958e7a"><code>a89d958</code></a>
Release v1.40.0/v2.2.0/v0.65.0/v0.34.0/v0.20.0/v0.15.0/v0.13.0/v0.12.0
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8493">#8493</a>)</li>
<li><a
href="1530d71edc"><code>1530d71</code></a>
Upgrade <code>semconv</code> to <code>v1.39.0</code> (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8481">#8481</a>)</li>
<li><a
href="135bb6050e"><code>135bb60</code></a>
Upgrade autoexport to latest otel dep (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8483">#8483</a>)</li>
<li><a
href="8e921058c6"><code>8e92105</code></a>
chore(deps): update module github.com/gabriel-vasile/mimetype to v1.4.13
(<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8486">#8486</a>)</li>
<li><a
href="147148e2d2"><code>147148e</code></a>
fix(deps): update module github.com/shirou/gopsutil/v4 to v4.26.1 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8487">#8487</a>)</li>
<li><a
href="d387510eec"><code>d387510</code></a>
Add version file for otellogrus bridge (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8485">#8485</a>)</li>
<li><a
href="fad49c80d5"><code>fad49c8</code></a>
add version file for otelslog bridge (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8480">#8480</a>)</li>
<li><a
href="9e1e5731ae"><code>9e1e573</code></a>
fix(deps): update module go.mongodb.org/mongo-driver to v1.17.8 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8479">#8479</a>)</li>
<li><a
href="37aa4753c9"><code>37aa475</code></a>
Add version file for otellogr bridge (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-go-contrib/issues/8477">#8477</a>)</li>
<li><a
href="fc8ef39de1"><code>fc8ef39</code></a>
chore(deps): update module github.com/grpc-ecosystem/grpc-gateway/v2 to
v2.27...</li>
<li>Additional commits viewable in <a
href="https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.64.0...zpages/v0.65.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=go.opentelemetry.io/contrib/instrumentation/runtime&package-manager=go_modules&previous-version=0.64.0&new-version=0.65.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2026-02-09 11:11:06 +02:00
Bob Dickinson
f06d1eda8a
Added /validate endpoint and modified mcp-publish so that all validation happens there (#896)
## Motivation and Context

Per discussion with @rdimitrov - all validation now happens in a new
`/validate` endpoint. For details, see below.

## Breaking Changes

None. Validation on /publish only validates schema version and semantic
checks (manual checks) as before. We can add schema validation by
changing a flag in the called to `ValidateJSONSchema` when we're ready.

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

## 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

## API Changes

### 1. New `/validate` Endpoint

A new public endpoint was added for validating `server.json` files
without publishing them.

**Details:**
- **Paths:** `/v0/validate` and `/v0.1/validate`
- **Method:** `POST`
- **Authentication:** None required (public endpoint)
- **Request:** JSON body containing `ServerJSON`
- **Response:** `200 OK` with `ValidationResult` containing:
  - `valid` (boolean)
- `issues` (array of validation issues with type, path, message,
severity, reference)

**Implementation:**
- New handler file: `internal/api/handlers/v0/validate.go`
- Performs comprehensive validation using `ValidationAll` option:
  - Schema version validation
  - Full schema validation
  - Semantic validation
- Always returns `200 OK` - validity is indicated in `result.Valid`
field
- Registered in router for both `/v0` and `/v0.1` prefixes

### 2. `/publish` Endpoint Validation Flow Changes

The publish endpoint now validates earlier in the request flow and
provides clearer error responses.

**Before:** Validation was mixed into `ValidatePublishRequest()` (which
also handled publisher extensions and registry ownership).

**After:**
1. Validates `ServerJSON` structure first using `ValidateServerJSON()`
(schema version + semantic validation)
2. Returns `422 Unprocessable Entity` with message: "Failed to publish
server, invalid schema: call /validate for details"
3. Only proceeds to publisher extensions/registry validation if schema
validation passes
4. No longer calls `ValidatePublishRequest()` internally for schema
validation

**Key Changes:**
- Validation happens before `CreateServer()` call
- Returns `422` on schema/semantic validation failures
- Error message directs users to `/validate` endpoint for details
- `ValidatePublishRequest()` no longer performs schema validation (added
note in comments)

### 3. `/edit` Endpoint Validation Flow Changes

Similar validation changes were applied to the edit endpoint.

**Changes:**
- Added `ValidateServerJSON()` call before server update
- Returns `422` with same error message format on validation failure
- Matches the `/publish` endpoint behavior

### 4. Validator Functions Refactoring

Updated validator functions to reflect that schema validation is now
done separately.

**Changes to `ValidatePublishRequest()`:**
- Removed internal `ValidateServerJSON()` call
- Added note: "ValidateServerJSON should be called separately before
this function"
- Now only validates publisher extensions and registry ownership

**Changes to `ValidateUpdateRequest()`:**
- Same change (removed schema validation, added note)
- Now only validates registry ownership

## CLI Changes

### 1. `validate` Command: Now Uses API Endpoint

**Before:** Performed local validation using
`validators.ValidateServerJSON()`.

**After:** Calls the `/v0/validate` endpoint on the registry.

**Details:**
- Reads `server.json` and sends it to the registry API
- Displays validation results returned by the server
- Uses the same error formatting as before
- Registry URL comes from token file (or default)
- No authentication required (public endpoint)

**Implementation:**
- Added `validateViaAPI()` function that POSTs to `/v0/validate`
- Parses `ValidationResult` response from server
- Uses shared `printValidationIssues()` for formatting

### 2. `publish` Command: Validation Only on 422 Errors

**Before:** Performed upfront local schema validation before publishing.

**After:** Attempts publish first, then validates only if server returns
422.

**Details:**
- Attempts publish immediately (no upfront validation)
- If publish returns 422 (Unprocessable Entity):
  - Calls `/v0/validate` endpoint to get detailed validation errors
  - Formats and displays them using same logic as `validate` command
  - Returns error with migration links
- For non-422 errors (401, 403, etc.), returns original error (no
validation call)

**Implementation Changes:**
- Removed upfront `runValidationAndPrintIssues()` call
- Modified `publishToRegistry()` to return HTTP status code alongside
response/error
- Added 422 detection logic that calls `validateViaAPI()`

### 3. Shared Validation Error Formatting

**Before:** `runValidationAndPrintIssues()` function handled both
validation and printing together.

**After:** Refactored to separate printing logic.

**Details:**
- Created `printValidationIssues()` function - just prints validation
issues (no validation logic)
- Removed `runValidationAndPrintIssues()` function (no longer used)
- Both `validate` and `publish` commands use `printValidationIssues()`
for consistent output

**Behavior:**
- Prints schema validation errors with migration guidance
- Prints all other validation issues with formatting
- Returns formatted error message string for schema issues

## Summary

Both CLI commands now use the `/validate` API endpoint for validation
instead of local validation. The `publish` command validates only after
receiving a 422 error response, and both commands share the same error
formatting via `printValidationIssues()`.

### Benefits

- **Centralized validation logic** - All validation happens on the
server
- **Always up-to-date** - CLI always uses current server validation
rules
- **Consistent experience** - Same validation behavior whether using CLI
or API directly
- **Better separation of concerns** - Schema validation separated from
publisher-specific validation
2026-02-04 15:52:07 +02:00
Bob Dickinson
fc88cf0870
Enhanced server.json validation (phase 1) (#636)
Added schema validation and support for exhaustive and more detailed
validation to existing validators. Added new `mcp-publisher validate`
command. No change to any existing tests or behavior other than the new
command.

## Motivation and Context

Many currently published servers fail schema validation. Of those that
pass, many have semantic/logical errors or other errors that make them
impossible to configure, or their configuration when applied doesn't
generate correct MCP server configuration (per their own documentation).

To address this, we need better tooling to validate servers and to
inform server publishers of errors, issues, and best practices in a way
that is clear and actionable, both during server development and at time
of publishing.

I have started a discussion about the broader topic:
https://github.com/modelcontextprotocol/registry/discussions/635

There is also a fairly detailed document describing the design,
architecture, implementation, future plans, etc. See [Enhanced
Validation Design](docs/explanations/proposed-enhanced-validation.md)

This PR is the first step in the process of improving validation. It
adds schema validation and updates all existing validators to use a new
model that allows them to track context and return rich and exhaustive
results. This has been done in a way that is backward compatible
(ValidateServerJSON is unchanged as are all existing validation unit
tests).

There is a new `mcp-publisher` command called `validate` that is
currenty the only place that schema validation and enhanced
(exhaustive/rich) validation results are exposed.

### Changes

Internal Validation improvements
- Schema validation has been implemented and is available as an option
in validation methods (defaults to off)
- Existing validators track the JSON path (context) of the attribute
they're evaluating, passing it to any child validators
- Existing validators are exhaustive (return all issues, not failing
fast on the first error)
- Existing validators return rich validation results, including:
  - type (schema, semantic, linter)
  - severity (error, warn, info)
  - path (JSON path to server.json element)
  - reference (to the schema element or rule that triggered the result)
  - message (currently the same as previous error message)

A new `validate` command has been added to `mcp-publisher` so publishers
can evaluate their server.json before publishing
- Includes new schema validation and previous semantic validation
- Shows full details of all issues encountered during validation

### Usage

Given a server.json that looks like this:

```json
{
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
  "name": "io.github.BobDickinson/registry",
  "description": "An MCP server that provides [describe what your server does]",
  "repository": {
    "url": "",
    "source": ""
  },
  "version": "=1.0.0",
  "packages": [
    {
      "registryType": "oci",
      "registryBaseUrl": "https://docker.io",
      "identifier": "registry",
      "version": "1.0.0",
      "transport": {
        "type": "sse"
      },
      "packageArguments": [
        {
          "type": "named",
          "name": "--mode",
          "description": "Operation mode",
          "format": "foo"
        }
      ],
      "environmentVariables": [
        {
          "description": "Your API key for the service",
          "isRequired": true,
          "format": "string",
          "isSecret": true,
          "name": "YOUR_API_KEY"
        }
      ]
    }
  ]
}
```

Run the command: 

`mcp-publisher validate server.json`

Which will produce the output:

```bash
❌ Validation failed with 5 issue(s):

1. [error] packages.0.packageArguments.0.format (schema)
   value must be one of "string", "number", "boolean", "filepath"
   Reference: #/definitions/Input/properties/format/enum from: [#/definitions/ServerDetail]/properties/packages/items/[#/definitions/Package]/properties/packageArguments/items/[#/definitions/Argument]/else/[#/definitions/NamedArgument]/allOf/0/[#/definitions/InputWithVariables]/allOf/0/[#/definitions/Input]/properties/format/enum

2. [error] packages.0.transport (schema)
   missing required fields: 'url'
   Reference: #/definitions/SseTransport/required from: [#/definitions/ServerDetail]/properties/packages/items/[#/definitions/Package]/properties/transport/else/else/[#/definitions/SseTransport]/required

3. [error] repository.url (schema)
   '' has invalid format 'uri'
   Reference: #/definitions/Repository/properties/url/format from: [#/definitions/ServerDetail]/properties/repository/[#/definitions/Repository]/properties/url/format

4. [error] version (semantic)
   version must be a specific version, not a range: "=1.0.0"
   Reference: version-looks-like-range

5. [error] packages[0].transport.url (semantic)
   url is required for sse transport type
   Reference: streamable-transport-url-required

Error: validation failed
```

Note in the results above:
- All errors contain the JSON path of the triggering element from
server.json.
- The first three errors are schema errors detected during schema
validation. The `Reference` provided contains an absolute path to the
schema rule that was violated, followed by the full schema path that
enforced that rule (with $ref redirect values substited and enclosed in
square brackets).
- The error messages provided by the existing semantic validators are
exactly the same as the previous error messages those validators
produced.

In the final solution we would remove semantic validators that are
redundant to schema validation (as in number 5 above). We have the
option to use the structured data produced by schema validation to
replace the generic schema validation message with a more descriptive
message if that is an issue (in particular, if the only argument for an
ad-hoc validator is that is produces a better/cleaner message, we would
just move that message creation code to the schema error result
formatter and special case it as opposed to having a redundant
validator).

### What's Next

If this general direction is supported and this PR is accepted, a fast
follow would include:

- Add new validators (infliuenced by observed issues) for both semantic
errors and best practices
- Require passing schema validation (in addition to improved semantic
validation) in order to publish
- Remove existing validation code that is redundant to schema validation
(ensuring error messages remain intelligible)
- Update validation client code and tests to handle the new rich return
types natively

### Issues

Schema validation requires embedding the `server.schema.json` file into
the validators package via `go:embed`, which is restricted from
embedding files outside of the package. For this reason we copy the
schema file into a schema subdir (via a prep target in the makefile) and
we .gitignore it. I tried cleaning up the copy in the make clean target,
but then the linter would complain about the embed target being missing
if it wasn't there during development. I also considered just checking
in the schema file and assuming a separate workflow for updating the
embedded schema file, but it's not clear what that workflow would be or
how it would be maintained. I'm open to a different approach, but the
schema file does need to be embedded somehow for schema validation to
work.

## How Has This Been Tested?
All existing validation tests pass, new tests implemented and all pass.

## Breaking Changes
No breaking changes, no behavior change except new `validate` command

## 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

---------

Co-authored-by: GitHub Action <action@github.com>
2026-01-16 00:49:39 +02:00
Radoslav Dimitrov
ad207f2390
Handle URL-encoded NUL bytes (%00) in URL path and query (#877)
<!-- Provide a brief summary of your changes -->

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

## 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>
2026-01-13 11:19:49 +02:00
lif
ab6a5a6b6d
fix(api): reject requests with NUL bytes in URL (#869)
## Summary
- Added `NulByteValidationMiddleware` to validate incoming requests
- Returns 400 Bad Request when NUL bytes (%00) detected in URL path or
query
- Prevents PostgreSQL encoding errors from being exposed as 500 errors

## Before
```
GET /v0.1/servers?cursor=%00
→ 500 Internal Server Error
→ "invalid byte sequence for encoding UTF8: 0x00"
```

## After
```
GET /v0.1/servers?cursor=%00
→ 400 Bad Request
→ "Invalid request: query parameters contain null bytes"
```

Fixes #862

Signed-off-by: majiayu000 <1835304752@qq.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2026-01-03 19:14:44 +02:00
Benedikt Reinartz
4338ec157a
Default to the local registry in the UI (#799)
<!-- Provide a brief summary of your changes -->
Change the UI default to the locally hosted registry.

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
Was quite confusing at first :)

## 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. -->
- [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
- [ ] I have added or updated documentation as needed
2025-12-16 21:59:39 +00:00
Tadas Antanavicius
b58836c48e
feat: Add LocalTransport/RemoteTransport with URL template variables (#570)
Co-authored with Claude Code, but I've closely reviewed/modified/vetted
all changes; ready for final review.

## Summary

This PR adds LocalTransport and RemoteTransport separation with URL
template variable support for remote servers, enabling e.g. multi-tenant
deployments with configurable endpoints.

**Key additions:**
- LocalTransport (for Packages) and RemoteTransport (for Remotes)
- URL template variables for remote transports (e.g., `{tenant_id}` in
URLs)
- Pattern validation ensuring URLs are URL-like while allowing templates
- Full test coverage via Go validator tests and documentation examples

## ⚠️ server.json Update

This PR updates the `server.json` schema. A new schema version will be
cut after this lands.

## Problem

The current schema describes URL templating with `{curly_braces}` for
transports but provides no mechanism for Remote contexts to define what
those variables should resolve to, making the feature unusable for
remote servers.

Referenced issue: #394
- Users need multi-tenant remote servers with different endpoints per
deployment
- Each tenant/region has its own URL (e.g., `us-cell1.example.com`,
`emea-cell1.example.com`)
- Current schema doesn't support parameterized remote URLs

## Solution

### Schema Architecture

**LocalTransport** (for Package context):
```typescript
LocalTransport = StdioTransport | StreamableHttpTransport | SseTransport
```
- Used in Package context (non-breaking rename of existing behavior)
- Variables in `{curly_braces}` reference parent Package
arguments/environment variables
- Supports all three transport types including stdio

**RemoteTransport** (for Remote context):
```typescript
RemoteTransport = (StreamableHttpTransport | SseTransport) + { variables: object }
```
- Extends StreamableHttp/Sse via `allOf` composition - no duplication!
- Adds `variables` object for URL templating
- Only supports streamable-http and sse (stdio not valid for remotes)

### Key Changes

**1. Schema Updates** (`openapi.yaml`, `server.schema.json`):
- Added `LocalTransport` union type for Package context
- Added `RemoteTransport` with `allOf` composition extending base
transports
- Updated Package to reference `LocalTransport`
- Updated remotes to reference `RemoteTransport`
- Added URL pattern validation: `^https?://[^\\s]+$` to both
StreamableHttp and SSE
- Removed `format: uri` from SSE (was blocking template variables)

**2. Go Types** (`pkg/model/types.go`):
- Base `Transport` struct unchanged for Package context
- `RemoteTransport` struct for remotes with `Variables` field
- Both work with existing validation infrastructure

**3. Validators** (`internal/validators/`):
- `validateRemoteTransport()` validates Remote-specific constraints
- `collectRemoteTransportVariables()` extracts available variables
- `IsValidTemplatedURL()` validates template variables reference defined
variables
- `IsValidRemoteURL()` handles template variable substitution before
localhost check
- 6 new test cases for remote transport variable validation

**4. Documentation Examples** (`generic-server-json.md`):
- Remote Server with URL Templating (StreamableHttp with `{tenant_id}`)
- SSE Remote with URL Templating (SSE with `{tenant_id}`)
- Local Server with URL Templating (Package with `{port}`)
- All examples validate via existing `validate-examples` tool

## Example: Remote Transport with Variables

```json
{
  "remotes": [
    {
      "type": "streamable-http",
      "url": "https://api.example.com/mcp/{tenant_id}/{region}",
      "variables": {
        "tenant_id": {
          "description": "Tenant identifier",
          "isRequired": true,
          "choices": ["us-cell1", "emea-cell1", "apac-cell1"]
        },
        "region": {
          "description": "Deployment region",
          "isRequired": true
        }
      }
    }
  ]
}
```

Clients configure the variables, and `{tenant_id}` and `{region}` get
replaced to connect to tenant-specific endpoints like
`https://api.example.com/mcp/us-cell1/east`.

## Architecture Details

**Context-based variable resolution:**
- **Package + LocalTransport**: `{port}` references `--port` argument or
`PORT` env var from parent Package
- **Remote + RemoteTransport**: `{tenant_id}` references
`variables.tenant_id` defined in the transport itself
- **Code reuse**: StreamableHttp/Sse definitions shared via `allOf`, not
duplicated
- **Validation**: Template variables validated against available
variables for each context

---

## Follow-on Work

Follow-on work will be done to adopt the feedback in
https://github.com/modelcontextprotocol/registry/pull/570#issuecomment-3367417636
regarding variable naming/prioritzation/scoping conventions, but should
not have any impact on the shape being introduced here (just
validations).

---------

Co-authored-by: yuna0x0 <yuna@yuna0x0.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Connor Peet <connor@peet.io>
2025-12-11 09:17:32 -08:00
Radoslav Dimitrov
c54a5ac277
Ensure tests are failing CI (#840) 2025-12-11 09:56:57 +02:00
adam jones
dcb5a6f696
Add 5s timeout to DNS auth lookup to prevent resource exhaustion (#825)
The DNS auth endpoint was vulnerable to resource exhaustion if an
attacker controlled a domain with a slow/non-responding authoritative
DNS server. Without a timeout, handler goroutines would block on DNS
resolution indefinitely, allowing an attacker to pile up goroutines
until OOM.

Production is protected by NGINX ingress's default 60s proxy timeout,
but this adds defense-in-depth with a tighter 5s bound.
2025-12-10 16:22:27 +00: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
Tadas Antanavicius
8f8fc89801
Fix missing $schema entries - data migration (#806)
Drafted by Claude Code, reviewed by me.

## Summary

This PR adds a data migration to fix 17 server entries that were
published with empty `$schema` fields.

**This is a pure data migration** - no code changes are needed because:

- Validation for the `$schema` field was already added in commit
[6d2001b](6d2001b)
on **2025-10-07**
- That validation was deployed to production on **2025-10-14** with
v1.3.0 ([PR
#668](https://github.com/modelcontextprotocol/registry/pull/668))
- All 17 affected entries were published before deployment (see dates
below)

## Affected Entries (17 total)

| Server | Version | Published |
|--------|---------|-----------|
| io.github.ruvnet/claude-flow | 2.0.0-alpha.104 | 2025-09-10 |
| io.github.ruvnet/claude-flow | 2.0.0-alpha.105 | 2025-09-10 |
| io.github.ruvnet/ruv-swarm | 1.0.18 | 2025-09-10 |
| io.github.ruvnet/ruv-swarm | 1.0.19 | 2025-09-10 |
| travel.kismet/mcp-server | 0.0.0 | 2025-09-10 |
| io.github.jkakar/cookwith-mcp | 1.0.0 | 2025-09-11 |
| io.github.jkakar/cookwith-mcp | 1.0.1 | 2025-09-12 |
| io.github.jkakar/cookwith-mcp | 1.0.2 | 2025-09-12 |
| io.github.OtherVibes/mcp-as-a-judge | 0.3.12 | 2025-09-18 |
| io.github.OtherVibes/mcp-as-a-judge | 0.3.13 | 2025-09-18 |
| io.github.OtherVibes/mcp-as-a-judge | 0.3.14 | 2025-09-18 |
| io.github.OtherVibes/mcp-as-a-judge | 0.3.20 | 2025-09-20 |
| io.github.Skills03/scrimba-teaching | 1.0.1 | 2025-09-21 |
| io.github.Skills03/scrimba-teaching | 1.1.0 | 2025-09-21 |
| io.github.Skills03/scrimba-teaching | 1.2.0 | 2025-09-21 |
| io.github.toby/mirror-mcp | 0.0.4 | 2025-10-01 |
| io.github.antuelle78/weather-mcp | 1.0.0 | 2025-10-09 ⚠️ |

**Note on outlier:** `io.github.antuelle78/weather-mcp` was published on
2025-10-09, which is after the validation code was committed
(2025-10-07) but before it was deployed (2025-10-14). This confirms the
entries slipped through due to the gap between merge and deployment.

## Dry Run SQL

To verify affected rows before running the migration:

```sql
SELECT server_name, version, value->>'$schema' as schema_value
FROM servers
WHERE (server_name, version) IN (
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.12'),
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.13'),
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.14'),
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.20'),
    ('io.github.Skills03/scrimba-teaching', '1.0.1'),
    ('io.github.Skills03/scrimba-teaching', '1.1.0'),
    ('io.github.Skills03/scrimba-teaching', '1.2.0'),
    ('io.github.antuelle78/weather-mcp', '1.0.0'),
    ('io.github.jkakar/cookwith-mcp', '1.0.0'),
    ('io.github.jkakar/cookwith-mcp', '1.0.1'),
    ('io.github.jkakar/cookwith-mcp', '1.0.2'),
    ('io.github.ruvnet/claude-flow', '2.0.0-alpha.104'),
    ('io.github.ruvnet/claude-flow', '2.0.0-alpha.105'),
    ('io.github.ruvnet/ruv-swarm', '1.0.18'),
    ('io.github.ruvnet/ruv-swarm', '1.0.19'),
    ('io.github.toby/mirror-mcp', '0.0.4'),
    ('travel.kismet/mcp-server', '0.0.0')
)
AND (value->>'$schema' = '' OR value->>'$schema' IS NULL);
```

Result against prod:

```sql
app=> SELECT server_name, version, value->>'$schema' as schema_value
FROM servers
WHERE (server_name, version) IN (
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.12'),
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.13'),
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.14'),
    ('io.github.OtherVibes/mcp-as-a-judge', '0.3.20'),
    ('io.github.Skills03/scrimba-teaching', '1.0.1'),
    ('io.github.Skills03/scrimba-teaching', '1.1.0'),
    ('io.github.Skills03/scrimba-teaching', '1.2.0'),
    ('io.github.antuelle78/weather-mcp', '1.0.0'),
    ('io.github.jkakar/cookwith-mcp', '1.0.0'),
    ('io.github.jkakar/cookwith-mcp', '1.0.1'),
    ('io.github.jkakar/cookwith-mcp', '1.0.2'),
    ('io.github.ruvnet/claude-flow', '2.0.0-alpha.104'),
    ('io.github.ruvnet/claude-flow', '2.0.0-alpha.105'),
    ('io.github.ruvnet/ruv-swarm', '1.0.18'),
    ('io.github.ruvnet/ruv-swarm', '1.0.19'),
    ('io.github.toby/mirror-mcp', '0.0.4'),
    ('travel.kismet/mcp-server', '0.0.0')
)
AND (value->>'$schema' = '' OR value->>'$schema' IS NULL);
             server_name             |     version     | schema_value 
-------------------------------------+-----------------+--------------
 io.github.toby/mirror-mcp           | 0.0.4           | 
 io.github.jkakar/cookwith-mcp       | 1.0.0           | 
 io.github.ruvnet/ruv-swarm          | 1.0.19          | 
 io.github.jkakar/cookwith-mcp       | 1.0.1           | 
 io.github.ruvnet/ruv-swarm          | 1.0.18          | 
 io.github.antuelle78/weather-mcp    | 1.0.0           | 
 io.github.jkakar/cookwith-mcp       | 1.0.2           | 
 io.github.OtherVibes/mcp-as-a-judge | 0.3.12          | 
 travel.kismet/mcp-server            | 0.0.0           | 
 io.github.Skills03/scrimba-teaching | 1.0.1           | 
 io.github.OtherVibes/mcp-as-a-judge | 0.3.20          | 
 io.github.Skills03/scrimba-teaching | 1.2.0           | 
 io.github.ruvnet/claude-flow        | 2.0.0-alpha.105 | 
 io.github.ruvnet/claude-flow        | 2.0.0-alpha.104 | 
 io.github.Skills03/scrimba-teaching | 1.1.0           | 
 io.github.OtherVibes/mcp-as-a-judge | 0.3.14          | 
 io.github.OtherVibes/mcp-as-a-judge | 0.3.13          | 
```

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-30 08:41:02 -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
adam jones
e7de7541c7
improve: Add hint about making GitHub org membership public in error messages (#772)
When users try to publish to a GitHub organization but don't have
permission, the error message now includes a helpful hint suggesting
they may need to make their organization membership public in GitHub
settings.

This addresses the issue where users couldn't understand why they were
unable to publish servers from organization repositories, even when they
were members of the organization.

Fixes #398

Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: adam jones <domdomegg@users.noreply.github.com>
2025-11-17 13:59:19 +00:00
Joel Verhagen
45bf43f9d1
Remove namespace match validation for websiteUrl and remote URL (#769)
<!-- Provide a brief summary of your changes -->

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

Resolve https://github.com/modelcontextprotocol/registry/issues/768 and
https://github.com/modelcontextprotocol/registry/issues/494.

Require HTTPS for remote URLs. Currently all remote URLs on the MCP
Registry are HTTPS.
Also fix a test bug when running on Windows (bad temp path).

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

Unit tests.
Published a server.json locally with a website URL not matching the
server name.

## 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. -->
- [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
- [ ] ~I have added or updated documentation as needed~ N/A

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
2025-11-17 11:32:59 +00:00
adam jones
8cf317af68
Add simple web UI for browsing the registry (#757)
## Summary

Adds a clean, minimal web interface at `/` for browsing MCP servers in
the registry. Built as a single self-contained HTML file embedded in the
Go binary.

## Features

- Responsive grid layout (1/2/3 columns based on screen size)
- Search servers by name
- Filter to show only latest versions (default on)
- "Recently Updated" section for servers updated in the last hour
- Expandable cards with full server metadata and JSON viewer
- Debug modal to switch between production/staging/local/custom API
endpoints
- Full URL parameter support and browser history navigation

## Implementation

- Single HTML file (~400 lines) with Tailwind CSS via CDN
- Zero build dependencies
- Embedded using `//go:embed` 
- Served directly from router at root path
- Uses stable v0.1 API endpoints

Fixes #99

---

AI disclosure: this is 99% vibed with Claude. It's intended as a v0 to
get something up and working, and then maybe at a later date we can
replace it with something more real.
2025-11-10 12:37:36 +00:00
Radoslav Dimitrov
ef4a60505e
Make Repository field properly optional in server.json (#717)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The Repository field in ServerJSON was not properly omitting empty
values
when marshaling to JSON. Even when a server had no repository
configured,
the JSON would include `"repository": {"url": "", "source": ""}` due to:

1. Repository struct fields (URL, Source) lacking omitempty tags
2. Repository being a value type instead of pointer in ServerJSON

This caused validation warnings for servers without repository URLs.

Changes:
- Add omitempty tags to Repository.URL and Repository.Source fields
- Change ServerJSON.Repository from model.Repository to
*model.Repository
- Update validateRepository to handle nil pointers
- Update all code creating Repository instances to use pointers

Now, servers without repository information correctly omit the field
entirely from JSON output, eliminating spurious validation warnings.
## 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-29 13:23:47 +00: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
Terry Nederveld
73d2174cec
feat: add CORS support to registry API (#711)
Fixes #710

This PR adds CORS middleware to enable browser-based clients and web
applications to access the registry API.

## Changes

- Add CORSMiddleware in internal/api/cors.go to handle preflight OPTIONS
requests and inject CORS headers
- Integrate CORS middleware into the server middleware stack
- Add CORS configuration options (CORS_ENABLED and CORS_ALLOWED_ORIGIN)

## Testing

- All existing tests pass
- Manual testing confirms CORS headers are present in responses
- OPTIONS preflight requests now return 204 with proper headers

## Configuration

New environment variables:
- MCP_REGISTRY_CORS_ENABLED (default: true)
- MCP_REGISTRY_CORS_ALLOWED_ORIGIN (default: *)
2025-10-27 18:07:13 +00:00
Joel Verhagen
6c0f085708
Add support for ECDSA P-384 (#685)
## Motivation and Context

See
https://github.com/modelcontextprotocol/registry/issues/482#issuecomment-3436993157
for full details.

This adds ECDSA P-384 (a popular NIST approved curve) in addition to
Ed25519.

## How Has This Been Tested?

I have tested with a local deployment against `mcp.joelverhagen.com`.
Also, I have added significant tests.

## Breaking Changes

None. (hopefully)

## 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
-->

@domdomegg and I spoke a bit about this on Discord:
https://discord.com/channels/1358869848138059966/1369487942862504016/1423710918180606052

---------

Co-authored-by: adam jones <domdomegg+git@gmail.com>
2025-10-24 12:02:33 -06: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
adam jones
f870a4c000
Improve golang type documentation and examples (#631)
Enhanced Go struct tags throughout `pkg/api/v0/types.go` and
`pkg/model/types.go` to better align with the OpenAPI specification.

## Changes

- Added comprehensive `doc` tags with detailed field descriptions
matching OpenAPI
- Added `example` tags showing valid values for fields (e.g.,
`"io.github.user/weather"`, `"npx"`)
- Added `format` tags (`uri`, `date-time`) for better type specification
- Added `pattern` and `enum` tags for validation constraints
- Added `placeholder` field to `Input` struct matching OpenAPI spec
- Removed redundant comments in favor of inline struct tags

These changes improve code clarity and make the types more
self-documenting, while maintaining compatibility with the OpenAPI
schema definitions in `docs/reference/api/openapi.yaml`.

---------

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-16 21:55:52 +01:00
Radoslav Dimitrov
2a80690063
Introduce a /version API (#667)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR introduces a `/version` API that serves the build
details along with the version of the API server being served. The
`/version` endpoint allows users to programmatically query which exact
version of the registry is running in production for debugging.
~~auditing, and compatibility checks~~.

## 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-15 17:24:46 +03:00
Radoslav Dimitrov
d9099e9117
Fix migration numbering and add migration 010 for package format (#675)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR fixes an issue we introduced on September, 30th that
caused some of the migrations to not apply. This PR fixes it. After
merging we'll have to reset staging as this would not work there (this
is expected).

## 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: -->
- [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
-->

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-15 17:00:39 +03:00
Radoslav Dimitrov
3592b2cc0c
Add the rest of the tag descriptions (#661)
<!-- Provide a brief summary of your changes -->

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

## 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:28:10 +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
087d8085b7
Replace / with - for operation IDs (#660)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
Follow up from #658 addressing the feedback from @tadasant 

## 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:26:43 +03:00
Aleksandr Beshkenadze
cce5e5df46
feat: add OpenAPI tags for improved SDK generation and API organization (#655)
## Summary
Adds OpenAPI tags to improve SDK generation and API documentation
organization, as discussed in #644.

## Changes
- **Go Code**: Define tag metadata (name + description) in
`internal/api/router/router.go` using Huma's `api.OpenAPI().Tags`
- **Reference Spec**: Update `docs/reference/api/openapi.yaml` to match
the generated spec
- **Tags Added**: servers, publish, auth, admin, health, ping

## Benefits
- ✅ Improved SDK generation with proper service grouping (e.g.,
`serversService`, `publishService`)
- ✅ Better API documentation organization in Scalar UI, Swagger UI, and
other tools
- ✅ Code-first approach ensures reference spec stays in sync via
compliance tests

## Testing
- ✅ Compliance test passes: `go test -v ./internal/api -run
TestOpenAPIEndpointCompliance`
- ✅ All 4 reference endpoints verified as implemented
- ✅ TypeScript SDK regenerated successfully with proper tag-based
grouping

## Related
Closes #644
2025-10-14 14:30:36 +03:00
Radoslav Dimitrov
1a7cd97099
Introduce v0.1 version of the API (#658)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR adds a new v0.1 API version. 

The goal is to mark v0.1 as a "freeze" point while keeping v0 as the
moving target for new features so integrators can start building on top
of something stable(r).

Details:
* This change is quite simple - we re-register the same handlers twice,
once as `v0` and once as `v0.1`
* Going forward, we'll keep v0.1 frozen while we add changes either on
top of both (non-breaking) or we'll have to branch if breaking changes
are necessary
* We all acknowledge this is not exactly best-practice, but it gives us
a way to gather feedback and allow clients to start integrating with the
API without breaking them (**note** this implies a follow up PR that
would branch the handlers used in v0.1).
* For integrators/clients: please provide your feedback - what works
well, what doesn't so we can take this into account going towards GA.

## 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? -->
No, this just adds a new API version alongside the existing one.

## 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
-->
Fixes: #649

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
2025-10-14 11:04:48 +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
adam jones
66993e9121
Add icon support to server.json schema (#620)
Implements icon support based on the Model Context Protocol
specification, allowing servers to specify display icons with optional
size, MIME type, and theme variants.

## Changes

- Add `Icon` type to `pkg/model/types.go` with struct tags for automatic
API validation
- Add `icons` field to `ServerJSON` in `pkg/api/v0/types.go`
- Add icon validation in `internal/validators/validators.go`:
  - Only HTTPS URLs allowed (no HTTP or data URIs)
  - Maximum URL length of 255 characters
  - Validates absolute URLs with proper scheme
- Update JSON schema and OpenAPI documentation with Icon definitions
- Add comprehensive test cases for icon validation
- Add example icon to seed.json (Airtable server)
- Change `websiteUrl` validation to HTTPS-only for consistency

## Implementation Details

The implementation uses a two-layer validation architecture:

1. **Huma framework** validates struct tags (`required`, `maxLength`,
`enum`, `pattern`) and returns 422 for violations
2. **Custom validators** handle business logic (HTTPS scheme
requirement, absolute URL requirement) and return 400 for violations

This approach leverages Huma's automatic validation while keeping
business-specific rules in the validator layer.

## Icon Field Specification

- `src` (required): HTTPS URL to icon resource, max 255 characters
- `mimeType` (optional): One of `image/png`, `image/jpeg`, `image/jpg`,
`image/svg+xml`, `image/webp`
- `sizes` (optional): Array of size strings in "WxH" format or "any" for
scalable formats
- `theme` (optional): Either "light" or "dark" to indicate intended
background

Multiple icons can be provided (e.g., for different themes or sizes).
2025-10-07 13:30:32 +01:00
adam jones
4f453ff2bd
Drop GET /v0/servers/{serverName} endpoint (#618)
Remove redundant endpoint as suggested in #617. Clients should now use
`GET /v0/servers/{serverName}/versions/latest` instead (using `latest`
as a special version value in the existing endpoint).

This simplifies the API surface and reduces implementation burden for
subregistries, while maintaining a clear migration path for consumers.

## Changes
- Removed `GET /v0/servers/{serverName}` endpoint
- Updated `GET /v0/servers/{serverName}/versions/{version}` to accept
`latest` as special version
- Updated all tests to use new endpoint pattern
- Updated documentation and OpenAPI spec
- Added breaking change notice to CHANGELOG

Fixes #617

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-07 13:56:45 +03:00
adam jones
44e35020b0
Add optional title field to server.json schema (#608)
## Summary

Introduces a new optional `title` field for MCP servers to provide a
clean, human-readable display name that clients and subregistries can
use for display purposes.

Fixes #606

## Changes

- Added `title` field to `ServerJSON` type (optional, max 100 chars)
- Added validation to reject titles ending with 'MCP Server', 'MCP
server', or 'MCP' suffixes
- Updated JSON schema and OpenAPI specification
- Added comprehensive test coverage for title validation
- Updated all documentation examples to demonstrate the new field

## Validation

The title field:
- Is optional (can be omitted)
- Must not be only whitespace if provided
- Must not end with MCP-related suffixes (case-insensitive)
- Can contain 'MCP' in the middle (e.g., "MCP Weather API" is valid)

Examples:
- ✅ Valid: "GitHub", "Weather API", "Slack Integration"
- ❌ Invalid: "GitHub MCP", "Weather MCP Server", "Slack-MCP"

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 11:57:01 +01:00
Radoslav Dimitrov
44950c23bb
Enforce schema validation on the publish and edit API level (#598)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR:
* Enforces schema validation on the API level (requests without schema
or older schema will fail)
* Updates the unit tests and examples to match that

## 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? -->
**Note this can be considered breaking if clients have not included it
before.**

## 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-05 13:57:10 +01:00
Tadas Antanavicius
e968cfdbca
fix: renumber migrations to fix staging deployment (#592)
## Summary

Fixes staging deployment by renumbering migrations to match what staging
has already applied.

## Problem

Staging already applied migration 008 (metadata separation) before we
inserted the new cleanup migration. When we added the cleanup as 008 and
renumbered metadata to 009, staging tried to apply 009 again and failed
with "column already exists".

## Current State

- **Staging**: Has run original 008 (metadata separation), marked as
complete. Failed trying to run 009 (which is the same migration
renumbered).
- **Production**: Has not run either 008 or 009 yet.

## Solution

- Rename `008_clean_invalid_data.sql` → `007a_clean_invalid_data.sql`
- Rename `009_separate_official_metadata.sql` →
`008_separate_official_metadata.sql` (back to original)

## How This Fixes It

With this renumbering:

1. **Staging behavior**:
- Sees 008 is already complete (the metadata migration it already ran)
- Sees 007a as new and runs it, but it will be a no-op (skips because
staging doesn't have the problematic production data)
   - Continues normally

2. **Production behavior**:
   - Runs 007a (cleanup migration) - removes invalid data
- Runs 008 (metadata separation) - applies successfully because data is
now clean
   - Both migrations run in the correct order as intended

## Testing

- Verified migration order works correctly
- Tested on empty database (test environment) - 007a skips as expected
- Tested with production data snapshot - 007a cleans data, 008 applies
successfully

This is a critical fix to unblock staging deployment.
2025-09-30 20:39:33 +03:00
Tadas Antanavicius
272ee9322d
fix: add server name validation and clean invalid data before migration 009 (#591)
## Summary

This PR adds two important improvements to ensure data integrity:

1. **Adds server name format validation at the API layer** to match the
database constraint from migration 008
2. **Includes migration 008 to clean up invalid data** before applying
strict constraints in migration 009

## Changes

### Server Name Validation
- Enforces validation at the API layer matching the database constraint
- **Namespace pattern**: `[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]` 
  - Must start and end with alphanumeric characters
  - Can contain dots and hyphens in the middle
- **Name pattern**: `[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]`
  - Must start and end with alphanumeric characters  
  - Can contain dots, underscores, and hyphens in the middle
- Provides clear, user-friendly error messages indicating which part is
invalid
- Uses DRY approach with composed regex patterns

### Migration 008: Data Cleanup
- Removes servers with invalid names or empty versions (5 servers
affected in production)
- Fixes invalid status values by setting them to 'active' (1 server
affected)
- Removes duplicate name+version combinations
- Includes comprehensive safety checks to prevent accidental data loss
- Must run before migration 009 which adds strict constraints

## Testing
- All validator tests pass
- Migration includes safety checks that match production data
- CI verification included

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-30 20:07:42 +03:00
Tadas Antanavicius
8bd13d5ed7
Apply gofmt formatting to all Go files (#588)
## Summary
- Applied `gofmt -s -w` across the entire codebase to ensure consistent
Go formatting
- This will trigger a deployment with minimal code changes

## Test plan
- [x] Pre-commit hooks pass
- [x] No functional changes, formatting only

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-30 17:32:22 +03:00
Tadas Antanavicius
2d49e5b68c
fix: prevent NULL status values during migration 008 (#587)
## Summary
- Fixes migration 008 failing with "column contains null values" error
- Adds COALESCE protection to handle 126 servers without status fields
- Includes test utilities to reproduce and verify fixes with production
data

## Problem

Migration 008 was failing in production because:
- 126 out of 781 servers have no `status` field in their JSON
- When extracting `rec.value->>'status'`, these became NULL values
- The `ALTER TABLE servers ALTER COLUMN status SET NOT NULL` constraint
then failed

## Solution

1. **Prevent NULLs during migration**: Use COALESCE to provide 'active'
default when status is NULL/empty
2. **Enhanced debugging**: Added RAISE NOTICE statements to log
problematic values
3. **Safety check**: Double-check that throws exception if any NULLs
remain
4. **Test utilities**: Created mirror_data tools to test migrations with
real production data

## Testing

Created and used `scripts/mirror_data/` tools to:
- Fetch all 781 servers from production API
- Load them into a test database
- Verify the migration fix works correctly

### Results:
- **Before fix**: 126 NULL status values created during migration
- **After fix**: 0 NULL status values, all servers have valid status
- Migration completes successfully with NOT NULL constraint

## Test plan
- [x] Tested locally with production data using mirror_data tools
- [x] Verified 0 NULL values after migration (vs 126 before)
- [x] Confirmed NOT NULL constraint adds successfully
- [ ] Deploy to staging and verify migration succeeds
- [ ] Monitor production deployment

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-30 06:43:26 -07:00
Radoslav Dimitrov
e39b008b41
Ensure we populate sane defaults for NULL values (#583)
<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR tries to ensure sane defaults for entries that for one
reason or another have NULL values for some of their properties.

## 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-09-30 15:47:18 +03:00