Skip to content

Commit ee8c869

Browse files
authored
feat: Implement MCP (Model Context Protocol) resource and controller with RBAC roles (#12)
* feat: Implement MCP (Model Context Protocol) resource and controller with RBAC roles Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat: Add MCP server configuration with namespace and specifications Would be cool to just have a side-car container that ensures those STDIO MCP servers can also support SSE or any streamable HTTP. Some MCPs supports only STDIO. which makes it complex to connect to in a containerized environment. Meaning you would normally have to create a custom bridge (web server) that sends a command to the internal process - not ideal, but it-is what it-is. Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: Update MCP image reference to use the correct repository I'm not going to host those servers registry, I think it would be easier to just use the existing ones. Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: Ensure consistent TLS secret name in MCP ingress configuration Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat: Enhance MCP configuration with server, bridge, ingress, and TLS specifications Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat: Add Horizontal Pod Autoscaler (HPA) configuration for MCP server - Introduced HPA configuration under the MCP server spec, allowing for dynamic scaling based on defined metrics. - Added properties for scaling behavior, including scaleUp and scaleDown policies, stabilization windows, and metrics specifications. - Updated image and replicas fields to provide defaults and descriptions for better clarity. - Removed deprecated ingress configuration and replaced it with HPA-specific settings to streamline deployment management. Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: WIP - update import statements and logger usage in MCP controller I think I should stick to standard MCP using node life time support server, and if the user wants they can configure their own container image, this allows more flexibility. Depending on what's configured option is used the package will take precedence over the image. Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: Start with implementing the official way of running MCP servers, then will switch to custom MCPs Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: Remove unused ingress configuration from MCP server YAML I think ingress doesn't make a lot of sense in the context of MCP, normally those are server you would like to run internally. I don't think there is a use-case to expose it to the outside world. Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: Remove unnecessary comment in shouldWatchNamespace function Signed-off-by: Eden Reich <eden.reich@gmail.com> * test: Add basic tests Signed-off-by: Eden Reich <eden.reich@gmail.com> * fix: Remove unnecessary variable declaration in MCP controller test Signed-off-by: Eden Reich <eden.reich@gmail.com> * refactor: Simplify fields Building a generic bridge for all Stdio servers is too complex. I'll just create a registry for MCP servers written in golang so people can pull them. Will add the essential ones like filesystem context7. Just need to build those containers and push them to registry so it's possible to pull them and run them as a streamable http that supports also https. I think the default npx -y with the modelcontext protocol lake of production settings. Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat: Enhance MCP Controller Tests and CRD Definitions - Refactored MCP controller tests to use DescribeTable for better organization and readability. - Added additional test cases for various reconciliation scenarios, including handling of HPA and service reconciliation. - Updated CRD definitions to include additional printer columns for MCP resources, displaying the URL of the MCP server and the resource age. - Modified the schema to support an array type for the command field in the MCP spec. - Added a new field in the status to store the URL of the MCP server. Signed-off-by: Eden Reich <eden.reich@gmail.com> * chore: Update test coverage badge to reflect current coverage Signed-off-by: Eden Reich <eden.reich@gmail.com> --------- Signed-off-by: Eden Reich <eden.reich@gmail.com>
1 parent 0481b4f commit ee8c869

21 files changed

Lines changed: 4577 additions & 28 deletions

PROJECT

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,35 @@
44
# More info: https://book.kubebuilder.io/reference/project-config.html
55
domain: inference-gateway.com
66
layout:
7-
- go.kubebuilder.io/v4
7+
- go.kubebuilder.io/v4
88
projectName: operator
99
repo: github.com/inference-gateway/operator
1010
resources:
11-
- api:
12-
crdVersion: v1
13-
namespaced: true
14-
controller: true
15-
domain: inference-gateway.com
16-
group: core
17-
kind: Gateway
18-
path: github.com/inference-gateway/operator/api/v1alpha1
19-
version: v1alpha1
20-
- api:
21-
crdVersion: v1
22-
namespaced: true
23-
controller: true
24-
domain: inference-gateway.com
25-
group: core
26-
kind: A2A
27-
path: github.com/inference-gateway/operator/api/v1alpha1
28-
version: v1alpha1
11+
- api:
12+
crdVersion: v1
13+
namespaced: true
14+
controller: true
15+
domain: inference-gateway.com
16+
group: core
17+
kind: Gateway
18+
path: github.com/inference-gateway/operator/api/v1alpha1
19+
version: v1alpha1
20+
- api:
21+
crdVersion: v1
22+
namespaced: true
23+
controller: true
24+
domain: inference-gateway.com
25+
group: core
26+
kind: A2A
27+
path: github.com/inference-gateway/operator/api/v1alpha1
28+
version: v1alpha1
29+
- api:
30+
crdVersion: v1
31+
namespaced: true
32+
controller: true
33+
domain: inference-gateway.com
34+
group: core
35+
kind: MCP
36+
path: github.com/inference-gateway/operator/api/v1alpha1
37+
version: v1alpha1
2938
version: "3"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
[![Container Registry](https://img.shields.io/badge/Container-ghcr.io-blue.svg?style=flat-square&logo=github)](https://github.com/inference-gateway/operator/pkgs/container/operator)
1818
[![Multi-Arch](https://img.shields.io/badge/Architecture-amd64%20%7C%20arm64-green?style=flat-square)](https://github.com/inference-gateway/operator/releases)
1919
[![Build Status](https://img.shields.io/badge/Build-Passing-brightgreen.svg?style=flat-square)](https://github.com/inference-gateway/operator)
20-
[![Tests](https://img.shields.io/badge/Tests-65.1%25%20Coverage-yellow.svg?style=flat-square)](https://github.com/inference-gateway/operator)
20+
[![Tests](https://img.shields.io/badge/Tests-66%25%20Coverage-yellow.svg?style=flat-square)](https://github.com/inference-gateway/operator)
2121
[![Lint](https://img.shields.io/badge/Lint-Passing-brightgreen.svg?style=flat-square)](https://golangci-lint.run/)
2222

2323
---

api/v1alpha1/gateway_types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ type GatewaySpec struct {
6767

6868
// MCP (Model Context Protocol) configuration
6969
// +optional
70-
MCP *MCPSpec `json:"mcp,omitempty"`
70+
MCP *MCPServersSpec `json:"mcp,omitempty"`
7171

7272
// A2A (Agent-to-Agent) configuration
7373
// +optional
@@ -272,8 +272,8 @@ type ProviderSpec struct {
272272
Env *[]corev1.EnvVar `json:"env,omitempty"`
273273
}
274274

275-
// MCPSpec contains Model Context Protocol configuration
276-
type MCPSpec struct {
275+
// MCPServersSpec contains Model Context Protocol configuration
276+
type MCPServersSpec struct {
277277
// Enable MCP integration
278278
// +optional
279279
// +kubebuilder:default=false

api/v1alpha1/mcp_types.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Copyright (c) 2025 Inference Gateway
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
SOFTWARE.
21+
*/
22+
23+
package v1alpha1
24+
25+
import (
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
)
28+
29+
// MCPSpec defines the desired state of MCP.
30+
type MCPSpec struct {
31+
// Replicas is the number of replicas for the MCP server.
32+
// +kubebuilder:default=1
33+
// +optional
34+
Replicas *int32 `json:"replicas,omitempty"`
35+
36+
// Image is the container image to use for the MCP server.
37+
// +optional
38+
// +kubebuilder:default="node:lts"
39+
Image string `json:"image,omitempty"`
40+
41+
// Server defines the configuration for the MCP server.
42+
// +optional
43+
Server *MCPServerSpec `json:"server,omitempty"`
44+
45+
// HPA defines the Horizontal Pod Autoscaler configuration for the MCP server.
46+
// +optional
47+
HPA *HPASpec `json:"hpa,omitempty"`
48+
}
49+
50+
type MCPBridgeSpec struct {
51+
// Create indicates whether to create a bridge sidecar container.
52+
// +kubebuilder:default=true
53+
// +optional
54+
Create bool `json:"create,omitempty"`
55+
56+
// Port is the port on which the bridge listens.
57+
// +kubebuilder:default=8081
58+
// +optional
59+
Port int32 `json:"port,omitempty"`
60+
}
61+
62+
type MCPServerSpec struct {
63+
// Port is the port on which the MCP server listens.
64+
// +kubebuilder:default=8080
65+
// +optional
66+
Port int32 `json:"port,omitempty"`
67+
68+
// Command is the command to run the MCP server.
69+
// If not specified, the default command will be used.
70+
// +optional
71+
Command []string `json:"command,omitempty"`
72+
73+
// Args are the arguments to pass to the MCP server command.
74+
// If not specified, the default arguments will be used.
75+
// +optional
76+
Args []string `json:"args,omitempty"`
77+
78+
// Timeout is the timeout for the MCP server.
79+
// +kubebuilder:default="30s"
80+
// +optional
81+
Timeout string `json:"timeout,omitempty"`
82+
83+
// TLS defines the TLS configuration for the MCP server.
84+
// +optional
85+
TLS *MCPTLSConfig `json:"tls,omitempty"`
86+
}
87+
88+
type MCPTLSConfig struct {
89+
// +optional
90+
// +kubebuilder:default=true
91+
// Enabled indicates whether TLS is enabled for the MCP server.
92+
Enabled bool `json:"enabled,omitempty"`
93+
94+
// SecretName is the name of the secret that contains the TLS certificate and key.
95+
// +kubebuilder:validation:Required
96+
SecretName string `json:"secretName"`
97+
}
98+
99+
// MCPStatus defines the observed state of MCP.
100+
type MCPStatus struct {
101+
// ObservedGeneration is the most recent generation observed for this resource.
102+
// +optional
103+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
104+
105+
// Conditions represent the latest available observations of the resource's state.
106+
// +optional
107+
// +kubebuilder:validation:Enum=Pending;Running;Failed;Unknown
108+
Conditions []metav1.Condition `json:"conditions,omitempty"`
109+
110+
// Ready indicates if the resource is ready.
111+
// +optional
112+
Ready bool `json:"ready,omitempty"`
113+
114+
// URL is the URL of the MCP server.
115+
// +optional
116+
URL string `json:"url,omitempty"`
117+
}
118+
119+
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=".status.url",description="URL of the MCP server"
120+
// +kubebuilder:printcolumn:name="AGE",type=date,JSONPath=".metadata.creationTimestamp",description="Age of the resource"
121+
// +kubebuilder:object:root=true
122+
// +kubebuilder:subresource:status
123+
124+
// MCP is the Schema for the mcps API.
125+
type MCP struct {
126+
metav1.TypeMeta `json:",inline"`
127+
metav1.ObjectMeta `json:"metadata,omitempty"`
128+
129+
Spec MCPSpec `json:"spec,omitempty"`
130+
Status MCPStatus `json:"status,omitempty"`
131+
}
132+
133+
// +kubebuilder:object:root=true
134+
135+
// MCPList contains a list of MCP.
136+
type MCPList struct {
137+
metav1.TypeMeta `json:",inline"`
138+
metav1.ListMeta `json:"metadata,omitempty"`
139+
Items []MCP `json:"items"`
140+
}
141+
142+
func init() {
143+
SchemeBuilder.Register(&MCP{}, &MCPList{})
144+
}

0 commit comments

Comments
 (0)