Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/upstream-projects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ projects:

- id: toolhive
repo: stacklok/toolhive
version: v0.47.1
version: v0.48.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/,
Expand Down
19 changes: 19 additions & 0 deletions docs/toolhive/guides-cli/configure-mcp-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ thv run --session-ttl 4h <SERVER>
Set a longer value when clients hold sessions open for long-running operations,
or a shorter value to free resources faster.

### Adjust the proxy read timeout

The proxy bounds how long it spends reading a full incoming request (headers
plus body). This limit protects the proxy from slow uploads that hold
connections open; long-lived SSE responses stream normally because the limit
applies to request bodies only.

The default is 30 seconds. Raise it for workloads that upload large payloads, or
lower it to fail slow uploads faster:

```bash
thv run --proxy-read-timeout 2m <SERVER>
```

The flag accepts any Go duration string, for example `45s` or `2m`. Omitting the
flag or passing `0` keeps the 30-second default; negative values are rejected.
The setting applies to the streamable HTTP, SSE, and transparent proxy
transports.

### Run a server in the foreground

By default, ToolHive runs the server in the background and returns control to
Expand Down
4 changes: 4 additions & 0 deletions docs/toolhive/guides-cli/skills-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ thv skill push ghcr.io/my-org/skills/my-skill:v1.0.0
error before publishing anything.
- **Key-pair with `--key`**: sign with a cosign private key on disk. Set
`COSIGN_PASSWORD` in the `thv serve` environment to decrypt an encrypted key.
Requires the locally discovered ToolHive server; a push to a remote or
manually configured server (for example, via `TOOLHIVE_API_URL`) returns
`403`. Use `--identity-token` or omit `--key` for keyless signing against
those servers.
- **Explicitly unsigned with `--no-sign`**: publish without any signature.

The three signing inputs are mutually exclusive. Combining `--key`,
Expand Down
84 changes: 74 additions & 10 deletions docs/toolhive/guides-k8s/embedded-auth-server-k8s.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,8 @@ this section to adapt that upstream connection:

- Use `oauth2Config` instead of `oidcConfig` when the upstream provider does not
support OIDC discovery.
- Within `oauth2Config`, set either pre-provisioned client credentials or
`dcrConfig`, not both.
- Within `oidcConfig` or `oauth2Config`, set either pre-provisioned client
credentials or `dcrConfig`, not both.
- Add identity mapping, authorization parameters, and an explicit callback URL
as the upstream provider requires.

Expand Down Expand Up @@ -759,6 +759,42 @@ For OAuth 2.0 servers that return identity in the token response itself, see

:::

#### Select the token endpoint auth method

When a client secret is configured, the embedded auth server sends the client
credentials to the upstream token endpoint using HTTP Basic auth
(`client_secret_basic`). This matches the RFC 7591 default for confidential
clients and covers most providers. Public clients without a secret use the
`none` method.

Set `tokenEndpointAuthMethod: client_secret_post` on `oauth2Config` for
providers that require credentials in the request body instead of the
`Authorization` header:

```yaml title="MCPExternalAuthConfig: token endpoint auth method"
oauth2Config:
authorizationEndpoint: 'https://example.com/oauth/authorize'
tokenEndpoint: 'https://example.com/oauth/token'
clientId: '<CLIENT_ID>'
clientSecretRef:
name: upstream-idp-secret
key: client-secret
# highlight-next-line
tokenEndpointAuthMethod: client_secret_post
```

Allowed values are `none`, `client_secret_basic`, and `client_secret_post`. DCR
clients continue to use the method negotiated at registration time.

:::info[Changed in v0.48.0]

Pre-registered OAuth 2.0 upstream clients with a configured secret previously
sent credentials in the request body. They now default to `client_secret_basic`.
If your provider rejects Basic auth on the token endpoint (rare), set
`tokenEndpointAuthMethod: client_secret_post` to restore the previous behavior.

:::

### Trust a private CA for the upstream provider

If the upstream identity provider serves its endpoints with a certificate signed
Expand Down Expand Up @@ -817,8 +853,8 @@ the two references configure trust for different network hops.

### Use dynamic client registration with an upstream provider

Some OAuth 2.0 providers register clients dynamically instead of requiring you
to create an application in a provider dashboard. Add `dcrConfig` to an
Some providers register clients dynamically instead of requiring you to create
an application in a provider dashboard. Add `dcrConfig` to an `oidcConfig` or
`oauth2Config` upstream to have the embedded authorization server register
itself at runtime using RFC 7591.

