Skip to content

Commit 523a279

Browse files
committed
Streamline ngrok tutorial and add auth addendum
Signed-off-by: Dan Barr <[email protected]>
1 parent c9b9099 commit 523a279

File tree

1 file changed

+57
-105
lines changed

1 file changed

+57
-105
lines changed

docs/toolhive/tutorials/k8s-ingress-ngrok.mdx

Lines changed: 57 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ mcp-mkp-proxy ClusterIP 10.96.106.88 <none> 8080/TCP 2m19s
134134

135135
Note 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

139139
Now, create a Gateway and HTTPRoute resource to expose the MCP server securely
140140
via ngrok.
@@ -146,7 +146,7 @@ account, it will be in the format `<RANDOM_NAME>.ngrok-free.app`. Replace
146146

147147
Create 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"
150150
apiVersion: gateway.networking.k8s.io/v1
151151
kind: Gateway
152152
metadata:
@@ -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"
166170
apiVersion: gateway.networking.k8s.io/v1
167171
kind: HTTPRoute
168172
metadata:
@@ -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
192196
kubectl 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:
204209
thv 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.
207213
The output should display a list of tools managed by the MCP server, confirming
208214
that you have successfully set up secure ingress using ngrok. For the "MKP" MCP
209215
server, 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:
243251
kubectl 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"
275259
apiVersion: gateway.networking.k8s.io/v1
276260
kind: HTTPRoute
277261
metadata:
278-
name: fetch-mcp-route
262+
name: mcp-servers-route
279263
namespace: toolhive-system
280264
spec:
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

384307
Apply 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

390313
You 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
408331
following:
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
412336
kubectl 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

Comments
 (0)