-
Notifications
You must be signed in to change notification settings - Fork 207
Add MCPServerEntry validation controller and MCPGroup integration #4664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a23561c
Add MCPServerEntry validation controller and MCPGroup integration
JAORMX b61f9b2
Add MCPServerEntry to Chainsaw RBAC assertions and integration test s…
JAORMX c66df7c
Address review feedback for MCPServerEntry controller
JAORMX def7bed
Recover deleted pieces from MCPServerEntries
JAORMX 65e3a08
Add missing Entries and EntryCount fields to MCPGroupStatus
JAORMX e7f2e0a
Fix operator E2E test ClusterRole assertion to match generated RBAC
JAORMX 01991e7
Align chainsaw RBAC assertions with generated ClusterRole
JAORMX File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| // SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package v1alpha1 | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| // MCPServerEntrySpec defines the desired state of MCPServerEntry. | ||
| // MCPServerEntry is a zero-infrastructure catalog entry that declares a remote MCP | ||
| // server endpoint. Unlike MCPRemoteProxy, it creates no pods, services, or deployments. | ||
| type MCPServerEntrySpec struct { | ||
| // RemoteURL is the URL of the remote MCP server. | ||
| // Both HTTP and HTTPS schemes are accepted at admission time. | ||
| // +kubebuilder:validation:Required | ||
| // +kubebuilder:validation:Pattern=`^https?://` | ||
| RemoteURL string `json:"remoteURL"` | ||
|
|
||
| // Transport is the transport method for the remote server (sse or streamable-http). | ||
| // No default is set (unlike MCPRemoteProxy) because MCPServerEntry points at external | ||
| // servers the user doesn't control — requiring explicit transport avoids silent mismatches. | ||
| // +kubebuilder:validation:Required | ||
| // +kubebuilder:validation:Enum=sse;streamable-http | ||
| Transport string `json:"transport"` | ||
|
|
||
| // GroupRef is the name of the MCPGroup this entry belongs to. | ||
| // Required — every MCPServerEntry must be part of a group for vMCP discovery. | ||
| // +kubebuilder:validation:Required | ||
| // +kubebuilder:validation:MinLength=1 | ||
| GroupRef string `json:"groupRef"` | ||
|
|
||
| // ExternalAuthConfigRef references a MCPExternalAuthConfig resource for token exchange | ||
| // when connecting to the remote MCP server. The referenced MCPExternalAuthConfig must | ||
| // exist in the same namespace as this MCPServerEntry. | ||
| // +optional | ||
| ExternalAuthConfigRef *ExternalAuthConfigRef `json:"externalAuthConfigRef,omitempty"` | ||
|
|
||
| // HeaderForward configures headers to inject into requests to the remote MCP server. | ||
| // Use this to add custom headers like API keys or correlation IDs. | ||
| // +optional | ||
| HeaderForward *HeaderForwardConfig `json:"headerForward,omitempty"` | ||
|
|
||
| // CABundleRef references a ConfigMap containing CA certificates for TLS verification | ||
| // when connecting to the remote MCP server. | ||
| // +optional | ||
| CABundleRef *CABundleSource `json:"caBundleRef,omitempty"` | ||
| } | ||
|
|
||
| // MCPServerEntryStatus defines the observed state of MCPServerEntry. | ||
| type MCPServerEntryStatus struct { | ||
| // ObservedGeneration reflects the generation most recently observed by the controller. | ||
| // +optional | ||
| ObservedGeneration int64 `json:"observedGeneration,omitempty"` | ||
|
|
||
| // Phase indicates the current lifecycle phase of the MCPServerEntry. | ||
| // +optional | ||
| // +kubebuilder:default=Pending | ||
| Phase MCPServerEntryPhase `json:"phase,omitempty"` | ||
|
|
||
| // Conditions represent the latest available observations of the MCPServerEntry's state. | ||
| // +listType=map | ||
| // +listMapKey=type | ||
| // +optional | ||
| Conditions []metav1.Condition `json:"conditions,omitempty"` | ||
| } | ||
|
|
||
| // MCPServerEntryPhase represents the lifecycle phase of an MCPServerEntry. | ||
| // +kubebuilder:validation:Enum=Valid;Pending;Failed | ||
| type MCPServerEntryPhase string | ||
|
|
||
| const ( | ||
| // MCPServerEntryPhaseValid indicates all validations passed and the entry is usable. | ||
| MCPServerEntryPhaseValid MCPServerEntryPhase = "Valid" | ||
|
|
||
| // MCPServerEntryPhasePending is the initial state before the first reconciliation. | ||
| MCPServerEntryPhasePending MCPServerEntryPhase = "Pending" | ||
|
|
||
| // MCPServerEntryPhaseFailed indicates one or more referenced resources are missing or invalid. | ||
| MCPServerEntryPhaseFailed MCPServerEntryPhase = "Failed" | ||
| ) | ||
|
|
||
| // Condition types for MCPServerEntry. | ||
| // Reuses shared condition type constants from mcpserver_types.go where the string | ||
| // values match (GroupRefValidated, ExternalAuthConfigValidated, CABundleRefValidated). | ||
| const ( | ||
| // ConditionTypeMCPServerEntryValid indicates overall validation status of the MCPServerEntry. | ||
| // Uses the shared "Valid" condition type since this is a configuration resource, not a workload. | ||
| ConditionTypeMCPServerEntryValid = ConditionTypeValid | ||
|
|
||
| // ConditionTypeMCPServerEntryGroupRefValidated indicates whether the referenced MCPGroup exists. | ||
| ConditionTypeMCPServerEntryGroupRefValidated = ConditionGroupRefValidated | ||
|
|
||
| // ConditionTypeMCPServerEntryAuthConfigValidated indicates whether the referenced | ||
| // MCPExternalAuthConfig exists (when configured). | ||
| ConditionTypeMCPServerEntryAuthConfigValidated = ConditionTypeExternalAuthConfigValidated | ||
|
|
||
| // ConditionTypeMCPServerEntryCABundleRefValidated indicates whether the referenced | ||
| // CA bundle ConfigMap exists (when configured). | ||
| ConditionTypeMCPServerEntryCABundleRefValidated = ConditionCABundleRefValidated | ||
| ) | ||
|
|
||
| // Condition reasons for MCPServerEntry. | ||
| // GroupRef reasons reuse shared constants from mcpserver_types.go. | ||
| // CABundle reasons reuse shared constants from mcpserver_types.go. | ||
| const ( | ||
| // ConditionReasonMCPServerEntryValid indicates the entry passed all validations. | ||
| ConditionReasonMCPServerEntryValid = "ConfigValid" | ||
|
|
||
| // ConditionReasonMCPServerEntryInvalid indicates one or more validations failed. | ||
| ConditionReasonMCPServerEntryInvalid = "ConfigInvalid" | ||
|
|
||
| // ConditionReasonMCPServerEntryGroupRefValidated reuses the shared GroupRef reason. | ||
| ConditionReasonMCPServerEntryGroupRefValidated = ConditionReasonGroupRefValidated | ||
|
|
||
| // ConditionReasonMCPServerEntryGroupRefNotFound reuses the shared GroupRef reason. | ||
| ConditionReasonMCPServerEntryGroupRefNotFound = ConditionReasonGroupRefNotFound | ||
|
|
||
| // ConditionReasonMCPServerEntryGroupRefNotReady reuses the shared GroupRef reason. | ||
| ConditionReasonMCPServerEntryGroupRefNotReady = ConditionReasonGroupRefNotReady | ||
|
|
||
| // ConditionReasonMCPServerEntryAuthConfigValid indicates the referenced auth config exists. | ||
| ConditionReasonMCPServerEntryAuthConfigValid = "AuthConfigValid" | ||
|
|
||
| // ConditionReasonMCPServerEntryAuthConfigNotFound indicates the referenced auth config was not found. | ||
| ConditionReasonMCPServerEntryAuthConfigNotFound = "AuthConfigNotFound" | ||
|
|
||
| // ConditionReasonMCPServerEntryAuthConfigNotConfigured indicates no auth config ref is set. | ||
| ConditionReasonMCPServerEntryAuthConfigNotConfigured = "AuthConfigNotConfigured" | ||
|
|
||
| // ConditionReasonMCPServerEntryCABundleRefValid reuses the shared CABundle reason. | ||
| ConditionReasonMCPServerEntryCABundleRefValid = ConditionReasonCABundleRefValid | ||
|
|
||
| // ConditionReasonMCPServerEntryCABundleRefNotFound reuses the shared CABundle reason. | ||
| ConditionReasonMCPServerEntryCABundleRefNotFound = ConditionReasonCABundleRefNotFound | ||
|
|
||
| // ConditionReasonMCPServerEntryCABundleRefNotConfigured indicates no CA bundle ref is set. | ||
| ConditionReasonMCPServerEntryCABundleRefNotConfigured = "CABundleRefNotConfigured" | ||
| ) | ||
|
|
||
| //+kubebuilder:object:root=true | ||
| //+kubebuilder:subresource:status | ||
| //+kubebuilder:resource:shortName=mcpentry,categories=toolhive | ||
| //+kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" | ||
| //+kubebuilder:printcolumn:name="Transport",type="string",JSONPath=".spec.transport" | ||
| //+kubebuilder:printcolumn:name="Remote URL",type="string",JSONPath=".spec.remoteURL" | ||
| //+kubebuilder:printcolumn:name="Group",type="string",JSONPath=".spec.groupRef" | ||
| //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" | ||
|
|
||
| // MCPServerEntry is the Schema for the mcpserverentries API. | ||
| // It declares a remote MCP server endpoint for vMCP discovery and routing | ||
| // without deploying any infrastructure. | ||
| type MCPServerEntry struct { | ||
| metav1.TypeMeta `json:",inline"` // nolint:revive | ||
| metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
|
||
| Spec MCPServerEntrySpec `json:"spec,omitempty"` | ||
| Status MCPServerEntryStatus `json:"status,omitempty"` | ||
| } | ||
|
|
||
| //+kubebuilder:object:root=true | ||
|
|
||
| // MCPServerEntryList contains a list of MCPServerEntry. | ||
| type MCPServerEntryList struct { | ||
| metav1.TypeMeta `json:",inline"` // nolint:revive | ||
| metav1.ListMeta `json:"metadata,omitempty"` | ||
| Items []MCPServerEntry `json:"items"` | ||
| } | ||
|
|
||
| func init() { | ||
| SchemeBuilder.Register(&MCPServerEntry{}, &MCPServerEntryList{}) | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.