Skip to content

Ban 'latest' as a reserved version string - #413

Merged
domdomegg merged 2 commits into
mainfrom
adamj/ban-latest-version
Sep 10, 2025
Merged

domdomegg merged 2 commits into
mainfrom
adamj/ban-latest-version

Conversation

@domdomegg

@domdomegg domdomegg commented Sep 10, 2025 •

Copy link
Copy Markdown
Member

Summary

  • Adds validation to prevent servers from being published with version string "latest"
  • "latest" is now a reserved keyword that cannot be used as a version string

Implementation

  • Added ErrReservedVersionString error constant
  • Created validateVersion function to check for reserved version strings
  • Integrated validation into the package field validation flow
  • Added comprehensive test coverage

This is so we can potentially support things like #333 in future more easily, as I imagine we might want to.

Add validation to prevent servers from being published with version
string "latest" as it is reserved for future use. This adds:

- New error constant ErrReservedVersionString
- validateVersion function to check for reserved strings
- Integration into package field validation
- Test coverage for the validation

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

Co-Authored-By: Claude <[email protected]>

:house: Remote-Dev: homespace
@domdomegg
domdomegg enabled auto-merge (squash) September 10, 2025 16:38
@connor4312

Copy link
Copy Markdown
Contributor

We may also want to validate that the version is a specific version and doesn't look like a semver version range (prefixed with =, ^, ~, etc)

@rdimitrov rdimitrov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed an issue to address @connor4312's comment separately 👍

@domdomegg
domdomegg merged commit 41aacc1 into main Sep 10, 2025
@domdomegg
domdomegg deleted the adamj/ban-latest-version branch September 10, 2025 17:52
@frenchi

frenchi commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Just an fyi of a suspected bug: this bans latest on serverDetail.Packages.version but not serverDetail.Version. Fixed over in #435 .

Repro case:

{
			name: "server with reserved version 'latest'",
			serverDetail: apiv0.ServerJSON{
				Name:        "com.example/test-server",
				Description: "A test server",
				Repository: model.Repository{
					URL:    "https://github.com/owner/repo",
					Source: "github",
				},
				Version: "latest",
				Packages: []model.Package{
					{
						Identifier:      "test-package",
						RegistryType:    "npm",
						RegistryBaseURL: "https://registry.npmjs.org",
						Version:         "1.0.0",
						Transport: model.Transport{
							Type: "stdio",
						},
					},
				},
			},
			expectedError: validators.ErrReservedVersionString.Error(), 
		},

@domdomegg

Copy link
Copy Markdown
Member Author

Good catch @frenchi, thank you! Will review that.

domdomegg pushed a commit that referenced this pull request Sep 12, 2025
<!-- Provide a brief summary of your changes -->

Validates that server versions are not ranges.


**NB:** This also introduces `validateVersion` as a check on the
top-level `ValidateServerJSON`, as previously we were only checking
version ranges of packages via `validatePackageField` (i.e. `latest`
would have still been a valid top level server version), despite #413's
wording suggesting otherwise.

## Motivation and Context
Solves #415

## How Has This Been Tested?

Tests added in `internal/validators/validators_test.go`

`make check` is clean.

## Breaking Changes
As per @domdomegg's comment, we should
> check nobody has done this in the existing registry data (if they
have, we should explore and understand why people have done this / if
it's valid etc.).

## 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)
- [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
-->
rdimitrov added a commit that referenced this pull request Oct 24, 2026
…rguments (#1583)

## Summary

Publish accepts arguments that violate the schema. `validateArgument`
only checks the named branch, so a `type` that is empty or outside the
enum passes through, and a positional argument with neither `value` nor
`valueHint` also passes. The scan in #1546 counted 52 entries with bad
argument types and 8 bare positional ones, my own paging of /v0/servers
found even more of the first kind. One I checked by hand:
`io.github.Jordan-Horner/[email protected]` (empty type under
packageArguments), another is `com.keyboardcrumbs/[email protected]` (type
"literal", still active). Every published schema version requires `type`
to be "positional" or "named" and positional args to carry `value` or
`valueHint`. Relates to #1546, the empty repository case is #1555's, and
version "latest" is already handled since #413.

## Change

`validateArgument` becomes a switch. Named branch unchanged. Positional
now requires `value` or `valueHint`. Unknown or empty `type` is
rejected. Two new errors in constants.go, references
`invalid-argument-type` and
`positional-argument-value-or-hint-required`.

Why not full schema validation at publish: the enhanced validation
design doc gates that behind the anyOf error consolidation (Phase 3),
raw anyOf failures on arguments come out as a noisy multi-error cluster.
These two checks give one clean message each and are one case arm each,
easy to delete at Phase 5 when schema-first lands. Until then publish
and edit just agree with what /v0/validate already reports.

Heads up on two consequences: editing a stored server that still carries
one of these legacy arguments will 422 until the argument is fixed, and
the importer skips invalid servers with a warning, so a re-seed from a
live dump drops those entries instead of failing. Also #1339 adds a
check inside the named branch of the same function, whichever lands
second has a small conflict to resolve there, happy to do it from my
side.

## Test

Tests first, they fail on main, the handler regression actually
publishes an empty-type argument with 200 on main and gets 422 with the
fix. On the branch: go test ./internal/... all green including the
handler tests against postgres, golangci-lint v2.13.1 (what CI pins) 0
issues, make validate passes. Two existing fixtures had positional args
with neither value nor valueHint, they got a valueHint since no
published schema version ever allowed that shape. New error paths and
references are pinned in validation_detailed_test.go.

## AI Disclosure

AI assistance (Claude) was used for issue research and during
development. All changes were reviewed and tested by the author.

---------

Co-authored-by: Radoslav Dimitrov <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants