@@ -134,7 +134,7 @@ mcp-mkp-proxy ClusterIP 10.96.106.88 <none> 8080/TCP 2m19s
134134
135135Note the service name and port number for the next step.
136136
137- ## Step 3: Create an ngrok Gateway resource
137+ ## Step 3: Create Gateway and HTTPRoute resources
138138
139139Now, create a Gateway and HTTPRoute resource to expose the MCP server securely
140140via ngrok.
@@ -146,7 +146,7 @@ account, it will be in the format `<RANDOM_NAME>.ngrok-free.app`. Replace
146146
147147Create a file named ` ngrok-mcp-gateway.yaml ` with the following content:
148148
149- ``` yaml {12,29,35-37 } title="ngrok-mcp-gateway.yaml"
149+ ``` yaml {12} title="ngrok-mcp-gateway.yaml"
150150apiVersion : gateway.networking.k8s.io/v1
151151kind : Gateway
152152metadata :
@@ -162,7 +162,11 @@ spec:
162162 allowedRoutes :
163163 namespaces :
164164 from : All
165- ---
165+ ` ` `
166+
167+ Then create a file named ` ngrok-mcp-httproute.yaml` with the following content:
168+
169+ ` ` ` yaml {13,20-22} title="ngrok-mcp-httproute.yaml"
166170apiVersion: gateway.networking.k8s.io/v1
167171kind: HTTPRoute
168172metadata:
@@ -186,10 +190,11 @@ spec:
186190 port: 8080 # Replace with your port number from Step 2
187191` ` `
188192
189- Apply the configuration to your cluster:
193+ Apply the configurations to your cluster :
190194
191195` ` ` bash
192196kubectl apply -f ngrok-mcp-gateway.yaml
197+ kubectl apply -f ngrok-mcp-httproute.yaml
193198` ` `
194199
195200# # Step 4: Access the MCP server
@@ -204,6 +209,7 @@ Use the ToolHive CLI to verify connectivity to the MCP server:
204209thv mcp list tools --server https://<YOUR_NGROK_DOMAIN>/mcp
205210` ` `
206211
212+ The `/mcp` path is the default endpoint for the MCP Streamable HTTP transport.
207213The output should display a list of tools managed by the MCP server, confirming
208214that you have successfully set up secure ingress using ngrok. For the "MKP" MCP
209215server, you should see output similar to this :
@@ -226,9 +232,11 @@ The MKP MCP server is now available to AI clients configured with
226232
227233# # Optional: Tunnel multiple MCP servers with URL rewrites
228234
229- If you have multiple MCP servers and want to expose them via the same ngrok
230- Gateway, you can use URL rewrites in the ` HTTPRoute ` resource. This allows you
231- to route requests to different MCP servers based on path prefixes.
235+ The previous steps exposed a single MCP server at the root path (`/`). If you
236+ have multiple MCP servers and want to expose them via the same ngrok Gateway,
237+ you can use path-based routing with URL rewrites in the HTTPRoute resource. This
238+ allows you to route requests to different MCP servers based on path prefixes
239+ like `/mkp` and `/fetch`.
232240
233241:::note
234242
@@ -243,39 +251,15 @@ Run a second MCP server, for example:
243251kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/heads/main/examples/operator/mcp-servers/mcpserver_fetch.yaml
244252` ` `
245253
246- Then, update the ` ngrok-mcp-gateway .yaml ` file. Update the ` rules ` section of
247- the existing ` HTTPRoute ` resource to give the MKP MCP server a path prefix of
254+ Then, update the `ngrok-mcp-httproute .yaml` file. Update the `rules` section of
255+ the existing HTTPRoute resource to give the MKP MCP server a path prefix of
248256` /mkp` and add a `URLRewrite` filter.
249257
250- ``` yaml {8,12-17} title="ngrok-mcp-gateway.yaml"
251- # ... existing HTTPRoute resource ...
252- spec :
253- # ... existing spec ...
254- rules :
255- - matches :
256- - path :
257- type : PathPrefix
258- value : /mkp
259- backendRefs :
260- - name : mcp-mkp-proxy
261- port : 8080
262- filters :
263- - type : URLRewrite
264- urlRewrite :
265- path :
266- type : ReplacePrefixMatch
267- replacePrefixMatch : ' '
268- ` ` `
269-
270- Then, add a new rule for the Fetch MCP server with a path prefix of ` /fetch`,
271- again replacing `<YOUR_NGROK_DOMAIN>` with your actual domain :
272-
273- ` ` ` yaml {14,19,21-22} title="ngrok-mcp-gateway.yaml"
274- ---
258+ ` ` ` yaml {18,22-27} showLineNumbers title="ngrok-mcp-httproute.yaml"
275259apiVersion: gateway.networking.k8s.io/v1
276260kind: HTTPRoute
277261metadata:
278- name: fetch- mcp-route
262+ name: mcp-servers -route
279263 namespace: toolhive-system
280264spec:
281265 parentRefs:
@@ -289,82 +273,22 @@ spec:
289273 - matches:
290274 - path:
291275 type: PathPrefix
292- value: /fetch
276+ value: /mkp
293277 backendRefs:
294- - name: mcp-fetch -proxy
278+ - name: mcp-mkp -proxy
295279 port: 8080
296280 filters:
297281 - type: URLRewrite
298282 urlRewrite:
299283 path:
300284 type: ReplacePrefixMatch
301- replacePrefixMatch: ''
285+ replacePrefixMatch: '' # Normally this would be '/' but ngrok requires empty string
302286` ` `
303287
304- At this point, your `ngrok-mcp-gateway.yaml` file should contain one `Gateway`
305- resource and two `HTTPRoute` resources.
288+ Add another rule for the Fetch MCP server with a path prefix of `/fetch` :
306289
307- <details>
308- <summary>Full example of updated <code>ngrok-mcp-gateway.yaml</code></summary>
309- ` ` ` yaml
310- apiVersion: gateway.networking.k8s.io/v1
311- kind: Gateway
312- metadata:
313- name: ngrok-gateway
314- namespace: toolhive-system
315- spec:
316- gatewayClassName: ngrok
317- listeners:
318- - name: https
319- protocol: HTTPS
320- port: 443
321- hostname: <YOUR_NGROK_DOMAIN>
322- allowedRoutes:
323- namespaces:
324- from: All
325- ---
326- apiVersion: gateway.networking.k8s.io/v1
327- kind: HTTPRoute
328- metadata:
329- name: mkp-mcp-route
330- namespace: toolhive-system
331- spec:
332- parentRefs:
333- - group: gateway.networking.k8s.io
334- kind: Gateway
335- name: ngrok-gateway
336- namespace: toolhive-system
337- hostnames:
338- - <YOUR_NGROK_DOMAIN>
339- rules:
340- - matches:
341- - path:
342- type: PathPrefix
343- value: /mkp
344- backendRefs:
345- - name: mcp-mkp-proxy
346- port: 8080
347- filters:
348- - type: URLRewrite
349- urlRewrite:
350- path:
351- type: ReplacePrefixMatch
352- replacePrefixMatch: ""
353- ---
354- apiVersion: gateway.networking.k8s.io/v1
355- kind: HTTPRoute
356- metadata:
357- name: fetch-mcp-route
358- namespace: toolhive-system
359- spec:
360- parentRefs:
361- - group: gateway.networking.k8s.io
362- kind: Gateway
363- name: ngrok-gateway
364- namespace: toolhive-system
365- hostnames:
366- - <YOUR_NGROK_DOMAIN>
367- rules:
290+ {/* prettier-ignore */}
291+ ` ` ` yaml showLineNumbers=28 title="ngrok-mcp-httproute.yaml"
368292 - matches:
369293 - path:
370294 type: PathPrefix
@@ -377,14 +301,13 @@ spec:
377301 urlRewrite:
378302 path:
379303 type: ReplacePrefixMatch
380- replacePrefixMatch: ""
304+ replacePrefixMatch: ''
381305` ` `
382- </details>
383306
384307Apply the updated configuration to your cluster :
385308
386309` ` ` bash
387- kubectl apply -f ngrok-mcp-gateway .yaml
310+ kubectl apply -f ngrok-mcp-httproute .yaml
388311` ` `
389312
390313You can now access both MCP servers using the same ngrok domain with different
@@ -408,7 +331,8 @@ To remove the ngrok resources from your cluster and ngrok account, run the
408331following :
409332
410333` ` ` bash
411- # Delete the Gateway and HTTPRoute resources
334+ # Delete the HTTPRoute and Gateway resources
335+ kubectl delete -f ngrok-mcp-httproute.yaml
412336kubectl delete -f ngrok-mcp-gateway.yaml
413337
414338# Delete the ngrok CRDs
@@ -430,3 +354,31 @@ next steps:
430354 server usage and performance.
431355- Try other gateway solutions like Traefik or Istio if they're already part of
432356 your infrastructure.
357+
358+ # # Addendum: Combining with MCP server authentication
359+
360+ When exposing MCP servers via ngrok or any other ingress solution, consider the
361+ security implications. While ngrok provides secure HTTPS tunnels, you should
362+ also implement authentication and authorization to control access. The ToolHive
363+ Operator supports
364+ [OAuth-based authentication methods](../guides-k8s/auth-k8s.mdx) that are out of
365+ scope for this tutorial but essential for production deployments.
366+
367+ When OAuth is enabled on an MCP server, add additional rules to the HTTPRoute
368+ resource to expose the OAuth metadata endpoint for proper authentication flow
369+ through the gateway.
370+
371+ Here's an example rule to add to the `mcp-servers-route` HTTPRoute in your
372+ ` ngrok-mcp-httproute.yaml` file. Add this rule alongside the existing `/mkp`
373+ path rule :
374+
375+ {/* prettier-ignore */}
376+ ` ` ` yaml title="ngrok-mcp-httproute.yaml"
377+ - matches:
378+ - path:
379+ type: Exact
380+ value: /.well-known/oauth-protected-resource/mkp
381+ backendRefs:
382+ - name: mcp-mkp-proxy
383+ port: 8080
384+ ` ` `
0 commit comments