## 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>
(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
<!-- 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>
## 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>
- Add workflow_dispatch to deploy.yml for manual deployment triggers
- Document that releases don't auto-deploy to production
- Add instructions for deploying releases to production
- Add verification steps for deployment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Summary
Fixes a race condition where a server could have no version marked as
`isLatest` if the database operations failed independently.
## Changes
- **CreateServer signature**: Now accepts `oldLatestVersionID` parameter
to atomically unmark previous latest version
- **PostgreSQL implementation**: Uses a transaction to ensure UPDATE
(unmark old latest) and INSERT (create new version) happen atomically
- **Service layer**: Maintains `WithPublishLock` for concurrent
operation serialization
- **Two-level protection**: Advisory lock prevents races, transaction
ensures atomicity
## Test plan
- [x] Existing race condition tests pass
- [x] Integration tests pass
- [x] Unit tests pass
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The follow PR removes the in-memory database option from the registry.
We had a chat about this with @domdomegg and although it is supposed to
be convenient, it isn't that useful in reality. We want to test and
develop the registry in a setup that is closer to the environment it is
supposed to be run. One of the main reasons are:
* We don't use it anywhere else besides unit testing and potentially
local development. Instead we should use postgresql for reliable results
(in-memory was actually giving false positive results)
* We have to keep maintaining it and make sure it mimics the behaviour
we have for postgresql which is not yielding a lot of value
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
Yes, I've updated the test unit make target to run in the postgresql
environment and now we can see the race condition issue happening.
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
No
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
---------
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
## Summary
Fixes race conditions in concurrent server publishing that could result
in data integrity violations:
- Multiple versions marked as `isLatest=true` for the same server
- Duplicate (serverId, version) combinations
- Same server name assigned different serverIDs
## Solution
Uses two complementary approaches for correctness:
### 1. PostgreSQL Advisory Locks
- Serializes concurrent publishes per server name using
`pg_advisory_xact_lock`
- Uses FNV-1a hash to convert server name to lock ID
- Transaction-scoped locks auto-release on commit/rollback
- **Required after #465**: The serverId introduction means concurrent
publishes must atomically coordinate whether to reuse an existing
serverId (for additional versions) or generate a new one (for first
version). Without locks, race conditions would assign different
serverIDs to versions of the same server.
### 2. Unique Constraints (Migration 007)
Two new indexes provide data integrity guarantees:
- **idx_unique_server_version**: Ensures (serverId, version) is unique
across all versions
- **idx_unique_latest_version**: Partial index ensuring only one version
per serverId has `isLatest=true`
These constraints serve as defense-in-depth safety nets. Even if lock
logic had a bug, the database would reject integrity violations.
Also drops redundant `idx_servers_version_id` since `version_id` is
already the primary key.
## Why WithPublishLock validation is needed after #465
The `ctx.Err()` check at the start of `WithPublishLock` is critical
because:
1. After #465 introduced serverId, the publish flow must atomically
check existing versions and assign/reuse serverId
2. If the context is cancelled during lock acquisition, we could
deadlock
3. Checking ctx validity before acquiring locks prevents these deadlocks
## Testing
Added `TestPublishConcurrentVersionsNoRace` with 100 concurrent
goroutines publishing different versions of the same server. All succeed
with:
- Exactly one version marked as latest
- All versions sharing the same serverId
- No duplicate (serverId, version) combinations
NB: this only covers the in memory db, not the postgres db. It'd be nice
to write tests for this one as well, but our current setup is a bit
annoying for this. Separately looking into ways to easily run all our
tests with real postgres and ideally scrap memory.go completely.
Wielded Claude Code to do this; across many iterations and have reviewed
all the code + largely rewritten the PR commentary here.
This PR implements the server ID consistency changes from
[#396](https://github.com/modelcontextprotocol/registry/issues/396). Our
goal is to:
- Rename `id` to `versionId` to make it clear that it is the ID for a
specific version, not a server
- Introduce `serverId` as a net new concept. This will be 1:1 with
`name`. While not strictly necessary (we could just use name as a PK),
the convention of having a UUID as an ID on our objects is just a
helpful subjective architecture decision. Plus also leaves open the door
to reconsidering the "should we allow renames" question.
## Key Changes
### 🗄️ Database Schema Changes
- **Renamed**: `id` column → `version_id` (unique identifier for each
server version)
### 📋 Metadata Structure Updates
- **JSON Schema**: Updated
`_meta.io.modelcontextprotocol.registry/official` to include:
- `serverId`: Consistent ID across all versions of a server
- `versionId`: Unique ID for this specific version (renamed from `id`)
### 🔧 API Endpoint Updates
- **New endpoint structure**: Changed from version-ID-based to
server-ID-based URLs:
- `GET /v0/servers/{server_id}` - Get latest version of server
- `GET /v0/servers/{server_id}?version=X.X.X` - Get specific version
- `GET /v0/servers/{server_id}/versions` - List all versions of a server
- `PUT /v0/servers/{server_id}?version=X.X.X` - Edit specific version
- **Consistent URL patterns**: All server endpoints now use `server_id`
as the primary identifier
### Database Migration Strategy
The migration (`005_add_server_id_rename_version_id.sql`):
1. Adds new `version_id` column
2. Generates deterministic UUIDs for servers with the same name
3. Updates JSON metadata to add `serverId` and rename `id` to
`versionId`
4. Copies existing `id` values to `version_id` column
5. Drops old `id` column and makes `version_id` the new primary key
## Testing
Besides of course automated tests passing, I:
1. Set up my local db with the pre-005-migration state, imported all the
production data
2. Ran the migration
3. Spot checked some diffs of the data that resulted --
```json
// before
{
"name": "io.github.ruvnet/ruv-swarm",
"_meta": {
"io.modelcontextprotocol.registry/official": {
"id": "0a9cda7f-2b73-4839-844b-6582f335de42",
"is_latest": false,
"updated_at": "2025-09-10T17:21:34.92137012Z",
"published_at": "2025-09-10T17:15:32.69690433Z"
}
},
"version": "1.0.18",
"repository": {
"url": "",
"source": ""
},
"description": "Neural network swarm orchestration with WebAssembly acceleration and MCP integration"
}
// after
{
"name": "io.github.ruvnet/ruv-swarm",
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "e8538952-96af-4a33-9e31-7fbed012521e",
"is_latest": false,
"versionId": "0a9cda7f-2b73-4839-844b-6582f335de42",
"updated_at": "2025-09-10T17:21:34.92137012Z",
"published_at": "2025-09-10T17:15:32.69690433Z"
}
},
"version": "1.0.18",
"repository": {
"url": "",
"source": ""
},
"description": "Neural network swarm orchestration with WebAssembly acceleration and MCP integration"
}
```
Servers that have multiple versions stayed consistent in their
`serverId`:
```json
// First version
{
"name": "app.getdialer/dialer",
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "80f08bdb-f3e4-4f19-b072-1676ae49b663",
"is_latest": false,
"versionId": "04f25b17-331d-48d7-b08b-37baece208a7",
"updated_at": "2025-09-09T00:16:49.162620072Z",
"published_at": "2025-09-08T23:47:09.452137971Z"
}
},
"status": "active",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"remotes": [
{
"url": "https://getdialer.app/mcp",
"type": "streamable-http"
},
{
"url": "https://getdialer.app/sse",
"type": "sse"
}
],
"version": "1.0.0",
"repository": {
"url": "",
"source": ""
},
"description": "An MCP server that provides your you make outbound phone calls using your own phone number"
}
// Second version
{
"name": "app.getdialer/dialer",
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "80f08bdb-f3e4-4f19-b072-1676ae49b663",
"is_latest": true,
"versionId": "faa26cb9-3aad-415e-aece-f715fff5fe5e",
"updated_at": "2025-09-09T00:16:49.107842808Z",
"published_at": "2025-09-09T00:16:49.107842808Z"
}
},
"status": "active",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"remotes": [
{
"url": "https://getdialer.app/mcp",
"type": "streamable-http"
},
{
"url": "https://getdialer.app/sse",
"type": "sse"
}
],
"version": "1.0.1",
"repository": {
"url": "",
"source": ""
},
"description": "An MCP server that provides lets you make outbound phone calls using your own phone number"
}
```
And I also tried publishing a test server anew; looked good:
```bash
⏺ Bash(curl -s "http://localhost:8080/v0/servers/102dc4e4-6ca8-40e6-aa11-cd45763d3352" | jq '.')
⎿ {
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"name": "io.modelcontextprotocol.anonymous/test-claude-migration-server",
"description": "A test server to verify the new migration and API endpoints work correctly",
"status": "active",
"repository": {
"url": "https://github.com/example/test-claude-migration-server",
"source": "github",
"id": "example/test-claude-migration-server"
},
"version": "1.0.0",
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "102dc4e4-6ca8-40e6-aa11-cd45763d3352",
"versionId": "48c9cd55-7bc4-4ac2-896e-59e81eb653e4",
"published_at": "2025-09-12T18:33:23.264203-07:00",
"updated_at": "2025-09-12T18:33:23.264203-07:00",
"is_latest": true
}
}
}
⏺ Bash(curl -s "http://localhost:8080/v0/servers/102dc4e4-6ca8-40e6-aa11-cd45763d3352/versions" | jq '.')
⎿ {
"servers": [
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
"name": "io.modelcontextprotocol.anonymous/test-claude-migration-server",
"description": "A test server to verify the new migration and API endpoints work correctly",
"status": "active",
"repository": {
"url": "https://github.com/example/test-claude-migration-server",
"source": "github",
"id": "example/test-claude-migration-server"
},
"version": "1.0.0",
"_meta": {
"io.modelcontextprotocol.registry/official": {
"serverId": "102dc4e4-6ca8-40e6-aa11-cd45763d3352",
"versionId": "48c9cd55-7bc4-4ac2-896e-59e81eb653e4",
"published_at": "2025-09-12T18:33:23.264203-07:00",
"updated_at": "2025-09-12T18:33:23.264203-07:00",
"is_latest": true
}
}
}
],
"metadata": {
"count": 1
}
}
```
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Preeti Dewani <preetidewani1990@gmail.com>
Co-authored-by: adam jones <adamj+git@anthropic.com>
Co-authored-by: Adam Jones <adamj@anthropic.com>
## Summary
Simplified the schema structure by:
- Replacing `version_detail.version` with direct `version` field
- Removing unused `release_date` field from seed data
- Updating all API types, models, and tests to match new structure
- Maintaining backwards compatibility in API responses
This change makes the schema more intuitive and reduces nesting depth.
<!-- 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 `transport` type used in Package and Remotes
* Added `docker` to the `runtime_hint` examples
* Added support for templated URLs. It is available for streamable-http
and sse transports for Packages. I didn't thought of reasons to have
this for Remotes, but we can enable it if needed.
* Added validation for templated URLs so we can ensure all referenced
placeholder variables are available.
* For streamable-http and SSE transport types the URL is now a required
property
* Updated the publisher init command to reflect these changes
* Updated the seed.json file
* Added unit tests for the transport type related changes
* Updated the server.json schema and the external pkg structs to reflect
the new transport type
* Updated the examples
Thanks to @kkkarthik for spotting it! 🙏
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
Ran the tests locally
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
Yes
## 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)
- [x] 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>
Fixes#96
## Motivation and Context
See #96
## How Has This Been Tested?
Has some unit and integration tests, still needs proper testing of the
different registry types
## Breaking Changes
Yes, new package uploads will need extra verification
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Motivation and Context
The database logic was getting a bit unwieldy and fragile - especially
because business logic that should really have lived at the registry
service level was being pushed into the persistence level.
This PR tightens the interface, simplifies the code there, as well as
rips out a bunch of mocks etc. we don't need.
## How Has This Been Tested?
Locally everything works
## Breaking Changes
The list endpoint was surprisingly always filtering for isLatest only,
which seems like a bug? I have removed this filter.
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR eliminates duplicate struct definitions by migrating
the publisher tool to use canonical types from pkg/model, creating a
single source of truth across the codebase. The publisher tool
previously had its own copies of structs that were duplicated, ie.
ServerJSON, Package, Repository, VersionDetail, EnvironmentVariable,
RuntimeArgument and a few others which was not optimal
**Changes in the PR:**
- Refactored the structs to single canonical types located at pkg/model
- Refactor the implementation to use/build canonical structures directly
- Remove all duplicate definitions (5 structs eliminated)
- Publisher now uses model.ServerJSON, model.Package, model.Repository,
etc.
- Renamed some of the structs to better reflect their use case, i.e.
ServerJSON instead of ServerDetail
- Fixes an issue where the Remote property was missing from the previous
publisher copy of the Server
**Benefits:**
- Single source of truth - Schema changes in one place
- Type safety - Compiler catches mismatches
- Enhanced validation - Field constraints and bson tags
- Zero breaking changes - Same CLI interface and JSON output
- Future-proof - New canonical fields automatically available
Supersedes: #217 and #240
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
Locally
## 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: -->
- [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>
## Summary
- Add reverse-DNS namespace format validation for server names
(namespace/name)
- Validate remote URLs match the DNS namespace derived from hostname
- Prevent duplicate remote URLs across different servers
- Support localhost/development URLs without namespace restrictions
- Add comprehensive test coverage for validation logic
- Update database layer to support remote_url filtering
Fixes#180 by ensuring proper namespace governance and preventing URL
conflicts.
---------
Co-authored-by: Claude <noreply@anthropic.com>