Expand All @@ -827,8 +863,39 @@ This is separate from MCP clients registering with ToolHive's embedded
authorization server in
[Configure MCP client registration](#configure-mcp-client-registration).

This example uses an RFC 8414 discovery document. ToolHive reads the
`registration_endpoint` and other provider metadata from `discoveryUrl`:
#### DCR with an OIDC upstream

For an OIDC upstream, an empty `dcrConfig: {}` is enough: the embedded auth
server derives the discovery URL from
`issuerUrl + /.well-known/openid-configuration` and reads the registration
endpoint from the document.

```yaml title="MCPExternalAuthConfig: OIDC upstream with DCR"
spec:
type: embeddedAuthServer
embeddedAuthServer:
issuer: 'https://toolhive.example.com'
upstreamProviders:
- name: keycloak
type: oidc
oidcConfig:
issuerUrl: 'https://keycloak.example.com/realms/main'
# highlight-start
dcrConfig: {}
# highlight-end
```

Set `dcrConfig.discoveryUrl` explicitly when the upstream publishes discovery at
a non-standard path, or `dcrConfig.registrationEndpoint` to bypass discovery
entirely.

Don't set `clientId` or `clientSecretRef` when you set `dcrConfig`. ToolHive
obtains the client ID and client secret from the DCR response.

#### DCR with an OAuth 2.0 upstream

For an OAuth 2.0 upstream, use an RFC 8414 discovery document. ToolHive reads
the `registration_endpoint` and other provider metadata from `discoveryUrl`:

```yaml title="embedded-auth-config-dcr.yaml"
apiVersion: toolhive.stacklok.dev/v1beta1
Expand Down Expand Up @@ -884,13 +951,10 @@ upstreamProviders:
# highlight-end
```

Set exactly one of `discoveryUrl` or `registrationEndpoint`. A direct
Set at most one of `discoveryUrl` or `registrationEndpoint`. A direct
registration endpoint bypasses discovery, so specify the authorization and token
endpoints and the scopes that ToolHive should request.

Don't set `clientId` or `clientSecretRef` when you set `dcrConfig`. ToolHive
obtains the client ID and client secret from the DCR response.

#### Authorize registration with an initial access token

If the upstream provider requires an initial access token, create a Secret in
Expand Down
50 changes: 46 additions & 4 deletions docs/toolhive/guides-vmcp/embedded-auth-server-vmcp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,30 @@ for guidance on which scopes to include.
This section configures ToolHive's OAuth connection to an external provider. It
is separate from incoming registration between MCP clients and ToolHive. Use a
preconfigured client ID and secret, or add `dcrConfig` to register ToolHive at
runtime when the provider supports RFC 7591.
runtime when the provider supports RFC 7591. DCR works with both `oidcConfig`
and `oauth2Config` upstreams.

This example uses an RFC 8414 discovery document to locate the provider's
`registration_endpoint`:
For an OIDC upstream, an empty `dcrConfig: {}` is enough: the embedded auth
server derives the discovery URL from
`issuerUrl + /.well-known/openid-configuration` and reads the registration
endpoint from the document.

```yaml title="VirtualMCPServer: OIDC upstream with DCR"
spec:
authServerConfig:
issuer: 'https://toolhive.example.com'
upstreamProviders:
- name: keycloak
type: oidc
oidcConfig:
issuerUrl: 'https://keycloak.example.com/realms/main'
# highlight-start
dcrConfig: {}
# highlight-end
```

For an OAuth 2.0 upstream, use an RFC 8414 discovery document to locate the
provider's `registration_endpoint`:

```yaml title="VirtualMCPServer: upstream DCR discovery"
spec:
Expand Down Expand Up @@ -439,7 +459,7 @@ dcrConfig:
registrationEndpoint: 'https://mcp.example.com/register'
```

Set exactly one of `discoveryUrl` or `registrationEndpoint`. A direct
Set at most one of `discoveryUrl` or `registrationEndpoint`. A direct
registration endpoint bypasses discovery, so keep the authorization endpoint,
token endpoint, and scopes in the parent `oauth2Config`. With `dcrConfig`,
ToolHive obtains the client ID and secret from the DCR response.
Expand All @@ -450,6 +470,28 @@ ToolHive obtains the client ID and secret from the DCR response.
| `dcrConfig.registrationEndpoint` | HTTPS URL of the RFC 7591 registration endpoint. |
| `dcrConfig.initialAccessTokenRef` | Optional reference to a Secret whose value ToolHive sends as a bearer token when it calls the registration endpoint. Requires `name` and `key`. |

### Select the token endpoint auth method

When a client secret is configured, ToolHive sends the credentials to the
upstream OAuth 2.0 token endpoint using HTTP Basic auth (`client_secret_basic`).
This matches the RFC 7591 default for confidential clients and covers most
providers. Public clients without a secret use the `none` method.

Set `tokenEndpointAuthMethod: client_secret_post` on `oauth2Config` for
providers that require credentials in the request body instead of the
`Authorization` header. Allowed values are `none`, `client_secret_basic`, and
`client_secret_post`. DCR clients continue to use the method negotiated at
registration time.

:::info[Changed in v0.48.0]

Pre-registered OAuth 2.0 upstream clients with a configured secret previously
sent credentials in the request body. They now default to `client_secret_basic`.
If your provider rejects Basic auth on the token endpoint (rare), set
`tokenEndpointAuthMethod: client_secret_post` to restore the previous behavior.

:::

### Authorize upstream registration with an initial access token

If the upstream provider requires an initial access token, create a Secret in
Expand Down
5 changes: 3 additions & 2 deletions docs/toolhive/reference/cli/thv_ai-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Manage AI-tool plugins

### Synopsis

The ai-plugin command provides subcommands to manage plugins for AI tools
(e.g. Claude Code, Codex) — not plugins for ToolHive itself.
Manage plugins for AI tools such as Claude Code and Codex, not plugins
for ToolHive itself. A plugin is a manifest-based bundle that may contain
commands, agents, skills, hooks, and server declarations.

### Options

Expand Down
8 changes: 6 additions & 2 deletions docs/toolhive/reference/cli/thv_ai-plugin_install.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ Install an AI-tool plugin

### Synopsis

Install a plugin by name or OCI reference.
The plugin will be fetched from a remote registry and installed locally.
Install a plugin from git, an OCI reference, or an exact registry name.

Project-scoped installs verify signatures and record trust in toolhive.lock.yaml.
Use --public-key for the first project install of a key-pair-signed OCI artifact;
the key is then pinned for sync and upgrade. User-scoped installs do not use
lock-file verification and reject --public-key.

```
thv ai-plugin install [plugin-name] [flags]
Expand Down
3 changes: 3 additions & 0 deletions docs/toolhive/reference/cli/thv_ai-plugin_push.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Push a built AI-tool plugin to an OCI registry

Push a previously built plugin artifact to a remote OCI registry.

Push signs keylessly by default. Use --no-sign to publish unsigned; plugin push
does not support key-pair signing and has no --key flag.

```
thv ai-plugin push [reference] [flags]
```
Expand Down
2 changes: 1 addition & 1 deletion docs/toolhive/reference/cli/thv_ai-plugin_sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ thv ai-plugin sync [flags]
--adopt Write lock entries for existing unmanaged project-scope installs
--allow-unsigned Record plugins as unsigned in the lock file: when adopting installs whose signature state cannot be established (--adopt), and when repairing an entry that records no trust decision and whose content is unsigned
--check Report drift without installing, writing, or removing anything
--clients string Comma-separated target client apps (e.g. claude-code,opencode), or "all" for every available client
--clients string Comma-separated target client apps (e.g. claude-code,codex), or "all" for every available client
--format string Output format (json, text) (default "text")
-h, --help help for sync
--project-root string Project root path (default: auto-detected from the current directory)
Expand Down
2 changes: 1 addition & 1 deletion docs/toolhive/reference/cli/thv_ai-plugin_upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ thv ai-plugin upgrade [plugin-name...] [flags]
```
--allow-ref-change Permit the artifact to move to a different repository during upgrade
--allow-signer-change Permit upgrading to an artifact signed by a different identity; the new identity replaces the recorded one
--clients string Comma-separated target client apps (e.g. claude-code,opencode), or "all" for every available client
--clients string Comma-separated target client apps (e.g. claude-code,codex), or "all" for every available client
--fail-on-changes Report what would change without installing anything; a CI freshness gate
--format string Output format (json, text) (default "text")
-h, --help help for upgrade
Expand Down
1 change: 1 addition & 0 deletions docs/toolhive/reference/cli/thv_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ thv run [flags] SERVER_OR_IMAGE_OR_PROTOCOL [-- ARGS...]
--print-resolved-overlays Debug: show resolved container paths for tmpfs overlays (default false)
--proxy-mode string Proxy mode for stdio (streamable-http or sse (deprecated, will be removed)) (default "streamable-http")
--proxy-port int Port for the HTTP proxy to listen on (host port)
--proxy-read-timeout duration Maximum time to read a full request on the proxy (e.g., 30s, 1m); zero uses the default (30s)
-p, --publish stringArray Publish a container's port(s) to the host (format: hostPort:containerPort)
--remote-auth Enable OAuth/OIDC authentication to remote MCP server (default false)
--remote-auth-authorize-url string OAuth authorization endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth)
Expand Down
2 changes: 1 addition & 1 deletion docs/toolhive/reference/cli/thv_skill_push.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ thv skill push [reference] [flags]
```
-h, --help help for push
--identity-token string OIDC identity token (or a path to a file containing one) for keyless signing. Mutually exclusive with --key. If omitted, one is acquired automatically: from the ambient CI OIDC token when running with id-token: write permission, otherwise via an interactive browser sign-in
--key string Path to a cosign private key to sign the pushed artifact. Encrypted keys are decrypted with COSIGN_PASSWORD read from the 'thv serve' process, which performs the signing. Consumers installing the result project-scoped must pass --public-key with the matching cosign public key the first time; distribute it alongside the artifact. Keyless signing needs no such out-of-band step, since the signer identity is verifiable from the artifact itself
--key string Path to a cosign private key to sign the pushed artifact. Requires the locally discovered ToolHive server; for a remote or manually configured API URL, use keyless signing. Encrypted keys are decrypted with COSIGN_PASSWORD read from the 'thv serve' process, which performs the signing. Consumers installing the result project-scoped must pass --public-key with the matching cosign public key the first time; distribute it alongside the artifact. Keyless signing needs no such out-of-band step, since the signer identity is verifiable from the artifact itself
--no-sign Push without signing (consumers will need an explicit unsigned exception to install project-scoped)
```

Expand Down
Loading