From d0a006f051803d399f32392ca8b32428ced9536b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:02:52 +0000 Subject: [PATCH 1/3] Update stacklok/toolhive to v0.60.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 48eab3ec..eaea19bf 100644 --- a/.github/upstream-projects.yaml +++ b/.github/upstream-projects.yaml @@ -64,7 +64,7 @@ projects: - id: toolhive repo: stacklok/toolhive - version: v0.59.0 + version: v0.60.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 6b2fc2352e08aab3a7ada64cb83b17320bfec194 Mon Sep 17 00:00:00 2001 From: "stacklok-octosts[bot]" <327033170+stacklok-octosts[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 23:59:24 +0000 Subject: [PATCH 2/3] Refresh reference assets for toolhive v0.60.0 --- .../reference/cli/thv_llm_config_set.md | 1 + docs/toolhive/reference/cli/thv_llm_setup.md | 1 + .../toolhive/reference/helm/operator-crds.mdx | 6 +- docs/toolhive/reference/helm/operator.mdx | 18 ++-- static/api-specs/toolhive-api.yaml | 89 +++++++++++++++++-- .../mcpexternalauthconfigs.schema.json | 6 +- .../virtualmcpservers.schema.json | 6 +- 7 files changed, 103 insertions(+), 24 deletions(-) diff --git a/docs/toolhive/reference/cli/thv_llm_config_set.md b/docs/toolhive/reference/cli/thv_llm_config_set.md index 8fba1571..1d629a09 100644 --- a/docs/toolhive/reference/cli/thv_llm_config_set.md +++ b/docs/toolhive/reference/cli/thv_llm_config_set.md @@ -40,6 +40,7 @@ thv llm config set [flags] --issuer string OIDC issuer URL --models strings Model IDs to persist and apply during "thv llm setup", comma-separated or by repeating the flag, e.g. --models=us.anthropic.claude-opus-4-8,us.anthropic.claude-sonnet-5. Credential-helper clients (Claude Desktop) write these as inferenceModels; with Bedrock compat, each ID overrides a Claude Code tier by matching 'haiku', 'opus', 'sonnet', or 'fable' in the ID. --proxy-port int Localhost proxy listen port (omit to keep current; default: 14000) + --scopes strings OIDC scopes to request, comma-separated or by repeating the flag. offline_access is always requested; pass openid explicitly if the provider needs it (the default scopes are openid and offline_access). Omit to keep the current scopes; an empty value restores the defaults. Changing the list clears cached tokens so the next sign-in requests the new scopes --short-prompt-cache Use Claude Code's five-minute prompt cache instead of ToolHive's one-hour default. Applied by "thv llm setup"; use --short-prompt-cache=false to restore one-hour caching. --tls-skip-verify Skip TLS certificate verification for the upstream gateway (local dev only; use --tls-skip-verify=false to clear) ``` diff --git a/docs/toolhive/reference/cli/thv_llm_setup.md b/docs/toolhive/reference/cli/thv_llm_setup.md index 99fb9bb4..ac10357d 100644 --- a/docs/toolhive/reference/cli/thv_llm_setup.md +++ b/docs/toolhive/reference/cli/thv_llm_setup.md @@ -98,6 +98,7 @@ thv llm setup [flags] --lazy Skip the interactive OIDC login and defer it until the first time a configured tool accesses the gateway. Tool config and persisted settings are written normally. Useful for unattended provisioning (e.g. an MDM profile). --models strings Model IDs to configure, comma-separated or by repeating the flag, e.g. --models=us.anthropic.claude-opus-4-8,us.anthropic.claude-sonnet-5. For Pi these filter its discovered models (Pi does not support --lazy). For credential-helper clients (Claude Desktop) these become inferenceModels. With --bedrock-compat, each ID overrides a Claude Code tier by matching 'haiku', 'opus', 'sonnet', or 'fable' in the ID (IDs matching no tier are ignored with a warning). Omit to pin each tier to the newest Bedrock model discovered from the gateway. --proxy-port int Localhost proxy listen port (omit to keep current; default: 14000) + --scopes strings OIDC scopes to request, comma-separated or by repeating the flag. offline_access is always requested; pass openid explicitly if the provider needs it (the default scopes are openid and offline_access). Omit to keep the current scopes; an empty value restores the defaults. Changing the list clears cached tokens so the next sign-in requests the new scopes --short-prompt-cache Use Claude Code's five-minute prompt cache instead of ToolHive's one-hour default. Persisted, so a later plain "thv llm setup" keeps it; use --short-prompt-cache=false to restore one-hour caching. --skip-browser Print the OIDC authorization URL instead of opening a browser, then wait for the callback. Use in headless/SSH/CI environments where no system browser is available. --tls-skip-verify Skip TLS certificate verification for the upstream gateway (local dev only). For direct-mode tools (Claude Code, Gemini CLI) this sets NODE_TLS_REJECT_UNAUTHORIZED=0, disabling TLS for ALL of that tool's outbound connections. For proxy-mode tools only the proxy-to-gateway connection is affected. diff --git a/docs/toolhive/reference/helm/operator-crds.mdx b/docs/toolhive/reference/helm/operator-crds.mdx index 7309e1e0..5eb606b5 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.59.0`. Source: -[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.59.0/deploy/charts/operator-crds/values.yaml) -at `v0.59.0`. +Chart: `toolhive-operator-crds`. Chart version: `0.60.0`. Source: +[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.60.0/deploy/charts/operator-crds/values.yaml) +at `v0.60.0`. ## Values diff --git a/docs/toolhive/reference/helm/operator.mdx b/docs/toolhive/reference/helm/operator.mdx index 7394c967..87ddc172 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.59.0`. Source: -[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.59.0/deploy/charts/operator/values.yaml) -at `v0.59.0`. +Chart: `toolhive-operator`. Chart version: `0.60.0`. Source: +[`values.yaml`](https://github.com/stacklok/toolhive/blob/v0.60.0/deploy/charts/operator/values.yaml) +at `v0.60.0`. ## Values @@ -73,7 +73,7 @@ features: gc: gogc: 75 gomemlimit: 110MiB -image: ghcr.io/stacklok/toolhive/operator:v0.59.0 +image: ghcr.io/stacklok/toolhive/operator:v0.60.0 imageDiscovery: enabled: false resources: {} @@ -158,8 +158,8 @@ serviceAccount: labels: {} name: toolhive-operator tolerations: [] -toolhiveRunnerImage: ghcr.io/stacklok/toolhive/proxyrunner:v0.59.0 -vmcpImage: ghcr.io/stacklok/toolhive/vmcp:v0.59.0 +toolhiveRunnerImage: ghcr.io/stacklok/toolhive/proxyrunner:v0.60.0 +vmcpImage: ghcr.io/stacklok/toolhive/vmcp:v0.60.0 volumeMounts: [] volumes: [] ``` @@ -318,7 +318,7 @@ Key containing the PEM-encoded CA certificate in the Secret. **Type:** `string` -**Default:** `"ghcr.io/stacklok/toolhive/operator:v0.59.0"` +**Default:** `"ghcr.io/stacklok/toolhive/operator:v0.60.0"` Container image for the operator. @@ -334,7 +334,7 @@ Image pull policy for the operator container and image-discovery containers. **Type:** `string` -**Default:** `"ghcr.io/stacklok/toolhive/proxyrunner:v0.59.0"` +**Default:** `"ghcr.io/stacklok/toolhive/proxyrunner:v0.60.0"` Container image for ToolHive proxy runners created by the operator. @@ -342,7 +342,7 @@ Container image for ToolHive proxy runners created by the operator. **Type:** `string` -**Default:** `"ghcr.io/stacklok/toolhive/vmcp:v0.59.0"` +**Default:** `"ghcr.io/stacklok/toolhive/vmcp:v0.60.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 07b01ce6..1c4763ce 100644 --- a/static/api-specs/toolhive-api.yaml +++ b/static/api-specs/toolhive-api.yaml @@ -217,7 +217,9 @@ components: for upstreams whose token endpoint answers invalid_grant for any client_id; an HTML page or a bare gateway status is not a verdict. A false rejection re-registers at every start and, when registration also - fails, fails startup. "none" skips the check; the registration is then + fails, fails startup. "none" skips the check: the upstream is not + contacted, but a registration made with another method than a configured + TokenEndpointAuthMethod is still replaced. Otherwise the registration is retired only through RotationKey. type: string registration_probe_interval: @@ -261,6 +263,24 @@ components: server trusts. type: string type: object + authserver.DelegateClientExchangeRefreshTokens: + description: |- + ExchangeRefreshTokens enables refresh-token issuance through token + exchange for this client. Presence of the block enables it; a nil block + refuses both grant kinds. Valid only with restricted, and requires + APIAudience. + properties: + id_jag_max_lifetime: + description: |- + IDJAGMaxLifetime caps the lifetime of grants that can be exchanged for + ID-JAGs. Empty refuses those grants. + type: string + refresh_token_max_lifetime: + description: |- + RefreshTokenMaxLifetime caps the lifetime of grants that can be exchanged + for child refresh tokens. Empty refuses those grants. + type: string + type: object authserver.DelegateClientRunConfig: properties: audiences: @@ -291,10 +311,14 @@ components: type: string grant_types: description: |- - GrantTypes are the grants this client may use. If omitted or empty, it - is [urn:ietf:params:oauth:grant-type:token-exchange]. When set, it must - include token exchange. Allowed values are authorization_code, - refresh_token, the token-exchange grant and the jwt-bearer grant. + GrantTypes are the grants this client may use. If omitted or empty, the + client is stored with [urn:ietf:params:oauth:grant-type:token-exchange] + and its ID-JAG redemption (a JWT-bearer assertion of typ + oauth-id-jag+jwt) is not gated, for backward compatibility. When set, the + list is complete: it must include token exchange, and it must include the + jwt-bearer grant to keep ID-JAG redemption. Allowed values are + authorization_code, refresh_token, the token-exchange grant and the + jwt-bearer grant. items: type: string type: array @@ -332,8 +356,56 @@ components: token_endpoint_auth_signing_alg: description: |- TokenEndpointAuthSigningAlg is the private_key_jwt signing algorithm. - Defaults to RS256. + If omitted, it is derived from the keys: each key's alg, or ES256, ES384 + or ES512 from an EC key's curve. RSA keys without alg use RS256. Keys + that disagree must set it. type: string + token_exchange: + $ref: '#/components/schemas/authserver.DelegateClientTokenExchange' + type: object + authserver.DelegateClientTokenExchange: + description: |- + TokenExchange is the client's token-exchange policy. Its + SelfIssuedSubjectTokens field selects which self-issued subject tokens + the client may exchange and is required whenever the block is present. + The block may be omitted only by a secret-authenticated client whose + GrantTypes is omitted or empty; such a client keeps the legacy rule (any) + and may exchange any self-issued token. A client that sets GrantTypes or + uses private_key_jwt must declare the block. + properties: + allowed_source_clients: + description: |- + AllowedSourceClients are client_id values, as they appear in the subject + token's client_id claim, whose self-issued tokens this client may + exchange in addition to its own. They must be user-facing clients that + register at runtime (DCR or CIMD), so startup cannot check that they + exist. A configured delegate client cannot be listed: its tokens carry + its own client_id, so listing it would trust everything it accepts. + Valid only with restricted; empty means the client's own tokens only, + except that exchange_refresh_tokens requires a non-empty list and does + not accept the client's own tokens. + items: + type: string + type: array + uniqueItems: false + api_audience: + description: |- + APIAudience is the client's own API audience. The client may request it + even when the subject token does not cover it. It must be one of the + client's Audiences. Valid only with restricted. + type: string + exchange_refresh_tokens: + $ref: '#/components/schemas/authserver.DelegateClientExchangeRefreshTokens' + self_issued_subject_tokens: + description: |- + SelfIssuedSubjectTokens is any or restricted, and is required. With + restricted, a self-issued subject token must carry the client's own + client_id or one of AllowedSourceClients, and a may_act claim naming + this client does not lift that restriction. With any, the client may + exchange any self-issued token. + type: string + required: + - self_issued_subject_tokens type: object authserver.IdentityFromTokenRunConfig: description: |- @@ -501,6 +573,11 @@ components: client_secret_post-shaped default). Set this to client_secret_basic explicitly for providers that require HTTP Basic auth. Public clients without a secret use the "none" method. + With DCRConfig, it is the method the client registers with, in place of the one + ToolHive picks from the upstream's advertised methods (client_secret_basic, then + client_secret_post, then none). Set it for an upstream whose token endpoint does + not accept the method ToolHive picks. A cached registration made with another + method is replaced with a new one at the next start. type: string token_response_mapping: $ref: '#/components/schemas/authserver.TokenResponseMappingRunConfig' diff --git a/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json b/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json index a3cc22aa..02b38d65 100644 --- a/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json +++ b/static/api-specs/toolhive-crds/mcpexternalauthconfigs.schema.json @@ -1429,7 +1429,7 @@ "type": "string" }, "registrationProbe": { - "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check; the registration is then retired only through\nRotationKey.", + "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check: the upstream is not contacted, but a registration made\nwith another method than a configured tokenEndpointAuthMethod is still\nreplaced. Otherwise the registration is retired only through\nRotationKey.", "enum": [ "tokenEndpoint", "authorizationEndpoint", @@ -1526,7 +1526,7 @@ "type": "string" }, "tokenEndpointAuthMethod": { - "description": "TokenEndpointAuthMethod selects how the client authenticates at the OAuth token\nendpoint. When empty, credentials are sent in the request body (the historical\nclient_secret_post-shaped default). Set this to client_secret_basic explicitly\nfor providers that require HTTP Basic auth. Public clients without a secret use\nthe \"none\" method.", + "description": "TokenEndpointAuthMethod selects how the client authenticates at the OAuth token\nendpoint. When empty, credentials are sent in the request body (the historical\nclient_secret_post-shaped default). Set this to client_secret_basic explicitly\nfor providers that require HTTP Basic auth. Public clients without a secret use\nthe \"none\" method.\nWith DCRConfig, it is the method the client registers with, in place of the one\nToolHive picks from the upstream's advertised methods (client_secret_basic, then\nclient_secret_post, then none). Set it for an upstream whose token endpoint does\nnot accept the method ToolHive picks. A cached registration made with another\nmethod is replaced with a new one at the next start.", "enum": [ "none", "client_secret_basic", @@ -1754,7 +1754,7 @@ "type": "string" }, "registrationProbe": { - "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check; the registration is then retired only through\nRotationKey.", + "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check: the upstream is not contacted, but a registration made\nwith another method than a configured tokenEndpointAuthMethod is still\nreplaced. Otherwise the registration is retired only through\nRotationKey.", "enum": [ "tokenEndpoint", "authorizationEndpoint", diff --git a/static/api-specs/toolhive-crds/virtualmcpservers.schema.json b/static/api-specs/toolhive-crds/virtualmcpservers.schema.json index dbf1c538..62a7b6bc 100644 --- a/static/api-specs/toolhive-crds/virtualmcpservers.schema.json +++ b/static/api-specs/toolhive-crds/virtualmcpservers.schema.json @@ -1320,7 +1320,7 @@ "type": "string" }, "registrationProbe": { - "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check; the registration is then retired only through\nRotationKey.", + "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check: the upstream is not contacted, but a registration made\nwith another method than a configured tokenEndpointAuthMethod is still\nreplaced. Otherwise the registration is retired only through\nRotationKey.", "enum": [ "tokenEndpoint", "authorizationEndpoint", @@ -1417,7 +1417,7 @@ "type": "string" }, "tokenEndpointAuthMethod": { - "description": "TokenEndpointAuthMethod selects how the client authenticates at the OAuth token\nendpoint. When empty, credentials are sent in the request body (the historical\nclient_secret_post-shaped default). Set this to client_secret_basic explicitly\nfor providers that require HTTP Basic auth. Public clients without a secret use\nthe \"none\" method.", + "description": "TokenEndpointAuthMethod selects how the client authenticates at the OAuth token\nendpoint. When empty, credentials are sent in the request body (the historical\nclient_secret_post-shaped default). Set this to client_secret_basic explicitly\nfor providers that require HTTP Basic auth. Public clients without a secret use\nthe \"none\" method.\nWith DCRConfig, it is the method the client registers with, in place of the one\nToolHive picks from the upstream's advertised methods (client_secret_basic, then\nclient_secret_post, then none). Set it for an upstream whose token endpoint does\nnot accept the method ToolHive picks. A cached registration made with another\nmethod is replaced with a new one at the next start.", "enum": [ "none", "client_secret_basic", @@ -1645,7 +1645,7 @@ "type": "string" }, "registrationProbe": { - "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check; the registration is then retired only through\nRotationKey.", + "description": "RegistrationProbe selects how ToolHive checks the cached upstream client\nat start. tokenEndpoint (the default) redeems a random authorization\ncode with the cached client and reads invalid_client as \"registration\ngone\"; it decides only for upstreams that authenticate the client\nbefore validating the grant. authorizationEndpoint sends an\nauthorization request for the cached client without following\nredirects and reads a direct 400, 401 or 403 that carries an RFC 6749\nJSON error object (error invalid_client, unauthorized_client or\ninvalid_request) as \"gone\" (RFC 6749 §4.1.2.1), for upstreams whose\ntoken endpoint answers invalid_grant for any client_id; an HTML page or\na bare gateway status is not a verdict. A false rejection re-registers\nat every start and, when registration also fails, fails startup. none\nskips the check: the upstream is not contacted, but a registration made\nwith another method than a configured tokenEndpointAuthMethod is still\nreplaced. Otherwise the registration is retired only through\nRotationKey.", "enum": [ "tokenEndpoint", "authorizationEndpoint", From 1e69d43dc68de27e876b15b3a245ca23e02ca211 Mon Sep 17 00:00:00 2001 From: "stacklok-octosts[bot]" <327033170+stacklok-octosts[bot]@users.noreply.github.com> Date: Tue, 3 Nov 2026 00:04:48 +0000 Subject: [PATCH 3/3] Apply prettier and eslint fixups to skill output --- .../guides-k8s/embedded-auth-server-k8s.mdx | 30 +++++++++++++++++-- .../guides-vmcp/embedded-auth-server-vmcp.mdx | 12 ++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/toolhive/guides-k8s/embedded-auth-server-k8s.mdx b/docs/toolhive/guides-k8s/embedded-auth-server-k8s.mdx index 5df0fb6b..799bbf12 100644 --- a/docs/toolhive/guides-k8s/embedded-auth-server-k8s.mdx +++ b/docs/toolhive/guides-k8s/embedded-auth-server-k8s.mdx @@ -832,8 +832,34 @@ oauth2Config: tokenEndpointAuthMethod: client_secret_basic ``` -Allowed values are `none`, `client_secret_basic`, and `client_secret_post`. DCR -clients continue to use the method negotiated at registration time. +Allowed values are `none`, `client_secret_basic`, and `client_secret_post`. + +With [`dcrConfig`](#use-dynamic-client-registration-with-an-upstream-provider), +ToolHive picks a method from the upstream's advertised +`token_endpoint_auth_methods_supported`, preferring `client_secret_basic`, then +`client_secret_post`, then `none`, and registers with it. Some upstreams +advertise `client_secret_basic` but read client credentials only from the +request body, so every code exchange fails with an error such as +`invalid_client: Missing client_id`. For these upstreams, set +`tokenEndpointAuthMethod` alongside `dcrConfig` to choose the method ToolHive +registers with: + +```yaml title="MCPExternalAuthConfig: DCR with a chosen auth method" +oauth2Config: + authorizationEndpoint: 'https://mcp.example.com/authorize' + tokenEndpoint: 'https://mcp.example.com/token' + # highlight-next-line + tokenEndpointAuthMethod: client_secret_post + dcrConfig: + discoveryUrl: 'https://mcp.example.com/.well-known/oauth-authorization-server' +``` + +ToolHive refuses to start when the upstream's discovery metadata lists supported +methods that don't include the configured one, when you set `none` and the +upstream doesn't advertise S256 PKCE, or when the registration response names a +different method. If ToolHive cached a registration made with a different +method, it replaces that registration with a new one at the next start, so you +don't need to change `rotationKey`. ### Trust a private CA for the upstream provider diff --git a/docs/toolhive/guides-vmcp/embedded-auth-server-vmcp.mdx b/docs/toolhive/guides-vmcp/embedded-auth-server-vmcp.mdx index bef9e31a..392c9e28 100644 --- a/docs/toolhive/guides-vmcp/embedded-auth-server-vmcp.mdx +++ b/docs/toolhive/guides-vmcp/embedded-auth-server-vmcp.mdx @@ -601,8 +601,16 @@ method. Set `tokenEndpointAuthMethod: client_secret_basic` on `oauth2Config` for providers that require credentials in the HTTP `Authorization` header instead of the request body. Allowed values are `none`, `client_secret_basic`, and -`client_secret_post`. DCR clients continue to use the method negotiated at -registration time. +`client_secret_post`. + +With `dcrConfig`, ToolHive registers with the method it prefers from the +upstream's advertised methods (`client_secret_basic`, then `client_secret_post`, +then `none`). When the upstream's token endpoint doesn't accept that method, set +`tokenEndpointAuthMethod` alongside `dcrConfig` to choose the method ToolHive +registers with. ToolHive replaces a cached registration made with a different +method at the next start. See +[Select the token endpoint auth method](../guides-k8s/embedded-auth-server-k8s.mdx#select-the-token-endpoint-auth-method) +for the validation rules. ### Authorize upstream registration with an initial access token