Enforce and document file hash requirement for MCPB packages (#368)
## Summary Implements mandatory file hash validation for MCPB (MCP Bundle) packages to ensure file integrity and security. ## Changes - **Validator enforcement**: MCPB packages now require `file_sha256` field at publish time - **Test coverage**: Updated all MCPB test cases to include valid SHA-256 hashes - **Documentation**: Updated publisher guide and schema to reflect hash requirement - **Error handling**: Clear error message when hash is missing ## Security Impact This ensures the complete security workflow for MCPB packages: 1. Authors generate and provide SHA-256 hashes when publishing 2. Registry validates hash is present (but doesn't verify content) 3. MCP clients validate hash matches downloaded file before installation Fixes #351
This commit is contained in:
parent
16790150e9
commit
8f5e17d0fe
8 changed files with 72 additions and 24 deletions
|
|
@ -219,6 +219,15 @@ The official MCP registry currently only supports the official Docker registry (
|
||||||
### Requirements
|
### Requirements
|
||||||
**MCP reference** - MCPB package URLs must contain "mcp" somewhere within them, to ensure the correct artifact has been uploaded. This may be with the `.mcpb` extension or in the name of your repository.
|
**MCP reference** - MCPB package URLs must contain "mcp" somewhere within them, to ensure the correct artifact has been uploaded. This may be with the `.mcpb` extension or in the name of your repository.
|
||||||
|
|
||||||
|
**File integrity** - MCPB packages must include a SHA-256 hash for file integrity verification. This is required at publish time and MCP clients will validate this hash before installation.
|
||||||
|
|
||||||
|
### How to Generate File Hashes
|
||||||
|
Calculate the SHA-256 hash of your MCPB file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl dgst -sha256 server.mcpb
|
||||||
|
```
|
||||||
|
|
||||||
### Example server.json
|
### Example server.json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -226,12 +235,19 @@ The official MCP registry currently only supports the official Docker registry (
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"registry_type": "mcpb",
|
"registry_type": "mcpb",
|
||||||
"identifier": "https://github.com/you/your-repo/releases/download/v1.0.0/server.mcpb"
|
"identifier": "https://github.com/you/your-repo/releases/download/v1.0.0/server.mcpb",
|
||||||
|
"file_sha256": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### File Hash Validation
|
||||||
|
- **Authors** are responsible for generating correct SHA-256 hashes when creating server.json
|
||||||
|
- **MCP clients** validate the hash before installing packages to ensure file integrity
|
||||||
|
- **The official registry** stores hashes but does not validate them
|
||||||
|
- **Subregistries** may choose to implement their own validation. This enables them to perform security scanning on MCPB files, and ensure clients get the same security scanned content.
|
||||||
|
|
||||||
The official MCP registry currently only supports artifacts hosted on GitHub or GitLab releases.
|
The official MCP registry currently only supports artifacts hosted on GitHub or GitLab releases.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,8 @@
|
||||||
},
|
},
|
||||||
"file_sha256": {
|
"file_sha256": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "SHA-256 hash of the package file for integrity verification.",
|
"pattern": "^[a-f0-9]{64}$",
|
||||||
|
"description": "SHA-256 hash of the package file for integrity verification. Required for MCPB packages and optional for other package types. Authors are responsible for generating correct SHA-256 hashes when creating server.json. If present, MCP clients must validate the downloaded file matches the hash before running packages to ensure file integrity.",
|
||||||
"example": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"
|
"example": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce"
|
||||||
},
|
},
|
||||||
"runtime_hint": {
|
"runtime_hint": {
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,7 @@ func TestPublishIntegration(t *testing.T) {
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
Identifier: "github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||||
Version: "1.7.2",
|
Version: "1.7.2",
|
||||||
|
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
Transport: model.Transport{
|
Transport: model.Transport{
|
||||||
Type: model.TransportTypeStdio,
|
Type: model.TransportTypeStdio,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||||
Version: "0.0.36",
|
Version: "0.0.36",
|
||||||
|
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
Transport: model.Transport{
|
Transport: model.Transport{
|
||||||
Type: model.TransportTypeStdio,
|
Type: model.TransportTypeStdio,
|
||||||
},
|
},
|
||||||
|
|
@ -149,6 +150,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
|
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
Transport: model.Transport{
|
Transport: model.Transport{
|
||||||
Type: model.TransportTypeStdio,
|
Type: model.TransportTypeStdio,
|
||||||
},
|
},
|
||||||
|
|
@ -209,6 +211,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
Identifier: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
|
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
Transport: model.Transport{
|
Transport: model.Transport{
|
||||||
Type: model.TransportTypeStdio,
|
Type: model.TransportTypeStdio,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,7 @@ func TestPublishEndpoint(t *testing.T) {
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: "https://github.com/example/server/releases/download/v1.0.0/server.tar.gz",
|
Identifier: "https://github.com/example/server/releases/download/v1.0.0/server.tar.gz",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
|
FileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
Transport: model.Transport{
|
Transport: model.Transport{
|
||||||
Type: model.TransportTypeStdio,
|
Type: model.TransportTypeStdio,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func ValidateMCPB(ctx context.Context, pkg model.Package, _ string) error {
|
func ValidateMCPB(ctx context.Context, pkg model.Package, _ string) error {
|
||||||
|
// MCPB packages must include a file hash for integrity verification
|
||||||
|
if pkg.FileSHA256 == "" {
|
||||||
|
return fmt.Errorf("MCPB package must include a file_sha256 hash for integrity verification")
|
||||||
|
}
|
||||||
|
|
||||||
err := validateMCPBUrl(pkg.Identifier)
|
err := validateMCPBUrl(pkg.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
packageName string
|
packageName string
|
||||||
serverName string
|
serverName string
|
||||||
|
fileSHA256 string
|
||||||
expectError bool
|
expectError bool
|
||||||
errorMessage string
|
errorMessage string
|
||||||
}{
|
}{
|
||||||
|
|
@ -23,18 +24,29 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
name: "valid MCPB package should pass",
|
name: "valid MCPB package should pass",
|
||||||
packageName: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
packageName: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",
|
||||||
serverName: "io.github.domdomegg/airtable-mcp-server",
|
serverName: "io.github.domdomegg/airtable-mcp-server",
|
||||||
|
fileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid MCPB package should pass",
|
name: "valid MCPB package should pass",
|
||||||
packageName: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
packageName: "https://github.com/microsoft/playwright-mcp/releases/download/v0.0.36/playwright-mcp-extension-v0.0.36.zip",
|
||||||
serverName: "com.microsoft/playwright-mcp",
|
serverName: "com.microsoft/playwright-mcp",
|
||||||
|
fileSHA256: "abc123ef4567890abcdef1234567890abcdef1234567890abcdef1234567890",
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid MCPB package with .mcpb extension should fail accessibility check",
|
name: "MCPB package without file hash should fail",
|
||||||
packageName: "https://github.com/example/server/releases/download/v1.0.0/server.mcpb",
|
packageName: "https://github.com/example/server/releases/download/v1.0.0/server.mcpb",
|
||||||
serverName: "com.example/test",
|
serverName: "com.example/test",
|
||||||
|
fileSHA256: "",
|
||||||
|
expectError: true,
|
||||||
|
errorMessage: "must include a file_sha256 hash for integrity verification",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-existent .mcpb package should fail accessibility check",
|
||||||
|
packageName: "https://github.com/example/server/releases/download/v1.0.0/server.mcpb",
|
||||||
|
serverName: "com.example/test",
|
||||||
|
fileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
expectError: true,
|
expectError: true,
|
||||||
errorMessage: "not publicly accessible",
|
errorMessage: "not publicly accessible",
|
||||||
},
|
},
|
||||||
|
|
@ -42,6 +54,7 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
name: "invalid URL without mcp anywhere should fail",
|
name: "invalid URL without mcp anywhere should fail",
|
||||||
packageName: "https://github.com/example/server/releases/download/v1.0.0/server.tar.gz",
|
packageName: "https://github.com/example/server/releases/download/v1.0.0/server.tar.gz",
|
||||||
serverName: "com.example/test",
|
serverName: "com.example/test",
|
||||||
|
fileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
expectError: true,
|
expectError: true,
|
||||||
errorMessage: "URL must contain 'mcp'",
|
errorMessage: "URL must contain 'mcp'",
|
||||||
},
|
},
|
||||||
|
|
@ -49,6 +62,7 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
name: "invalid URL format should fail",
|
name: "invalid URL format should fail",
|
||||||
packageName: "not://a valid url for mcpb!",
|
packageName: "not://a valid url for mcpb!",
|
||||||
serverName: "com.example/test",
|
serverName: "com.example/test",
|
||||||
|
fileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
expectError: true,
|
expectError: true,
|
||||||
errorMessage: "invalid MCPB package URL",
|
errorMessage: "invalid MCPB package URL",
|
||||||
},
|
},
|
||||||
|
|
@ -56,6 +70,7 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
name: "non-existent file should fail accessibility check",
|
name: "non-existent file should fail accessibility check",
|
||||||
packageName: "https://github.com/nonexistent/repo/releases/download/v1.0.0/mcp-server.tar.gz",
|
packageName: "https://github.com/nonexistent/repo/releases/download/v1.0.0/mcp-server.tar.gz",
|
||||||
serverName: "com.example/test",
|
serverName: "com.example/test",
|
||||||
|
fileSHA256: "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce",
|
||||||
expectError: true,
|
expectError: true,
|
||||||
errorMessage: "not publicly accessible",
|
errorMessage: "not publicly accessible",
|
||||||
},
|
},
|
||||||
|
|
@ -66,6 +81,7 @@ func TestValidateMCPB(t *testing.T) {
|
||||||
pkg := model.Package{
|
pkg := model.Package{
|
||||||
RegistryType: model.RegistryTypeMCPB,
|
RegistryType: model.RegistryTypeMCPB,
|
||||||
Identifier: tt.packageName,
|
Identifier: tt.packageName,
|
||||||
|
FileSHA256: tt.fileSHA256,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := registries.ValidateMCPB(ctx, pkg, tt.serverName)
|
err := registries.ValidateMCPB(ctx, pkg, tt.serverName)
|
||||||
|
|
|
||||||
|
|
@ -1189,33 +1189,37 @@ func TestValidate_RegistryTypesAndUrls(t *testing.T) {
|
||||||
baseURL string
|
baseURL string
|
||||||
identifier string
|
identifier string
|
||||||
version string
|
version string
|
||||||
|
fileSHA256 string
|
||||||
expectError bool
|
expectError bool
|
||||||
}{
|
}{
|
||||||
// Valid registry types (should pass)
|
// Valid registry types (should pass)
|
||||||
{"valid_npm", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeNPM, model.RegistryURLNPM, "airtable-mcp-server", "1.7.2", false},
|
{"valid_npm", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeNPM, model.RegistryURLNPM, "airtable-mcp-server", "1.7.2", "", false},
|
||||||
{"valid_npm", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeNPM, "", "airtable-mcp-server", "1.7.2", false},
|
{"valid_npm", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeNPM, "", "airtable-mcp-server", "1.7.2", "", false},
|
||||||
{"valid_pypi", "io.github.domdomegg/time-mcp-pypi", model.RegistryTypePyPI, model.RegistryURLPyPI, "time-mcp-pypi", "1.0.1", false},
|
{"valid_pypi", "io.github.domdomegg/time-mcp-pypi", model.RegistryTypePyPI, model.RegistryURLPyPI, "time-mcp-pypi", "1.0.1", "", false},
|
||||||
{"valid_pypi", "io.github.domdomegg/time-mcp-pypi", model.RegistryTypePyPI, "", "time-mcp-pypi", "1.0.1", false},
|
{"valid_pypi", "io.github.domdomegg/time-mcp-pypi", model.RegistryTypePyPI, "", "time-mcp-pypi", "1.0.1", "", false},
|
||||||
{"valid_oci", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeOCI, model.RegistryURLDocker, "domdomegg/airtable-mcp-server", "1.7.2", false},
|
{"valid_oci", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeOCI, model.RegistryURLDocker, "domdomegg/airtable-mcp-server", "1.7.2", "", false},
|
||||||
{"valid_nuget", "io.github.domdomegg/time-mcp-server", model.RegistryTypeNuGet, model.RegistryURLNuGet, "TimeMcpServer", "1.0.2", false},
|
{"valid_nuget", "io.github.domdomegg/time-mcp-server", model.RegistryTypeNuGet, model.RegistryURLNuGet, "TimeMcpServer", "1.0.2", "", false},
|
||||||
{"valid_nuget", "io.github.domdomegg/time-mcp-server", model.RegistryTypeNuGet, "", "TimeMcpServer", "1.0.2", false},
|
{"valid_nuget", "io.github.domdomegg/time-mcp-server", model.RegistryTypeNuGet, "", "TimeMcpServer", "1.0.2", "", false},
|
||||||
{"valid_mcpb_github", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeMCPB, model.RegistryURLGitHub, "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", false},
|
{"valid_mcpb_github", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeMCPB, model.RegistryURLGitHub, "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", false},
|
||||||
{"valid_mcpb_github", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeMCPB, "", "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", false},
|
{"valid_mcpb_github", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeMCPB, "", "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", false},
|
||||||
{"valid_mcpb_gitlab", "io.gitlab.fforster/gitlab-mcp", model.RegistryTypeMCPB, model.RegistryURLGitLab, "https://gitlab.com/fforster/gitlab-mcp/-/releases/v1.31.0/downloads/gitlab-mcp_1.31.0_Linux_x86_64.tar.gz", "1.31.0", false}, // this is not actually a valid mcpb, but it's the closest I can get for testing for now
|
{"valid_mcpb_gitlab", "io.gitlab.fforster/gitlab-mcp", model.RegistryTypeMCPB, model.RegistryURLGitLab, "https://gitlab.com/fforster/gitlab-mcp/-/releases/v1.31.0/downloads/gitlab-mcp_1.31.0_Linux_x86_64.tar.gz", "1.31.0", "abc123ef4567890abcdef1234567890abcdef1234567890abcdef1234567890", false}, // this is not actually a valid mcpb, but it's the closest I can get for testing for now
|
||||||
{"valid_mcpb_gitlab", "io.gitlab.fforster/gitlab-mcp", model.RegistryTypeMCPB, "", "https://gitlab.com/fforster/gitlab-mcp/-/releases/v1.31.0/downloads/gitlab-mcp_1.31.0_Linux_x86_64.tar.gz", "1.31.0", false}, // this is not actually a valid mcpb, but it's the closest I can get for testing for now
|
{"valid_mcpb_gitlab", "io.gitlab.fforster/gitlab-mcp", model.RegistryTypeMCPB, "", "https://gitlab.com/fforster/gitlab-mcp/-/releases/v1.31.0/downloads/gitlab-mcp_1.31.0_Linux_x86_64.tar.gz", "1.31.0", "abc123ef4567890abcdef1234567890abcdef1234567890abcdef1234567890", false}, // this is not actually a valid mcpb, but it's the closest I can get for testing for now
|
||||||
|
|
||||||
|
// Test MCPB without file hash (should fail)
|
||||||
|
{"invalid_mcpb_no_hash", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeMCPB, model.RegistryURLGitHub, "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", "", true},
|
||||||
|
|
||||||
// Invalid registry types (should fail)
|
// Invalid registry types (should fail)
|
||||||
{"invalid_maven", "io.github.domdomegg/airtable-mcp-server", "maven", model.RegistryURLNPM, "airtable-mcp-server", "1.7.2", true},
|
{"invalid_maven", "io.github.domdomegg/airtable-mcp-server", "maven", model.RegistryURLNPM, "airtable-mcp-server", "1.7.2", "", true},
|
||||||
{"invalid_cargo", "io.github.domdomegg/time-mcp-pypi", "cargo", model.RegistryURLPyPI, "time-mcp-pypi", "1.0.1", true},
|
{"invalid_cargo", "io.github.domdomegg/time-mcp-pypi", "cargo", model.RegistryURLPyPI, "time-mcp-pypi", "1.0.1", "", true},
|
||||||
{"invalid_gem", "io.github.domdomegg/airtable-mcp-server", "gem", model.RegistryURLDocker, "domdomegg/airtable-mcp-server", "1.7.2", true},
|
{"invalid_gem", "io.github.domdomegg/airtable-mcp-server", "gem", model.RegistryURLDocker, "domdomegg/airtable-mcp-server", "1.7.2", "", true},
|
||||||
{"invalid_unknown", "io.github.domdomegg/time-mcp-server", "unknown", model.RegistryURLNuGet, "TimeMcpServer", "1.0.2", true},
|
{"invalid_unknown", "io.github.domdomegg/time-mcp-server", "unknown", model.RegistryURLNuGet, "TimeMcpServer", "1.0.2", "", true},
|
||||||
{"invalid_blank", "io.github.domdomegg/time-mcp-server", "", model.RegistryURLNuGet, "TimeMcpServer", "1.0.2", true},
|
{"invalid_blank", "io.github.domdomegg/time-mcp-server", "", model.RegistryURLNuGet, "TimeMcpServer", "1.0.2", "", true},
|
||||||
{"invalid_docker", "io.github.domdomegg/airtable-mcp-server", "docker", model.RegistryURLDocker, "domdomegg/airtable-mcp-server", "1.7.2", true}, // should be oci
|
{"invalid_docker", "io.github.domdomegg/airtable-mcp-server", "docker", model.RegistryURLDocker, "domdomegg/airtable-mcp-server", "1.7.2", "", true}, // should be oci
|
||||||
{"invalid_github", "io.github.domdomegg/airtable-mcp-server", "github", model.RegistryURLGitHub, "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", true}, // should be mcpb
|
{"invalid_github", "io.github.domdomegg/airtable-mcp-server", "github", model.RegistryURLGitHub, "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb", "1.7.2", "", true}, // should be mcpb
|
||||||
|
|
||||||
{"invalid_mix_1", "io.github.domdomegg/time-mcp-server", model.RegistryTypeNuGet, model.RegistryURLNPM, "TimeMcpServer", "1.0.2", true},
|
{"invalid_mix_1", "io.github.domdomegg/time-mcp-server", model.RegistryTypeNuGet, model.RegistryURLNPM, "TimeMcpServer", "1.0.2", "", true},
|
||||||
{"invalid_mix_2", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeOCI, model.RegistryURLNPM, "domdomegg/airtable-mcp-server", "1.7.2", true},
|
{"invalid_mix_2", "io.github.domdomegg/airtable-mcp-server", model.RegistryTypeOCI, model.RegistryURLNPM, "domdomegg/airtable-mcp-server", "1.7.2", "", true},
|
||||||
{"invalid_mix_3", "io.github.domdomegg/airtable-mcp-server", model.RegistryURLNPM, model.RegistryURLNPM, "airtable-mcp-server", "1.7.2", true},
|
{"invalid_mix_3", "io.github.domdomegg/airtable-mcp-server", model.RegistryURLNPM, model.RegistryURLNPM, "airtable-mcp-server", "1.7.2", "", true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
@ -1237,6 +1241,7 @@ func TestValidate_RegistryTypesAndUrls(t *testing.T) {
|
||||||
RegistryType: tc.registryType,
|
RegistryType: tc.registryType,
|
||||||
RegistryBaseURL: tc.baseURL,
|
RegistryBaseURL: tc.baseURL,
|
||||||
Version: tc.version,
|
Version: tc.version,
|
||||||
|
FileSHA256: tc.fileSHA256,
|
||||||
Transport: model.Transport{
|
Transport: model.Transport{
|
||||||
Type: "stdio",
|
Type: "stdio",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue