Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 57 additions & 9 deletions docs/toolhive/guides-cli/build-containers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ For example:
# Build a Python MCP server using uvx
thv build uvx://mcp-server-git

# Build a Node.js MCP server using npx
thv build npx://@modelcontextprotocol/server-filesystem
# Build a Node.js MCP server using npx with a pinned version
thv build npx://@upstash/context7-mcp@1.0.26

# Build a Go MCP server
thv build go://github.com/example/my-mcp-server@latest
Expand Down Expand Up @@ -73,7 +73,7 @@ This is particularly useful for:
### Tagging examples

<Tabs groupId='tagging' queryString='tagging'>
<TabItem value='registry' label='Container Registry' default>
<TabItem value='registry' label='Container registry' default>

Build and tag for pushing to a container registry:

Expand All @@ -96,14 +96,23 @@ Build images with predictable names for Kubernetes manifests:
```bash
# Build with a consistent tag
thv build --tag mcp-servers/git-server:stable uvx://mcp-server-git
```

Use the built image in your Kubernetes manifests:

# Use in Kubernetes manifest
# spec:
# image: mcp-servers/git-server:stable
```yaml
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPServer
metadata:
name: git-server
namespace: production
spec:
image: mcp-servers/git-server:stable
# ... other spec fields ...
```

</TabItem>
<TabItem value='versioning' label='Version Management'>
<TabItem value='versioning' label='Version management'>

Build multiple versions of the same server:

Expand Down Expand Up @@ -158,6 +167,45 @@ thv build --tag grafana-mcp:latest go://github.com/grafana/mcp-grafana/cmd/mcp-g
thv build --tag my-local-server:dev go://./cmd/my-mcp-server
```

## Build-time arguments

Some MCP servers require specific subcommands or arguments that must always be
present. You can bake these required arguments directly into the container image
at build time using the `--` separator at the end of the `thv build` command:

```bash
thv build <PROTOCOL_SCHEME> -- <BUILD_ARGS>
```

:::info[Build-time vs runtime arguments]

- **Build-time arguments**: Baked into the container image and always present.
These are typically required subcommands or essential configuration flags.
- **Runtime arguments**: Passed when running the container and appended after
build-time arguments. These are typically optional flags or dynamic
configuration.

:::

Build-time arguments are embedded in the container's ENTRYPOINT and always
execute before any runtime arguments. For example, the LaunchDarkly MCP server
requires a `start` subcommand:

```bash
# Bake "start" subcommand into container
thv build --tag launchdarkly-mcp:latest npx://@launchdarkly/mcp-server -- start

# Runtime args still append after baked-in args
thv run launchdarkly-mcp:latest -- --verbose
# Executes: npx @launchdarkly/mcp-server start --verbose
```

You can include multiple build-time arguments as needed:

```bash
thv build uvx://my-package -- --transport stdio --log-level info
```

## Dockerfile generation

Use the `--dry-run` flag to generate the Dockerfile without building the image:
Expand Down Expand Up @@ -196,7 +244,7 @@ ENTRYPOINT ["uv", "tool", "run", "mcp-server-git"]
## Kubernetes workflows

The `thv build` command is especially useful for Kubernetes deployments where
you want to pre-build containers before deploying them.
you need to pre-build containers before deploying them.

### Pre-build workflow

Expand Down Expand Up @@ -265,7 +313,7 @@ jobs:

For more advanced CI/CD patterns including multi-architecture builds, supply
chain security, and change detection, see the
[Advanced CI/CD with ToolHive](./advanced-cicd.mdx) guide.
[Advanced CI/CD patterns](./advanced-cicd.mdx) guide.

## Advanced usage

Expand Down
52 changes: 50 additions & 2 deletions docs/toolhive/tutorials/k8s-ingress-ngrok.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ spec:
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: ""
replacePrefixMatch: ''
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
Expand Down Expand Up @@ -369,7 +369,7 @@ spec:
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: ""
replacePrefixMatch: ''
```
</details>

Expand Down Expand Up @@ -422,3 +422,51 @@ next steps:
server usage and performance.
- Try other gateway solutions like Traefik or Istio if they're already part of
your infrastructure.

## Addendum: Combining with MCP server authentication

When exposing MCP servers via ngrok or any other ingress solution, consider the
security implications. While ngrok provides secure HTTPS tunnels, you should
also implement authentication and authorization to control access. The ToolHive
Operator supports
[OAuth-based authentication methods](../guides-k8s/auth-k8s.mdx) that are out of
scope for this tutorial but essential for production deployments.

When OAuth is enabled on an MCP server, add an `HTTPRoute` resource to expose
the OAuth metadata endpoint for proper authentication flow through the gateway.

Here's an example `HTTPRoute` for the MKP MCP server:

```yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mkp-wellknown-insertion-route
namespace: toolhive-system
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: ngrok-gateway
namespace: toolhive-system
hostnames:
- <YOUR_NGROK_DOMAIN>
rules:
- matches:
- path:
type: Exact
value: /.well-known/oauth-protected-resource/mkp
backendRefs:
- name: mcp-mkp-proxy
port: 8080
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplaceFullPath
replaceFullPath: /.well-known/oauth-protected-resource
```

Replace the hostname and backend references as appropriate for your MCP server
configuration.