Skip to content

Commit 2b36e40

Browse files
committed
Add MCP server sidecar support to OpenStackClient
Adds the ability to run the rhos-mcps MCP server as a sidecar container inside the openstackclient pod, exposed via a k8s Service. This allows the OpenStackAssistant (Goose) to execute read-only OpenStack CLI commands through the MCP protocol. OpenStackClient changes: - New MCPConfig struct (enabled, containerImage) on the CR spec - When enabled, adds an mcp-server sidecar container sharing the same clouds.yaml/secure.yaml credential mounts - Controller creates a ConfigMap with rhos-mcps config (openstack enabled, openshift disabled, allow_write: false) - Controller creates a Service on port 8080 for the MCP endpoint OpenStackAssistant changes: - New MCPServerRef (name, url) and mcpServers field on GooseConfig - Each MCP server is passed as MCP_SERVER_<name> env var to the pod - Entrypoint script generates Goose streamable_http extension entries from these env vars
1 parent be3a685 commit 2b36e40

24 files changed

Lines changed: 881 additions & 38 deletions

api/assistant/v1beta1/openstackassistant_types.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ type LightspeedStackSpec struct {
5252
CaBundleSecretName string `json:"caBundleSecretName,omitempty"`
5353
}
5454

55+
// MCPServerRef references an MCP server endpoint to configure as a Goose extension.
56+
// Either URL or OpenStackClientRef must be specified, but not both.
57+
type MCPServerRef struct {
58+
// Name is the extension name in Goose config
59+
// +kubebuilder:validation:Required
60+
Name string `json:"name"`
61+
62+
// URL is the MCP server's Streamable HTTP endpoint.
63+
// Mutually exclusive with OpenStackClientRef.
64+
// +kubebuilder:validation:Optional
65+
URL string `json:"url,omitempty"`
66+
67+
// OpenStackClientRef is the name of an OpenStackClient CR in the same
68+
// namespace that has MCP enabled. The controller auto-computes the
69+
// correct service URL and TLS CA configuration.
70+
// Mutually exclusive with URL.
71+
// +kubebuilder:validation:Optional
72+
OpenStackClientRef string `json:"openstackClientRef,omitempty"`
73+
}
74+
5575
// GooseConfig defines Goose-specific provider configuration
5676
type GooseConfig struct {
5777
// Model is the model identifier for the Goose AI agent
@@ -70,13 +90,17 @@ type GooseConfig struct {
7090
// will be written to ~/.goosehints in the pod.
7191
// +kubebuilder:validation:Optional
7292
Hints *string `json:"hints,omitempty"`
93+
94+
// MCPServers lists MCP server endpoints to configure as Goose extensions.
95+
// +kubebuilder:validation:Optional
96+
MCPServers []MCPServerRef `json:"mcpServers,omitempty"`
7397
}
7498

7599
// OpenStackAssistantSpec defines the desired state of OpenStackAssistant
76100
type OpenStackAssistantSpec struct {
77-
// ContainerImage for the assistant container.
78-
// +kubebuilder:validation:Required
79-
ContainerImage string `json:"containerImage"`
101+
// ContainerImage for the assistant container (will be set to environmental default if empty).
102+
// +kubebuilder:validation:Optional
103+
ContainerImage string `json:"containerImage,omitempty"`
80104

81105
// Provider is the AI agent provider type. Currently only "goose" is supported.
82106
// +kubebuilder:validation:Optional

api/assistant/v1beta1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/bases/assistant.openstack.org_openstackassistants.yaml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ spec:
5353
description: OpenStackAssistantSpec defines the desired state of OpenStackAssistant
5454
properties:
5555
containerImage:
56-
description: ContainerImage for the assistant container.
56+
description: ContainerImage for the assistant container (will be set
57+
to environmental default if empty).
5758
type: string
5859
env:
5960
description: Env is a list of additional environment variables for
@@ -188,6 +189,33 @@ spec:
188189
The ConfigMap must have a key "hints" with the content that
189190
will be written to ~/.goosehints in the pod.
190191
type: string
192+
mcpServers:
193+
description: MCPServers lists MCP server endpoints to configure
194+
as Goose extensions.
195+
items:
196+
description: |-
197+
MCPServerRef references an MCP server endpoint to configure as a Goose extension.
198+
Either URL or OpenStackClientRef must be specified, but not both.
199+
properties:
200+
name:
201+
description: Name is the extension name in Goose config
202+
type: string
203+
openstackClientRef:
204+
description: |-
205+
OpenStackClientRef is the name of an OpenStackClient CR in the same
206+
namespace that has MCP enabled. The controller auto-computes the
207+
correct service URL and TLS CA configuration.
208+
Mutually exclusive with URL.
209+
type: string
210+
url:
211+
description: |-
212+
URL is the MCP server's Streamable HTTP endpoint.
213+
Mutually exclusive with OpenStackClientRef.
214+
type: string
215+
required:
216+
- name
217+
type: object
218+
type: array
191219
model:
192220
description: |-
193221
Model is the model identifier for the Goose AI agent
@@ -232,7 +260,6 @@ spec:
232260
- goose
233261
type: string
234262
required:
235-
- containerImage
236263
- lightspeedStack
237264
type: object
238265
status:

api/bases/client.openstack.org_openstackclients.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,22 @@ spec:
180180
x-kubernetes-list-map-keys:
181181
- name
182182
x-kubernetes-list-type: map
183+
mcp:
184+
description: |-
185+
MCP is the optional MCP server sidecar configuration.
186+
When enabled, the rhos-mcps server runs alongside the openstackclient
187+
container and is exposed via a k8s Service.
188+
properties:
189+
enabled:
190+
default: false
191+
description: Enabled controls whether the MCP server sidecar is
192+
added to the pod.
193+
type: boolean
194+
type: object
195+
mcpContainerImage:
196+
description: MCPContainerImage for the MCP server sidecar container
197+
(set via OpenStackVersion)
198+
type: string
183199
nodeSelector:
184200
additionalProperties:
185201
type: string

api/bases/core.openstack.org_openstackcontrolplanes.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13621,6 +13621,12 @@ spec:
1362113621
x-kubernetes-list-map-keys:
1362213622
- name
1362313623
x-kubernetes-list-type: map
13624+
mcp:
13625+
properties:
13626+
enabled:
13627+
default: false
13628+
type: boolean
13629+
type: object
1362413630
nodeSelector:
1362513631
additionalProperties:
1362613632
type: string
@@ -19004,6 +19010,8 @@ spec:
1900419010
type: string
1900519011
openstackClientImage:
1900619012
type: string
19013+
openstackMcpImage:
19014+
type: string
1900719015
openstackNetworkExporterImage:
1900819016
type: string
1900919017
osContainerImage:

api/bases/core.openstack.org_openstackversions.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ spec:
199199
type: string
200200
openstackClientImage:
201201
type: string
202+
openstackMcpImage:
203+
type: string
202204
openstackNetworkExporterImage:
203205
type: string
204206
osContainerImage:
@@ -439,6 +441,8 @@ spec:
439441
type: string
440442
openstackClientImage:
441443
type: string
444+
openstackMcpImage:
445+
type: string
442446
openstackNetworkExporterImage:
443447
type: string
444448
osContainerImage:
@@ -643,6 +647,8 @@ spec:
643647
type: string
644648
openstackClientImage:
645649
type: string
650+
openstackMcpImage:
651+
type: string
646652
openstackNetworkExporterImage:
647653
type: string
648654
osContainerImage:
@@ -859,6 +865,8 @@ spec:
859865
type: string
860866
openstackClientImage:
861867
type: string
868+
openstackMcpImage:
869+
type: string
862870
openstackNetworkExporterImage:
863871
type: string
864872
osContainerImage:

api/client/v1beta1/openstackclient_types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ type OpenStackClientSpec struct {
3535
// +kubebuilder:validation:Required
3636
// ContainerImage for the OpenstackClient container (will be set to environmental default if empty)
3737
ContainerImage string `json:"containerImage"`
38+
39+
// +kubebuilder:validation:Optional
40+
// MCPContainerImage for the MCP server sidecar container (set via OpenStackVersion)
41+
MCPContainerImage string `json:"mcpContainerImage,omitempty"`
42+
}
43+
44+
// MCPConfig defines optional MCP server sidecar configuration
45+
type MCPConfig struct {
46+
// Enabled controls whether the MCP server sidecar is added to the pod.
47+
// +kubebuilder:validation:Optional
48+
// +kubebuilder:default=false
49+
Enabled bool `json:"enabled"`
3850
}
3951

4052
// OpenStackClientSpecCore defines the desired state of OpenStackClient
@@ -67,6 +79,12 @@ type OpenStackClientSpecCore struct {
6779
// +optional
6880
// List of environment variables to set in the container.
6981
Env []corev1.EnvVar `json:"env,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
82+
83+
// MCP is the optional MCP server sidecar configuration.
84+
// When enabled, the rhos-mcps server runs alongside the openstackclient
85+
// container and is exposed via a k8s Service.
86+
// +kubebuilder:validation:Optional
87+
MCP *MCPConfig `json:"mcp,omitempty"`
7088
}
7189

7290
// OpenStackClientStatus defines the observed state of OpenStackClient

api/client/v1beta1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/core/v1beta1/openstackversion_types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ type ContainerTemplate struct {
152152
OctaviaWorkerImage *string `json:"octaviaWorkerImage,omitempty"`
153153
OctaviaRsyslogImage *string `json:"octaviaRsyslogImage,omitempty"`
154154
OpenstackClientImage *string `json:"openstackClientImage,omitempty"`
155+
OpenstackMcpImage *string `json:"openstackMcpImage,omitempty"`
155156
OsContainerImage *string `json:"osContainerImage,omitempty"` //fixme wire this in?
156157
OvnControllerImage *string `json:"ovnControllerImage,omitempty"`
157158
OvnControllerOvsImage *string `json:"ovnControllerOvsImage,omitempty"`

api/core/v1beta1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)