MCPcopy Index your code
hub / github.com/modelcontextprotocol/registry / ValidateOCI

Function ValidateOCI

internal/validators/registries/oci.go:56–145  ·  view source on GitHub ↗

ValidateOCI validates that an OCI image contains the correct MCP server name annotation. Supports canonical OCI references including: - registry/namespace/image:tag - registry/namespace/image@sha256:digest - registry/namespace/image:tag@sha256:digest - namespace/image:tag (defaults to docker.io) Su

(ctx context.Context, pkg model.Package, serverName string)

Source from the content-addressed store, hash-verified

54// - Google Artifact Registry (*.pkg.dev)
55// - Microsoft Container Registry (mcr.microsoft.com)
56func ValidateOCI(ctx context.Context, pkg model.Package, serverName string) error {
57 if pkg.Identifier == "" {
58 return ErrMissingIdentifierForOCI
59 }
60
61 // Validate that old format fields are not present
62 if pkg.RegistryBaseURL != "" {
63 return fmt.Errorf("OCI packages must not have 'registryBaseUrl' field - use canonical reference in 'identifier' instead (e.g., 'docker.io/owner/image:1.0.0')")
64 }
65 if pkg.Version != "" {
66 return fmt.Errorf("OCI packages must not have 'version' field - include version in 'identifier' instead (e.g., 'docker.io/owner/image:1.0.0')")
67 }
68 if pkg.FileSHA256 != "" {
69 return fmt.Errorf("OCI packages must not have 'fileSha256' field")
70 }
71
72 // Parse the OCI reference using go-containerregistry's name package
73 // This handles all the complexity of reference parsing including defaults
74 ref, err := name.ParseReference(pkg.Identifier)
75 if err != nil {
76 return fmt.Errorf("invalid OCI reference: %w", err)
77 }
78
79 // Validate that the registry is in the allowlist
80 registry := ref.Context().RegistryStr()
81 if !isAllowedRegistry(registry) {
82 return fmt.Errorf("%w: %s", ErrUnsupportedRegistry, registry)
83 }
84
85 // Add explicit timeout to prevent hanging on slow registries
86 // Use a new context with timeout to avoid modifying the caller's context
87 timeoutCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
88 defer cancel()
89
90 // Fetch the image using anonymous authentication (public images only)
91 // The go-containerregistry library handles:
92 // - OCI auth discovery via WWW-Authenticate headers
93 // - Token negotiation for different registries
94 // - Rate limiting and retries
95 // - Multi-arch manifest resolution
96 img, err := remote.Image(ref, remote.WithAuth(authn.Anonymous), remote.WithContext(timeoutCtx))
97 if err != nil {
98 // Check if this is a timeout error
99 if errors.Is(err, context.DeadlineExceeded) {
100 return fmt.Errorf("OCI image validation timed out after 30 seconds for '%s'. The registry may be slow or unreachable", pkg.Identifier)
101 }
102
103 // Check for specific HTTP status codes
104 var transportErr *transport.Error
105 if errors.As(err, &transportErr) {
106 switch transportErr.StatusCode {
107 case http.StatusTooManyRequests:
108 // Fail closed: a 429 means we could not verify the
109 // `io.modelcontextprotocol.server.name` label on the image, which is
110 // the only ownership proof we apply to OCI packages. Returning nil
111 // here would let a publisher bind their server record to an arbitrary
112 // public image they do not control, which is the bug this branch
113 // used to have. Surface the rate-limit as a retryable error instead.

Calls 1

isAllowedRegistryFunction · 0.85

Used in the wild real call sites across dependent graphs

searching dependent graphs…