From dd83ddf28f8e5753783b51761de825ca9d5f2706 Mon Sep 17 00:00:00 2001 From: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:14:15 +0100 Subject: [PATCH 1/4] adds docs for using different secrets managers in k8s Signed-off-by: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> --- .../{run-mcp-k8s.md => run-mcp-k8s.mdx} | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) rename docs/toolhive/guides-k8s/{run-mcp-k8s.md => run-mcp-k8s.mdx} (90%) diff --git a/docs/toolhive/guides-k8s/run-mcp-k8s.md b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx similarity index 90% rename from docs/toolhive/guides-k8s/run-mcp-k8s.md rename to docs/toolhive/guides-k8s/run-mcp-k8s.mdx index d1f5362e..1111ecf1 100644 --- a/docs/toolhive/guides-k8s/run-mcp-k8s.md +++ b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -253,10 +253,14 @@ process. ### Run a server with secrets -For MCP servers that require authentication tokens or other secrets, add the -`secrets` field to the `MCPServer` resource. This example shows how to use a -Kubernetes secret to pass a GitHub personal access token to the `github` MCP -server. +For MCP servers that require authentication tokens or other secrets, you can use +secrets from multiple secrets managers: + + + + +This example shows how to use an existing Kubernetes secret to pass a GitHub +personal access token to the `github` MCP server. ```yaml {13-16} title="my-mcpserver-with-secrets.yaml" apiVersion: toolhive.stacklok.dev/v1alpha1 @@ -291,6 +295,62 @@ Apply the MCPServer resource: kubectl apply -f my-mcpserver-with-secrets.yaml ``` + + + +This example shows how to use an existing Kubernetes secret created by the +[External Secrets Operator](https://external-secrets.io/) to pass a GitHub +personal access token to the `github` MCP server. + +:::info[Important] + +Given the External Secrets Operator creates standard Kubernetes secrets based on +external secrets, the MCP server definition will look the same as the Kubernetes +example. + +::: + +```yaml {13-16} title="my-mcpserver-with-secrets-eso.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: production # Can be any namespace +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + port: 8080 + permissionProfile: + type: builtin + name: network + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +First, create the secret by using +[External Secrets Operator](https://external-secrets.io/latest/api/externalsecret). +Note that the secret must be created in the same namespace as the MCP server and +the key must match the one specified in the `MCPServer` resource. + +Apply the MCPServer resource: + +```bash +kubectl apply -f my-mcpserver-with-secrets-eso.yaml +``` + + + + +This example shows how to use [Vault](https://developer.hashicorp.com/vault) to +inject secrets into the ToolHive containers for consumption. + +Chris to flesh out with Jakub + + + + ### Mount a volume You can mount volumes into the MCP server pod to provide persistent storage or From 31d423ddc7cdcd9e979662c557999c667ac3ce96 Mon Sep 17 00:00:00 2001 From: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> Date: Tue, 9 Sep 2025 19:01:41 +0100 Subject: [PATCH 2/4] adds vault secret injection docs Signed-off-by: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> --- docs/toolhive/guides-k8s/run-mcp-k8s.mdx | 61 +++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx index 1111ecf1..2c659919 100644 --- a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx +++ b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -346,7 +346,66 @@ kubectl apply -f my-mcpserver-with-secrets-eso.yaml This example shows how to use [Vault](https://developer.hashicorp.com/vault) to inject secrets into the ToolHive containers for consumption. -Chris to flesh out with Jakub +Injecting secrets using Vault is done with its agent sidecar container, but +before you can start injecting secrets into the container there are some steps +to do before hand. This includes setting up Vault to be able to authenticate and +pull the necessary secrets. We will not detail here how to do this as there are +some very helpful +[Vault guides](https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-sidecar) +on setting this up, but before we can inject secrets into the ToolHive +containers we need to have the following: + +- Vault available +- Vault configured for Kubernetes authentication +- Vault policy created to be able to read the desired secrets +- Vault role created that is bound to the ToolHive ProxyRunner Service Account + in order to enable authentication + +```yaml {23-33} title="my-mcpserver-with-vault-secrets-injection.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-vault-generic + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + port: 9095 + permissionProfile: + type: builtin + name: network + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' + resourceOverrides: + proxyDeployment: + podTemplateMetadataOverrides: + annotations: + # Enable Vault Agent injection + vault.hashicorp.com/agent-inject: 'true' + vault.hashicorp.com/role: '' + + # Inject GitHub configuration secret + vault.hashicorp.com/agent-inject-secret-github-config: 'workload-secrets/data/github-mcp/config' + vault.hashicorp.com/agent-inject-template-github-config: | + {{- with secret "workload-secrets/data/github-mcp/config" -}} + GITHUB_PERSONAL_ACCESS_TOKEN={{ .Data.data.token }} + {{- end -}} +``` + +Apply the MCPServer resource: + +```bash +kubectl apply -f my-mcpserver-with-vault-secrets-injection.yaml +``` + +The Vault agent sidecar will now inject secrets from the +`workload-secrets/data/github-mcp/config` inside of Vault, into the ProxyRunner +container. From e02f6a3948c1c104e77f610dddedfa87578ffc6d Mon Sep 17 00:00:00 2001 From: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> Date: Tue, 9 Sep 2025 22:11:07 +0100 Subject: [PATCH 3/4] corrects name of k8s docs Signed-off-by: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> --- docs/toolhive/guides-cli/advanced-cicd.mdx | 2 +- docs/toolhive/guides-cli/build-containers.mdx | 4 ++-- docs/toolhive/guides-k8s/deploy-operator-helm.md | 6 +++--- docs/toolhive/guides-k8s/intro.md | 2 +- docs/toolhive/guides-mcp/filesystem.mdx | 2 +- docs/toolhive/tutorials/quickstart-k8s.mdx | 5 +++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/toolhive/guides-cli/advanced-cicd.mdx b/docs/toolhive/guides-cli/advanced-cicd.mdx index 8113bd5e..36d619f7 100644 --- a/docs/toolhive/guides-cli/advanced-cicd.mdx +++ b/docs/toolhive/guides-cli/advanced-cicd.mdx @@ -313,6 +313,6 @@ When implementing advanced CI/CD patterns: ## Related information - [Build MCP server containers](./build-containers.mdx) -- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.md) +- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.mdx) - [`thv build` command reference](../reference/cli/thv_build.md) - [Secrets management](./secrets-management.mdx) diff --git a/docs/toolhive/guides-cli/build-containers.mdx b/docs/toolhive/guides-cli/build-containers.mdx index 89e06eb8..a94ba231 100644 --- a/docs/toolhive/guides-cli/build-containers.mdx +++ b/docs/toolhive/guides-cli/build-containers.mdx @@ -316,7 +316,7 @@ thv build --tag my-server:dev go:///path/to/my-project - Use built containers with [`thv run`](./run-mcp-servers.mdx) for local development -- Deploy pre-built containers to [Kubernetes](../guides-k8s/run-mcp-k8s.md) +- Deploy pre-built containers to [Kubernetes](../guides-k8s/run-mcp-k8s.mdx) - Set up [CI/CD pipelines](#cicd-integration) for automated building - Learn about [container registry workflows](#custom-image-tagging) @@ -324,7 +324,7 @@ thv build --tag my-server:dev go:///path/to/my-project - [`thv build` command reference](../reference/cli/thv_build.md) - [Run MCP servers](./run-mcp-servers.mdx) -- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.md) +- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.mdx) - [Custom permissions](./custom-permissions.mdx) ## Troubleshooting diff --git a/docs/toolhive/guides-k8s/deploy-operator-helm.md b/docs/toolhive/guides-k8s/deploy-operator-helm.md index 6c0ef88f..084b0578 100644 --- a/docs/toolhive/guides-k8s/deploy-operator-helm.md +++ b/docs/toolhive/guides-k8s/deploy-operator-helm.md @@ -251,9 +251,9 @@ create it. ## Next steps -See [Run MCP servers in Kubernetes](./run-mcp-k8s.md) to learn how to create and -manage MCP servers using the ToolHive operator in your Kubernetes cluster. The -operator supports deploying MCPServer resources based on the deployment mode +See [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) to learn how to create +and manage MCP servers using the ToolHive operator in your Kubernetes cluster. +The operator supports deploying MCPServer resources based on the deployment mode configured during installation. ## Related information diff --git a/docs/toolhive/guides-k8s/intro.md b/docs/toolhive/guides-k8s/intro.md index 50e593bc..2bcb6d37 100644 --- a/docs/toolhive/guides-k8s/intro.md +++ b/docs/toolhive/guides-k8s/intro.md @@ -57,4 +57,4 @@ Kubernetes cluster. Helm simplifies the installation process and lets you manage the operator using Helm charts. Once the operator is installed, you can create and manage MCP servers using the -[`MCPServer` custom resource](./run-mcp-k8s.md). +[`MCPServer` custom resource](./run-mcp-k8s.mdx). diff --git a/docs/toolhive/guides-mcp/filesystem.mdx b/docs/toolhive/guides-mcp/filesystem.mdx index 06949859..ebe89ba6 100644 --- a/docs/toolhive/guides-mcp/filesystem.mdx +++ b/docs/toolhive/guides-mcp/filesystem.mdx @@ -109,7 +109,7 @@ outbound network access (see the Create a Kubernetes manifest to deploy the Filesystem MCP server with a -[persistent volume](../guides-k8s/run-mcp-k8s.md#mount-a-volume). +[persistent volume](../guides-k8s/run-mcp-k8s.mdx#mount-a-volume). Update the `podTemplateSpec` section to include your specific volume claim and mount path: diff --git a/docs/toolhive/tutorials/quickstart-k8s.mdx b/docs/toolhive/tutorials/quickstart-k8s.mdx index 86304040..405e30bb 100644 --- a/docs/toolhive/tutorials/quickstart-k8s.mdx +++ b/docs/toolhive/tutorials/quickstart-k8s.mdx @@ -432,8 +432,9 @@ operator. Here are some next steps to explore: -- Learn about [advanced MCP server configurations](../guides-k8s/run-mcp-k8s.md) - for production deployments +- Learn about + [advanced MCP server configurations](../guides-k8s/run-mcp-k8s.mdx) for + production deployments - Learn more about [Helm deployment options](../guides-k8s/deploy-operator-helm.md) and configuration From f9e694e8fb6a00315e9017b3ab1b633846fc6d93 Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:26:06 -0400 Subject: [PATCH 4/4] Update wording, rewrite the Vault section Signed-off-by: Dan Barr <6922515+danbarr@users.noreply.github.com> --- docs/toolhive/guides-k8s/run-mcp-k8s.mdx | 148 +++++++++-------------- 1 file changed, 56 insertions(+), 92 deletions(-) diff --git a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx index 2c659919..f274977b 100644 --- a/docs/toolhive/guides-k8s/run-mcp-k8s.mdx +++ b/docs/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -253,14 +253,25 @@ process. ### Run a server with secrets -For MCP servers that require authentication tokens or other secrets, you can use -secrets from multiple secrets managers: +When your MCP servers require authentication tokens or other secrets, ToolHive +supports multiple secrets management methods to fit your existing +infrastructure. Choose the method that best suits your needs: - + -This example shows how to use an existing Kubernetes secret to pass a GitHub -personal access token to the `github` MCP server. +ToolHive can reference existing Kubernetes secrets to inject sensitive data into +your MCP server pods as environment variables. This example demonstrates how to +pass a GitHub personal access token to the `github` MCP server. + +First, create the secret. The secret must exist in the same namespace as your +MCP server and the key must match what you specify in the `MCPServer` resource. + +```bash +kubectl -n production create secret generic github-token --from-literal=token= +``` + +Next, define the `MCPServer` resource to reference the secret: ```yaml {13-16} title="my-mcpserver-with-secrets.yaml" apiVersion: toolhive.stacklok.dev/v1alpha1 @@ -281,15 +292,7 @@ spec: targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN ``` -First, create the secret. Note that the secret must be created in the same -namespace as the MCP server and the key must match the one specified in the -`MCPServer` resource. - -```bash -kubectl -n production create secret generic github-token --from-literal=token= -``` - -Apply the MCPServer resource: +Finally, apply the MCPServer resource: ```bash kubectl apply -f my-mcpserver-with-secrets.yaml @@ -298,18 +301,27 @@ kubectl apply -f my-mcpserver-with-secrets.yaml -This example shows how to use an existing Kubernetes secret created by the -[External Secrets Operator](https://external-secrets.io/) to pass a GitHub -personal access token to the `github` MCP server. +[External Secrets Operator](https://external-secrets.io/) is a Kubernetes +operator that integrates external secret management systems and syncs secrets +into Kubernetes as native resources. This example demonstrates how to use +ESO-managed secrets with your MCP server. -:::info[Important] +:::note -Given the External Secrets Operator creates standard Kubernetes secrets based on -external secrets, the MCP server definition will look the same as the Kubernetes -example. +When you use the External Secrets Operator, your MCP server definition will look +the same as the Kubernetes-native example. This is because the External Secrets +Operator creates standard Kubernetes secrets from external sources. ::: +First, create a secret using the +[ExternalSecret resource](https://external-secrets.io/latest/api/externalsecret/). +The exact configuration depends on your external secret management system. The +secret must exist in the same namespace as your MCP server and the key must +match what you specify in the `MCPServer` resource. + +Next, define the `MCPServer` resource to reference the secret: + ```yaml {13-16} title="my-mcpserver-with-secrets-eso.yaml" apiVersion: toolhive.stacklok.dev/v1alpha1 kind: MCPServer @@ -329,83 +341,35 @@ spec: targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN ``` -First, create the secret by using -[External Secrets Operator](https://external-secrets.io/latest/api/externalsecret). -Note that the secret must be created in the same namespace as the MCP server and -the key must match the one specified in the `MCPServer` resource. - -Apply the MCPServer resource: +Finally, apply the MCPServer resource: ```bash kubectl apply -f my-mcpserver-with-secrets-eso.yaml ``` - - -This example shows how to use [Vault](https://developer.hashicorp.com/vault) to -inject secrets into the ToolHive containers for consumption. - -Injecting secrets using Vault is done with its agent sidecar container, but -before you can start injecting secrets into the container there are some steps -to do before hand. This includes setting up Vault to be able to authenticate and -pull the necessary secrets. We will not detail here how to do this as there are -some very helpful -[Vault guides](https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-sidecar) -on setting this up, but before we can inject secrets into the ToolHive -containers we need to have the following: - -- Vault available -- Vault configured for Kubernetes authentication -- Vault policy created to be able to read the desired secrets -- Vault role created that is bound to the ToolHive ProxyRunner Service Account - in order to enable authentication - -```yaml {23-33} title="my-mcpserver-with-vault-secrets-injection.yaml" -apiVersion: toolhive.stacklok.dev/v1alpha1 -kind: MCPServer -metadata: - name: github-vault-generic - namespace: toolhive-system -spec: - image: ghcr.io/github/github-mcp-server:latest - transport: stdio - port: 9095 - permissionProfile: - type: builtin - name: network - resources: - limits: - cpu: '100m' - memory: '128Mi' - requests: - cpu: '50m' - memory: '64Mi' - resourceOverrides: - proxyDeployment: - podTemplateMetadataOverrides: - annotations: - # Enable Vault Agent injection - vault.hashicorp.com/agent-inject: 'true' - vault.hashicorp.com/role: '' - - # Inject GitHub configuration secret - vault.hashicorp.com/agent-inject-secret-github-config: 'workload-secrets/data/github-mcp/config' - vault.hashicorp.com/agent-inject-template-github-config: | - {{- with secret "workload-secrets/data/github-mcp/config" -}} - GITHUB_PERSONAL_ACCESS_TOKEN={{ .Data.data.token }} - {{- end -}} -``` - -Apply the MCPServer resource: - -```bash -kubectl apply -f my-mcpserver-with-vault-secrets-injection.yaml -``` - -The Vault agent sidecar will now inject secrets from the -`workload-secrets/data/github-mcp/config` inside of Vault, into the ProxyRunner -container. + + +HashiCorp Vault provides multiple integration methods for Kubernetes +environments: + +1. [Vault Sidecar Agent Injector](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/injector), + which injects a sidecar container into your pod to fetch and renew secrets +2. [Vault Secrets Operator](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/vso), + which creates Kubernetes secrets from Vault secrets (similar to the External + Secrets Operator) +3. [Vault CSI Provider](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/csi), + which mounts secrets directly into your pod as files + +ToolHive supports the first two methods. When you use the Vault Secrets +Operator, your MCP server definition will look the same as the Kubernetes-native +example because the Vault Secrets Operator creates standard Kubernetes secrets +from Vault. + +The Vault Sidecar Agent Injector requires additional configuration in your +`MCPServer` resource to add the required annotations. For a complete example, +see the +[HashiCorp Vault integration tutorial](../tutorials/vault-integration.mdx).