feat: implement DNS namespace validation and remote URL uniqueness checks (#306)

## 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>
This commit is contained in:
adam jones 2025-08-26 23:03:38 +01:00 • committed by GitHub
commit 923b2e07b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 497 additions and 29 deletions

View file

@ -71,7 +71,7 @@ func RegisterPublishEndpoint(api huma.API, registry service.RegistryService, cfg
// Publish the server with extensions
publishedServer, err := registry.Publish(publishRequest)
if err != nil {
return nil, huma.Error500InternalServerError("Failed to publish server", err)
return nil, huma.Error400BadRequest("Failed to publish server", err)
}
// Return the published server in extension wrapper format

View file

@ -99,10 +99,10 @@ func TestPublishIntegration(t *testing.T) {
assert.Equal(t, publishReq.Server.VersionDetail.Version, response.Server.VersionDetail.Version)
})
t.Run("successful publish without auth (no prefix)", func(t *testing.T) {
t.Run("successful publish with none auth (no prefix)", func(t *testing.T) {
publishReq := model.PublishRequest{
Server: model.ServerDetail{
Name: "test-mcp-server-no-auth",
Name: "com.example/test-mcp-server-no-auth",
Description: "A test MCP server without authentication",
Repository: model.Repository{
URL: "https://example.com/test-mcp-server",

View file

@ -203,9 +203,9 @@ func TestPublishEndpoint(t *testing.T) {
},
},
setupMocks: func(registry *MockRegistryService) {
registry.On("Publish", mock.AnythingOfType("model.PublishRequest")).Return(nil, errors.New("database error"))
registry.On("Publish", mock.AnythingOfType("model.PublishRequest")).Return(nil, errors.New("cannot publish duplicate version"))
},
expectedStatus: http.StatusInternalServerError,
expectedStatus: http.StatusBadRequest,
expectedError: "Failed to publish server",
},
}