Skip to content

Commit 028cc6d

Browse files
yroblataskbot
andauthored
vMCP: Kubernetes - VirtualMCPCompositeToolDefinition CRD (#2412)
* vMCP: Kubernetes - VirtualMCPCompositeToolDefinition CRD Design and implement the VirtualMCPCompositeToolDefinition CRD for defining reusable composite workflows that can be referenced by multiple VirtualMCPServer instances. * fix lint * add tests * complete coverage * changes from review --------- Co-authored-by: taskbot <[email protected]>
1 parent f1705ca commit 028cc6d

15 files changed

+3189
-45
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package v1alpha1
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
"k8s.io/apimachinery/pkg/runtime"
6+
)
7+
8+
// VirtualMCPCompositeToolDefinitionSpec defines the desired state of VirtualMCPCompositeToolDefinition
9+
type VirtualMCPCompositeToolDefinitionSpec struct {
10+
// Name is the workflow name exposed as a composite tool
11+
// +kubebuilder:validation:Required
12+
// +kubebuilder:validation:MinLength=1
13+
// +kubebuilder:validation:MaxLength=64
14+
// +kubebuilder:validation:Pattern=`^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$`
15+
Name string `json:"name"`
16+
17+
// Description is a human-readable description of the workflow
18+
// +kubebuilder:validation:Required
19+
// +kubebuilder:validation:MinLength=1
20+
Description string `json:"description"`
21+
22+
// Parameters defines the input parameter schema for the workflow
23+
// Each key is a parameter name, each value is the parameter specification
24+
// +optional
25+
Parameters map[string]ParameterSpec `json:"parameters,omitempty"`
26+
27+
// Steps defines the workflow step definitions
28+
// Steps are executed sequentially in Phase 1
29+
// Phase 2 will support DAG execution via dependsOn
30+
// +kubebuilder:validation:Required
31+
// +kubebuilder:validation:MinItems=1
32+
Steps []WorkflowStep `json:"steps"`
33+
34+
// Timeout is the overall workflow timeout
35+
// Defaults to 30m if not specified
36+
// +kubebuilder:default="30m"
37+
// +kubebuilder:validation:Pattern=`^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$`
38+
// +optional
39+
Timeout string `json:"timeout,omitempty"`
40+
41+
// FailureMode defines the failure handling strategy
42+
// - abort: Stop execution on first failure (default)
43+
// - continue: Continue executing remaining steps
44+
// - best_effort: Try all steps, report partial success
45+
// +kubebuilder:validation:Enum=abort;continue;best_effort
46+
// +kubebuilder:default=abort
47+
// +optional
48+
FailureMode string `json:"failureMode,omitempty"`
49+
}
50+
51+
// VirtualMCPCompositeToolDefinitionStatus defines the observed state of VirtualMCPCompositeToolDefinition
52+
type VirtualMCPCompositeToolDefinitionStatus struct {
53+
// ValidationStatus indicates the validation state of the workflow
54+
// - Valid: Workflow structure is valid
55+
// - Invalid: Workflow has validation errors
56+
// +optional
57+
ValidationStatus ValidationStatus `json:"validationStatus,omitempty"`
58+
59+
// ValidationErrors contains validation error messages if ValidationStatus is Invalid
60+
// +optional
61+
ValidationErrors []string `json:"validationErrors,omitempty"`
62+
63+
// ReferencingVirtualServers lists VirtualMCPServer resources that reference this workflow
64+
// This helps track which servers need to be reconciled when this workflow changes
65+
// +optional
66+
ReferencingVirtualServers []string `json:"referencingVirtualServers,omitempty"`
67+
68+
// ObservedGeneration is the most recent generation observed for this VirtualMCPCompositeToolDefinition
69+
// It corresponds to the resource's generation, which is updated on mutation by the API Server
70+
// +optional
71+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
72+
73+
// Conditions represent the latest available observations of the workflow's state
74+
// +optional
75+
Conditions []metav1.Condition `json:"conditions,omitempty"`
76+
}
77+
78+
// ValidationStatus represents the validation state of a workflow
79+
// +kubebuilder:validation:Enum=Valid;Invalid;Unknown
80+
type ValidationStatus string
81+
82+
const (
83+
// ValidationStatusValid indicates the workflow is valid
84+
ValidationStatusValid ValidationStatus = "Valid"
85+
86+
// ValidationStatusInvalid indicates the workflow has validation errors
87+
ValidationStatusInvalid ValidationStatus = "Invalid"
88+
89+
// ValidationStatusUnknown indicates validation hasn't been performed yet
90+
ValidationStatusUnknown ValidationStatus = "Unknown"
91+
)
92+
93+
// Condition types for VirtualMCPCompositeToolDefinition
94+
const (
95+
// ConditionTypeWorkflowValidated indicates whether the workflow has been validated
96+
ConditionTypeWorkflowValidated = "WorkflowValidated"
97+
98+
// Note: ConditionTypeReady is shared across multiple resources and defined in mcpremoteproxy_types.go
99+
)
100+
101+
// Condition reasons for VirtualMCPCompositeToolDefinition
102+
const (
103+
// ConditionReasonValidationSuccess indicates workflow validation succeeded
104+
ConditionReasonValidationSuccess = "ValidationSuccess"
105+
106+
// ConditionReasonValidationFailed indicates workflow validation failed
107+
ConditionReasonValidationFailed = "ValidationFailed"
108+
109+
// ConditionReasonSchemaInvalid indicates parameter or step schema is invalid
110+
ConditionReasonSchemaInvalid = "SchemaInvalid"
111+
112+
// ConditionReasonTemplateInvalid indicates template syntax is invalid
113+
ConditionReasonTemplateInvalid = "TemplateInvalid"
114+
115+
// ConditionReasonDependencyCycle indicates step dependencies contain cycles
116+
ConditionReasonDependencyCycle = "DependencyCycle"
117+
118+
// ConditionReasonToolNotFound indicates a referenced tool doesn't exist
119+
ConditionReasonToolNotFound = "ToolNotFound"
120+
121+
// ConditionReasonWorkflowReady indicates the workflow is ready to use
122+
ConditionReasonWorkflowReady = "WorkflowReady"
123+
124+
// ConditionReasonWorkflowNotReady indicates the workflow is not ready
125+
ConditionReasonWorkflowNotReady = "WorkflowNotReady"
126+
)
127+
128+
// CompositeToolDefinitionRef references a VirtualMCPCompositeToolDefinition resource
129+
type CompositeToolDefinitionRef struct {
130+
// Name is the name of the VirtualMCPCompositeToolDefinition resource in the same namespace
131+
// +kubebuilder:validation:Required
132+
Name string `json:"name"`
133+
}
134+
135+
// AdvancedWorkflowStep extends WorkflowStep with Phase 2 features
136+
// This is embedded in WorkflowStep for future expansion
137+
type AdvancedWorkflowStep struct {
138+
// RetryPolicy defines retry behavior for this step (Phase 2)
139+
// +optional
140+
RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"`
141+
142+
// Transform defines output transformation template (Phase 2)
143+
// Allows mapping step output to different structure
144+
// +optional
145+
Transform string `json:"transform,omitempty"`
146+
147+
// CacheKey defines a cache key template for result caching (Phase 2)
148+
// If specified and cache hit occurs, step is skipped
149+
// +optional
150+
CacheKey string `json:"cacheKey,omitempty"`
151+
}
152+
153+
// RetryPolicy defines retry behavior for workflow steps
154+
type RetryPolicy struct {
155+
// MaxRetries is the maximum number of retry attempts
156+
// +kubebuilder:validation:Minimum=1
157+
// +kubebuilder:validation:Maximum=10
158+
// +kubebuilder:default=3
159+
// +optional
160+
MaxRetries int `json:"maxRetries,omitempty"`
161+
162+
// BackoffStrategy defines the backoff strategy
163+
// - fixed: Fixed delay between retries
164+
// - exponential: Exponential backoff
165+
// +kubebuilder:validation:Enum=fixed;exponential
166+
// +kubebuilder:default=exponential
167+
// +optional
168+
BackoffStrategy string `json:"backoffStrategy,omitempty"`
169+
170+
// InitialDelay is the initial delay before first retry
171+
// +kubebuilder:default="1s"
172+
// +kubebuilder:validation:Pattern=`^([0-9]+(\.[0-9]+)?(ms|s|m))+$`
173+
// +optional
174+
InitialDelay string `json:"initialDelay,omitempty"`
175+
176+
// MaxDelay is the maximum delay between retries
177+
// +kubebuilder:default="30s"
178+
// +kubebuilder:validation:Pattern=`^([0-9]+(\.[0-9]+)?(ms|s|m))+$`
179+
// +optional
180+
MaxDelay string `json:"maxDelay,omitempty"`
181+
182+
// RetryableErrors defines which errors should trigger retry
183+
// If empty, all errors are retryable
184+
// Supports regex patterns
185+
// +optional
186+
RetryableErrors []string `json:"retryableErrors,omitempty"`
187+
}
188+
189+
// ElicitationStep defines user input elicitation (Phase 2)
190+
type ElicitationStep struct {
191+
// Message is the elicitation message to display to the user
192+
// Supports template expansion
193+
// +kubebuilder:validation:Required
194+
Message string `json:"message"`
195+
196+
// Schema defines the expected response schema
197+
// Uses JSON Schema format
198+
// +optional
199+
// +kubebuilder:pruning:PreserveUnknownFields
200+
// +kubebuilder:validation:Type=object
201+
Schema *runtime.RawExtension `json:"schema,omitempty"`
202+
203+
// Timeout is the maximum time to wait for user input
204+
// +kubebuilder:default="5m"
205+
// +kubebuilder:validation:Pattern=`^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$`
206+
// +optional
207+
Timeout string `json:"timeout,omitempty"`
208+
209+
// DefaultResponse is the default response if user doesn't respond in time
210+
// +optional
211+
// +kubebuilder:pruning:PreserveUnknownFields
212+
// +kubebuilder:validation:Type=object
213+
DefaultResponse *runtime.RawExtension `json:"defaultResponse,omitempty"`
214+
}
215+
216+
//+kubebuilder:object:root=true
217+
//+kubebuilder:subresource:status
218+
//+kubebuilder:resource:shortName=vmcpctd;compositetool
219+
//+kubebuilder:printcolumn:name="Workflow",type="string",JSONPath=".spec.name",description="Workflow name"
220+
//+kubebuilder:printcolumn:name="Steps",type="integer",JSONPath=".spec.steps[*]",description="Number of steps"
221+
//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.validationStatus",description="Validation status"
222+
//+kubebuilder:printcolumn:name="Refs",type="integer",JSONPath=".status.referencingVirtualServers[*]",description="Refs"
223+
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
224+
//+kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
225+
226+
// VirtualMCPCompositeToolDefinition is the Schema for the virtualmcpcompositetooldefinitions API
227+
// VirtualMCPCompositeToolDefinition defines reusable composite workflows that can be referenced
228+
// by multiple VirtualMCPServer instances
229+
type VirtualMCPCompositeToolDefinition struct {
230+
metav1.TypeMeta `json:",inline"` // nolint:revive
231+
metav1.ObjectMeta `json:"metadata,omitempty"`
232+
233+
Spec VirtualMCPCompositeToolDefinitionSpec `json:"spec,omitempty"`
234+
Status VirtualMCPCompositeToolDefinitionStatus `json:"status,omitempty"`
235+
}
236+
237+
//+kubebuilder:object:root=true
238+
239+
// VirtualMCPCompositeToolDefinitionList contains a list of VirtualMCPCompositeToolDefinition
240+
type VirtualMCPCompositeToolDefinitionList struct {
241+
metav1.TypeMeta `json:",inline"` // nolint:revive
242+
metav1.ListMeta `json:"metadata,omitempty"`
243+
Items []VirtualMCPCompositeToolDefinition `json:"items"`
244+
}
245+
246+
func init() {
247+
SchemeBuilder.Register(&VirtualMCPCompositeToolDefinition{}, &VirtualMCPCompositeToolDefinitionList{})
248+
}

0 commit comments

Comments
 (0)