From bc06ef258a985e1796bd2d4990499fab7a84f99d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:53:55 +0000 Subject: [PATCH 1/3] Update stacklok/toolhive to v0.54.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/upstream-projects.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/upstream-projects.yaml b/.github/upstream-projects.yaml index a2008a5d..0f7140c0 100644 --- a/.github/upstream-projects.yaml +++ b/.github/upstream-projects.yaml @@ -64,7 +64,7 @@ projects: - id: toolhive repo: stacklok/toolhive - version: v0.53.0 + version: v0.54.0 # toolhive is a monorepo covering the CLI, the Kubernetes # operator, and the vMCP gateway. It also introduces cross- # cutting features that land in concepts/, integrations/, From f2369684317b51414a8084b74f42f0879ede025d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 15 Oct 2026 12:55:16 +0000 Subject: [PATCH 2/3] Refresh reference assets for toolhive v0.54.0 --- .../reference/cli/thv_client_register.md | 1 + .../reference/cli/thv_client_remove.md | 1 + .../toolhive/reference/helm/operator-crds.mdx | 6 ++-- docs/toolhive/reference/helm/operator.mdx | 18 +++++----- static/api-specs/toolhive-api.yaml | 19 +++++++--- .../mcpexternalauthconfigs.schema.json | 36 ++++++++++++++++++- .../virtualmcpservers.schema.json | 36 ++++++++++++++++++- 7 files changed, 98 insertions(+), 19 deletions(-) diff --git a/docs/toolhive/reference/cli/thv_client_register.md b/docs/toolhive/reference/cli/thv_client_register.md index 0cef577f..8870beeb 100644 --- a/docs/toolhive/reference/cli/thv_client_register.md +++ b/docs/toolhive/reference/cli/thv_client_register.md @@ -34,6 +34,7 @@ Valid clients: - lm-studio: LM Studio application - mistral-vibe: Mistral Vibe IDE - opencode: OpenCode editor + - pi: Pi coding agent - qoder: Qoder IDE - roo-code: VS Code Roo Code extension (deprecated) - trae: Trae IDE diff --git a/docs/toolhive/reference/cli/thv_client_remove.md b/docs/toolhive/reference/cli/thv_client_remove.md index 52ec371f..55a30a8b 100644 --- a/docs/toolhive/reference/cli/thv_client_remove.md +++ b/docs/toolhive/reference/cli/thv_client_remove.md @@ -34,6 +34,7 @@ Valid clients: - lm-studio: LM Studio application - mistral-vibe: Mistral Vibe IDE - opencode: OpenCode editor + - pi: Pi coding agent - qoder: Qoder IDE - roo-code: VS Code Roo Code extension (deprecated) - trae: Trae IDE diff --git a/docs/toolhive/reference/helm/operator-crds.mdx b/docs/toolhive/reference/helm/operator-crds.mdx index 4622f3fd..f4a8df8a 100644 --- a/docs/toolhive/reference/helm/operator-crds.mdx +++ b/docs/toolhive/reference/helm/operator-crds.mdx @@ -11,9 +11,9 @@ Control CRD installation and preservation with these Helm values. This reference lists values declared in the chart's default `values.yaml`. Templates can also accept optional settings and inherited `global` values. -Chart: `toolhive-operator-crds`. Chart version: `0.53.0`. Source: -[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.53.0/deploy/charts/operator-crds/values.yaml) -at `v0.53.0`. +Chart: `toolhive-operator-crds`. Chart version: `0.54.0`. Source: +[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.54.0/deploy/charts/operator-crds/values.yaml) +at `v0.54.0`. ## Values diff --git a/docs/toolhive/reference/helm/operator.mdx b/docs/toolhive/reference/helm/operator.mdx index 3886c22b..47245442 100644 --- a/docs/toolhive/reference/helm/operator.mdx +++ b/docs/toolhive/reference/helm/operator.mdx @@ -10,9 +10,9 @@ Configure the ToolHive operator with these Helm values. This reference lists values declared in the chart's default `values.yaml`. Templates can also accept optional settings and inherited `global` values. -Chart: `toolhive-operator`. Chart version: `0.53.0`. Source: -[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.53.0/deploy/charts/operator/values.yaml) -at `v0.53.0`. +Chart: `toolhive-operator`. Chart version: `0.54.0`. Source: +[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.54.0/deploy/charts/operator/values.yaml) +at `v0.54.0`. ## Values @@ -68,7 +68,7 @@ features: gc: gogc: 75 gomemlimit: 110MiB -image: ghcr.io/stacklok/toolhive/operator:v0.53.0 +image: ghcr.io/stacklok/toolhive/operator:v0.54.0 imageDiscovery: enabled: false resources: {} @@ -153,8 +153,8 @@ serviceAccount: labels: {} name: toolhive-operator tolerations: [] -toolhiveRunnerImage: ghcr.io/stacklok/toolhive/proxyrunner:v0.53.0 -vmcpImage: ghcr.io/stacklok/toolhive/vmcp:v0.53.0 +toolhiveRunnerImage: ghcr.io/stacklok/toolhive/proxyrunner:v0.54.0 +vmcpImage: ghcr.io/stacklok/toolhive/vmcp:v0.54.0 volumeMounts: [] volumes: [] ``` @@ -255,7 +255,7 @@ Key containing the password in `existingSecret`. When empty, uses **Type:** `string` -**Default:** `"ghcr.io/stacklok/toolhive/operator:v0.53.0"` +**Default:** `"ghcr.io/stacklok/toolhive/operator:v0.54.0"` Container image for the operator. @@ -271,7 +271,7 @@ Image pull policy for the operator container and image-discovery containers. **Type:** `string` -**Default:** `"ghcr.io/stacklok/toolhive/proxyrunner:v0.53.0"` +**Default:** `"ghcr.io/stacklok/toolhive/proxyrunner:v0.54.0"` Container image for ToolHive proxy runners created by the operator. @@ -279,7 +279,7 @@ Container image for ToolHive proxy runners created by the operator. **Type:** `string` -**Default:** `"ghcr.io/stacklok/toolhive/vmcp:v0.53.0"` +**Default:** `"ghcr.io/stacklok/toolhive/vmcp:v0.54.0"` Container image for Virtual MCP Server (vMCP) deployments created by the operator. diff --git a/static/api-specs/toolhive-api.yaml b/static/api-specs/toolhive-api.yaml index 3f28ec76..c48331f4 100644 --- a/static/api-specs/toolhive-api.yaml +++ b/static/api-specs/toolhive-api.yaml @@ -764,20 +764,22 @@ components: Profile selects how the endpoint's TLS connection is authenticated: SPIFFEBundleEndpointProfileHTTPSWeb (Web PKI) or SPIFFEBundleEndpointProfileHTTPSSPIFFE (a separately distributed - X.509-SVID root). Required, since the future bundle loader cannot - otherwise know which trust anchor to use for the initial connection. + X.509-SVID root). Required, since an endpoint loader needs this profile + to choose the trust anchor for its initial connection. type: string url: type: string type: object authserver.SPIFFEBundleSourceRunConfig: description: |- - BundleSource declares exactly one future trust-bundle source. It is - validated for shape only; fetching or loading a bundle from it is a - later step. + BundleSource declares exactly one trust-bundle source and is validated for + shape here. The standalone file loader exists, but source initialization + is not wired into SPIFFE authentication. properties: endpoint: $ref: '#/components/schemas/authserver.SPIFFEBundleEndpointSourceRunConfig' + file: + $ref: '#/components/schemas/authserver.SPIFFEFileBundleSourceRunConfig' type: type: string workload_api: @@ -844,6 +846,11 @@ components: this association policy. type: string type: object + authserver.SPIFFEFileBundleSourceRunConfig: + properties: + path: + type: string + type: object authserver.SPIFFETrustDomainRunConfig: properties: bundle_source: @@ -1268,6 +1275,7 @@ components: - factory - copilot-cli - qoder + - pi type: string x-enum-varnames: - RooCode @@ -1295,6 +1303,7 @@ components: - Factory - CopilotCli - Qoder + - Pi github_com_stacklok_toolhive_pkg_client.ClientAppStatus: properties: client_type: diff --git a/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json b/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json index bda8c513..7646c1b5 100644 --- a/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json +++ b/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json @@ -686,7 +686,7 @@ "description": "SPIFFETrustDomainConfig declares one SPIFFE trust domain accepted by the\nembedded authorization server. Configuration is not authentication: no\nlive X.509-SVID or JWT-SVID validation exists yet, so a declared trust\ndomain does not by itself let any workload authenticate — RunConfig.Validate\n(pkg/authserver/config.go) currently hard-rejects any non-empty\nspiffeTrustDomains at authserver startup via validateSPIFFENotYetEnforced,\na deliberate placeholder until real SVID verification lands.", "properties": { "bundleSource": { - "description": "BundleSource declares exactly one future trust-bundle source. It is\nvalidated for shape only; fetching or loading a bundle from it is a\nlater step.", + "description": "BundleSource declares exactly one trust-bundle source and is validated for\nshape here. The standalone file loader exists, but is not wired into SPIFFE\nauthentication.", "properties": { "endpoint": { "description": "Endpoint declares a HTTPS SPIFFE Bundle Endpoint. Required when Type is\n\"bundle_endpoint\".", @@ -712,10 +712,40 @@ ], "type": "object" }, + "file": { + "description": "File declares a SPIFFE trust bundle projected from a ConfigMap.\nRequired when Type is \"file\".", + "properties": { + "configMapKey": { + "description": "ConfigMapKey is the key within the ConfigMap holding the SPIFFE JWKS\ntrust-bundle document. It must not be \".\" or start with \"..\".", + "maxLength": 253, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "type": "string", + "x-kubernetes-validations": [ + { + "rule": "self != '.' && !self.startsWith('..')" + } + ] + }, + "configMapName": { + "description": "ConfigMapName is the name of the ConfigMap containing the trust bundle.", + "maxLength": 253, + "minLength": 1, + "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$", + "type": "string" + } + }, + "required": [ + "configMapKey", + "configMapName" + ], + "type": "object" + }, "type": { "description": "Type selects the trust-bundle source.", "enum": [ "bundle_endpoint", + "file", "workload_api" ], "type": "string" @@ -734,6 +764,10 @@ "message": "endpoint configuration must be set if and only if type is 'bundle_endpoint'", "rule": "self.type == 'bundle_endpoint' ? has(self.endpoint) : !has(self.endpoint)" }, + { + "message": "file configuration must be set if and only if type is 'file'", + "rule": "self.type == 'file' ? has(self.file) : !has(self.file)" + }, { "message": "workloadAPI configuration must be set if and only if type is 'workload_api'", "rule": "self.type == 'workload_api' ? has(self.workloadAPI) : !has(self.workloadAPI)" diff --git a/static/api-specs/toolhive-crds/virtualmcpservers.schema.json b/static/api-specs/toolhive-crds/virtualmcpservers.schema.json index e0ed3d98..986d1dc1 100644 --- a/static/api-specs/toolhive-crds/virtualmcpservers.schema.json +++ b/static/api-specs/toolhive-crds/virtualmcpservers.schema.json @@ -577,7 +577,7 @@ "description": "SPIFFETrustDomainConfig declares one SPIFFE trust domain accepted by the\nembedded authorization server. Configuration is not authentication: no\nlive X.509-SVID or JWT-SVID validation exists yet, so a declared trust\ndomain does not by itself let any workload authenticate — RunConfig.Validate\n(pkg/authserver/config.go) currently hard-rejects any non-empty\nspiffeTrustDomains at authserver startup via validateSPIFFENotYetEnforced,\na deliberate placeholder until real SVID verification lands.", "properties": { "bundleSource": { - "description": "BundleSource declares exactly one future trust-bundle source. It is\nvalidated for shape only; fetching or loading a bundle from it is a\nlater step.", + "description": "BundleSource declares exactly one trust-bundle source and is validated for\nshape here. The standalone file loader exists, but is not wired into SPIFFE\nauthentication.", "properties": { "endpoint": { "description": "Endpoint declares a HTTPS SPIFFE Bundle Endpoint. Required when Type is\n\"bundle_endpoint\".", @@ -603,10 +603,40 @@ ], "type": "object" }, + "file": { + "description": "File declares a SPIFFE trust bundle projected from a ConfigMap.\nRequired when Type is \"file\".", + "properties": { + "configMapKey": { + "description": "ConfigMapKey is the key within the ConfigMap holding the SPIFFE JWKS\ntrust-bundle document. It must not be \".\" or start with \"..\".", + "maxLength": 253, + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$", + "type": "string", + "x-kubernetes-validations": [ + { + "rule": "self != '.' && !self.startsWith('..')" + } + ] + }, + "configMapName": { + "description": "ConfigMapName is the name of the ConfigMap containing the trust bundle.", + "maxLength": 253, + "minLength": 1, + "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$", + "type": "string" + } + }, + "required": [ + "configMapKey", + "configMapName" + ], + "type": "object" + }, "type": { "description": "Type selects the trust-bundle source.", "enum": [ "bundle_endpoint", + "file", "workload_api" ], "type": "string" @@ -625,6 +655,10 @@ "message": "endpoint configuration must be set if and only if type is 'bundle_endpoint'", "rule": "self.type == 'bundle_endpoint' ? has(self.endpoint) : !has(self.endpoint)" }, + { + "message": "file configuration must be set if and only if type is 'file'", + "rule": "self.type == 'file' ? has(self.file) : !has(self.file)" + }, { "message": "workloadAPI configuration must be set if and only if type is 'workload_api'", "rule": "self.type == 'workload_api' ? has(self.workloadAPI) : !has(self.workloadAPI)" From 9c15a6ec6303e69065fdd651a1fdaf2795c99cd1 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 15 Oct 2026 13:00:21 +0000 Subject: [PATCH 3/3] Apply prettier and eslint fixups to skill output --- .../concepts/embedded-auth-server.mdx | 9 ++++++ .../guides-vmcp/delegate-agent-identity.mdx | 10 ++++++ .../reference/client-compatibility.mdx | 32 +++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/docs/toolhive/concepts/embedded-auth-server.mdx b/docs/toolhive/concepts/embedded-auth-server.mdx index a9dafedf..c1c0409e 100644 --- a/docs/toolhive/concepts/embedded-auth-server.mdx +++ b/docs/toolhive/concepts/embedded-auth-server.mdx @@ -203,6 +203,15 @@ the stored upstream access token has expired, ToolHive automatically refreshes it using the stored refresh token before forwarding the request. Your MCP session continues without re-authentication. +ToolHive also refreshes when the stored upstream ID token has expired, even if +the access token is still valid. This keeps the ID token current for providers +like Microsoft Entra ID, which issue ID tokens that expire before the access +token, and for features that use the upstream ID token, such as token exchange. +If the provider omits a new ID token from the refresh response, ToolHive keeps +the existing one and doesn't retry until the access token next expires. A failed +ID token refresh doesn't interrupt the session, because the access token is +still valid. + If the refresh token is also expired or has been revoked by the upstream provider, ToolHive returns a `401` response, prompting re-authentication through the OAuth flow. diff --git a/docs/toolhive/guides-vmcp/delegate-agent-identity.mdx b/docs/toolhive/guides-vmcp/delegate-agent-identity.mdx index 7e22571e..373a9112 100644 --- a/docs/toolhive/guides-vmcp/delegate-agent-identity.mdx +++ b/docs/toolhive/guides-vmcp/delegate-agent-identity.mdx @@ -271,6 +271,16 @@ claim, works exactly as in the pre-provisioned case above; the issued token's `act.sub` is the DCR-assigned `client_id` instead of a statically configured one. +ToolHive rejects registrations whose keys fall outside these limits: + +- RSA keys must have a modulus between 2048 and 8192 bits and an odd public + exponent between 3 and 65537. +- The inline `jwks` can contain at most 10 keys. + +ToolHive also checks stored clients against these limits, so a client whose +registered key falls outside them fails authentication with `invalid_client` and +must register again. + ## Next steps - [Accept workload assertions with the JWT-bearer grant](./accept-workload-assertions.mdx) diff --git a/docs/toolhive/reference/client-compatibility.mdx b/docs/toolhive/reference/client-compatibility.mdx index a92debe0..9a928fe8 100644 --- a/docs/toolhive/reference/client-compatibility.mdx +++ b/docs/toolhive/reference/client-compatibility.mdx @@ -31,6 +31,7 @@ We've tested ToolHive with these clients: | Mistral Vibe | ✅ | ✅ | ✅ | | | OpenAI Codex | ✅ | ✅ | ✅ | | | OpenCode | ✅ | ✅ | ✅ | | +| Pi | ✅ | ✅ | ✅ | v0.99.0+ ([see note][7]) | | Qoder IDE | ✅ | ✅ | ✅ | | | Roo Code (VS Code) | ✅ | ✅ | ✅ | Deprecated ([see note][6]) | | Sourcegraph Amp CLI | ✅ | ✅ | ✅ | | @@ -48,6 +49,7 @@ We've tested ToolHive with these clients: [4]: #stdio-only-client-configuration [5]: #chatgpt-desktop-configuration [6]: #roo-code-and-cline +[7]: #pi The minimum versions listed are the earliest versions that support the Streamable HTTP transport protocol. @@ -316,6 +318,36 @@ Example configuration: When you register the Copilot CLI as a client, ToolHive automatically updates this file whenever you run an MCP server. +### Pi + +The [Pi coding agent](https://pi.dev/) stores its global MCP configuration in a +JSON file in your home directory. + +- **All platforms**: `~/.pi/agent/mcp.json` + +Example configuration: + +```json +{ + "mcpServers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" } + } +} +``` + +Pi supports the Streamable HTTP transport only. ToolHive adds SSE servers to the +file without a `type` field, but Pi treats them as Streamable HTTP and can't +connect to them. + +If you've installed a Pi extension that provides its own MCP support, such as +`pi-mcp-adapter`, Pi ignores `mcp.json`, including the servers ToolHive +configures. Remove the extension to use Pi's built-in MCP support. + +When you register Pi as a client, ToolHive automatically updates this file +whenever you run an MCP server. ToolHive installs skills to `~/.pi/agent/skills` +for user scope and `.pi/skills` for project scope. + ## Manual configuration If your client doesn't support automatic configuration, you'll need to set up