@@ -12,11 +12,100 @@ sidebar_position: 20
1212- The ToolHive operator installed in your cluster (see
1313 [ Deploy the operator using Helm] ( ./deploy-operator-helm.md ) )
1414
15+ ## Overview
16+
17+ The ToolHive operator deploys MCP servers in Kubernetes by creating proxy pods
18+ that manage the actual MCP server containers. Here's how the architecture works:
19+
20+ ### High-level architecture
21+
22+ This diagram shows the basic relationship between components. The ToolHive
23+ operator watches for ` MCPServer ` resources and automatically creates the
24+ necessary infrastructure to run your MCP servers securely within the cluster.
25+
26+ ``` mermaid
27+ flowchart LR
28+ Client["Client"] --> Proxy["ToolHive Proxy"]
29+ Proxy --> MCP["MCP Server"]
30+
31+ subgraph K8s["Kubernetes Cluster"]
32+ subgraph NS["Namespace"]
33+ Operator["ToolHive Operator"]
34+ Proxy
35+ MCP
36+ end
37+ end
38+
39+ Operator -.->|creates| Proxy
40+ Proxy -.->|creates| MCP
41+ ```
42+
43+ ### STDIO transport flow
44+
45+ For MCP servers using STDIO transport, the proxy directly attaches to the MCP
46+ server pod's standard input/output streams.
47+
48+ ``` mermaid
49+ flowchart LR
50+ subgraph Proxy["Created by Operator"]
51+ direction TB
52+ ProxyService["SVC: ToolHive-Proxy"]
53+ ProxyPod["POD: ToolHive-Proxy"]
54+ ProxyService --> ProxyPod
55+ end
56+
57+ subgraph MCP["Created by Proxy"]
58+ direction TB
59+ MCPPod["POD: MCP Server"]
60+ end
61+
62+ Client["Client"] -->|HTTP/SSE| Proxy
63+ Proxy -->|Attaches/STDIO| MCP
64+ ```
65+
66+ ### SSE transport flow
67+
68+ For MCP servers using Server-Sent Events (SSE) transport, the proxy creates both
69+ a pod and a headless service. This allows direct HTTP/SSE communication between
70+ the proxy and MCP server while maintaining network isolation and service
71+ discovery.
72+
73+ ``` mermaid
74+ flowchart LR
75+ subgraph Proxy["Created by Operator"]
76+ direction TB
77+ ProxyService["SVC: ToolHive-Proxy"]
78+ ProxyPod["POD: ToolHive-Proxy"]
79+ ProxyService --> ProxyPod
80+ end
81+
82+ subgraph MCP["Created by Proxy"]
83+ direction TB
84+ MCPService["SVC: MCP-Headless"]
85+ MCPPod["POD: MCP Server"]
86+ end
87+
88+ Client["Client"] -->|HTTP/SSE| Proxy
89+ Proxy -->|HTTP/SSE| MCP
90+ MCPService --> MCPPod
91+ ```
92+
1593## Create an MCP Server
1694
95+ ::: note
96+
97+ Currently, you must create ` MCPServer ` resources in the same namespace as the
98+ ToolHive operator, which is typically ` toolhive-system ` . We are working on
99+ support for multi-namespace deployments in the future. Watch
100+ [ this issue] ( https://github.com/stacklok/toolhive/issues/538 ) for updates.
101+
102+ :::
103+
17104To create an MCP server, define an ` MCPServer ` resource and apply it to your
18- cluster. This example creates the
19- [ ` osv ` MCP server] ( https://github.com/StacklokLabs/osv-mcp ) .
105+ cluster. This minimal example creates the
106+ [ ` osv ` MCP server] ( https://github.com/StacklokLabs/osv-mcp ) which queries the
107+ [ Open Source Vulnerability (OSV) database] ( https://osv.dev/ ) for vulnerability
108+ information.
20109
21110``` yaml title="my-mcpserver.yaml"
22111apiVersion : toolhive.stacklok.dev/v1alpha1
@@ -46,7 +135,102 @@ Apply the resource:
46135kubectl apply -f my-mcpserver.yaml
47136```
48137
49- ## Run a server with secrets
138+ :::info What's happening?
139+
140+ When you apply an ` MCPServer ` resource, here's what happens:
141+
142+ 1 . The ToolHive operator running in the ` toolhive-system ` namespace detects the
143+ new resource
144+ 2 . The operator creates a new Deployment containing a ToolHive proxy pod and
145+ service to handle client connections
146+ 3 . The proxy creates the actual ` MCPServer ` pod containing your specified
147+ container image
148+ 4 . For STDIO transport, the proxy attaches directly to the pod; for SSE
149+ transport, a headless service is created for direct pod communication
150+ 5 . Clients can now connect through the service → proxy → MCP server chain to use
151+ the tools and resources (note: external clients will need an ingress
152+ controller or similar mechanism to access the service from outside the
153+ cluster)
154+
155+ :::
156+
157+ For more examples of ` MCPServer ` resources, see the
158+ [ example MCP server manifests] ( https://github.com/stacklok/toolhive/tree/main/examples/operator/mcp-servers )
159+ in the ToolHive repo.
160+
161+ ## Customize server settings
162+
163+ You can customize the MCP server by adding additional fields to the ` MCPServer `
164+ resource. Below are some common configurations.
165+
166+ ### Customize the MCP server pod
167+
168+ You can customize the MCP server pod that gets created by the proxy using the
169+ ` podTemplateSpec ` field. This gives you full control over the pod specification,
170+ allowing you to set security contexts, resource limits, node selectors, and
171+ other pod-level configurations.
172+
173+ The ` podTemplateSpec ` field follows the standard Kubernetes
174+ [ ` PodTemplateSpec ` ] ( https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-template-v1/#PodTemplateSpec )
175+ format, so you can use any valid pod specification options.
176+
177+ This example sets security contexts and resource limits. It allows the MCP
178+ container to run as root, an unfortunate requirement for the Fetch MCP server
179+ image, while still applying some security restrictions.
180+
181+ ``` yaml title="my-mcpserver-custom-pod.yaml"
182+ apiVersion : toolhive.stacklok.dev/v1alpha1
183+ kind : MCPServer
184+ metadata :
185+ name : fetch
186+ namespace : toolhive-system
187+ spec :
188+ image : docker.io/mcp/fetch
189+ transport : stdio
190+ port : 8080
191+ permissionProfile :
192+ type : builtin
193+ name : network
194+ podTemplateSpec :
195+ spec :
196+ containers :
197+ - name : mcp # This name must be "mcp"
198+ securityContext :
199+ allowPrivilegeEscalation : false
200+ runAsNonRoot : false # Allows the MCP container to run as root
201+ runAsUser : 0
202+ capabilities :
203+ drop :
204+ - ALL
205+ resources : # These resources apply to the MCP container
206+ limits :
207+ cpu : ' 500m'
208+ memory : ' 512Mi'
209+ requests :
210+ cpu : ' 100m'
211+ memory : ' 128Mi'
212+ securityContext :
213+ runAsNonRoot : true # The pod itself can run as a non-root user
214+ seccompProfile :
215+ type : RuntimeDefault
216+ resources : # These resources apply to the proxy container
217+ limits :
218+ cpu : ' 100m'
219+ memory : ' 128Mi'
220+ requests :
221+ cpu : ' 50m'
222+ memory : ' 64Mi'
223+ ` ` `
224+
225+ :::info Container name requirement
226+
227+ When customizing containers in ` podTemplateSpec`, you must use `name: mcp` for
228+ the main container. This ensures the proxy can properly manage the MCP server
229+ process.
230+
231+ :: :
232+
233+ # ## Run a server with secrets
50234
51235For MCP servers that require authentication tokens or other secrets, add the
52236` secrets` field to the `MCPServer` resource. This example shows how to use a
@@ -86,7 +270,7 @@ Apply the MCPServer resource:
86270kubectl apply -f my-mcpserver-with-secrets.yaml
87271` ` `
88272
89- # # Mount a volume
273+ # ## Mount a volume
90274
91275:::warning TODO
92276
@@ -102,7 +286,7 @@ To check the status of your MCP servers:
102286kubectl -n toolhive-system get mcpservers
103287` ` `
104288
105- This will show the status, URL, and age of each MCP server.
289+ The status, URL, and age of each MCP server is displayed .
106290
107291For more details about a specific MCP server :
108292
@@ -114,7 +298,8 @@ kubectl -n toolhive-system describe mcpserver <NAME>
114298
115299:::tip QUESTION
116300
117- TODO : Move to a /reference page?
301+ TODO : Move to a /reference page? Also could use a way to keep this in sync
302+ automatically.
118303
119304:: :
120305
@@ -132,16 +317,6 @@ TODO: Move to a /reference page?
132317| `secrets` | References to secrets to mount in the container | No | - |
133318| `permissionProfile` | Permission profile configuration | No | - |
134319
135- # ## Volumes
136-
137- The `volumes` field has the following parameters :
138-
139- :::warning TODO
140-
141- Need info!
142-
143- :: :
144-
145320# ## Secrets
146321
147322The `secrets` field has the following parameters :
@@ -152,6 +327,16 @@ The `secrets` field has the following parameters:
152327 secret in the MCP server (optional). If left unspecified, it defaults to the
153328 key.
154329
330+ # ## Volumes
331+
332+ The `volumes` field has the following parameters :
333+
334+ :::warning TODO
335+
336+ Need info!
337+
338+ :: :
339+
155340# ## Permission Profiles
156341
157342Permission profiles can be configured in two ways :
@@ -175,8 +360,7 @@ Permission profiles can be configured in two ways:
175360
176361The ConfigMap should contain a JSON permission profile.
177362
178- # # Examples
363+ # # Next steps
179364
180- See the
181- [example MCP server manifests](https://github.com/stacklok/toolhive/tree/main/examples/operator/mcp-servers)
182- in the ToolHive repo for example MCPServer resources.
365+ See the [Client compatibility](../reference/client-compatibility.mdx) reference
366+ to learn how to connect to MCP servers using different clients.
0 commit comments