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.46.0
version: v0.47.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
109 changes: 109 additions & 0 deletions docs/toolhive/guides-cli/ai-plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,118 @@ After building, push to a remote OCI registry:
thv ai-plugin push ghcr.io/my-org/plugins/my-plugin:v1.0.0
```

`thv ai-plugin push` signs the pushed artifact by default using Sigstore keyless
signing. ToolHive acquires a short-lived OIDC identity token and records the
signature in the public transparency log. In GitHub Actions with
`id-token: write` permission, the ambient OIDC token is used automatically; on
an interactive terminal, ToolHive prompts for browser sign-in; in any other
environment the push fails with an actionable error before publishing anything.

To supply a pre-acquired identity token, pass `--identity-token`:

```bash
thv ai-plugin push ghcr.io/my-org/plugins/my-plugin:v1.0.0 \
--identity-token "$IDENTITY_TOKEN"
```

To publish without a signature, pass `--no-sign`:

```bash
thv ai-plugin push ghcr.io/my-org/plugins/my-plugin:v1.0.0 --no-sign
```

To install an unsigned plugin project-scoped, pass `--allow-unsigned` to
`thv ai-plugin install`.

:::note

Plugin push is keyless-only. If you need to sign with a cosign key pair, publish
a [skill](./skills-management.mdx) instead, which supports both keyless and
key-pair signing via `--key`.

:::

Push uses your existing container registry credentials (for example, from
`docker login` or `podman login`). Authenticate before pushing.

## Reproduce and upgrade project-scoped plugins

Project-scoped installs record their source, resolved digest, and signer
identity in a `toolhive.lock.yaml` file at the project root. This lets you
reinstall the same plugin content on another machine, detect drift in CI, and
upgrade to newer content when the catalog moves forward. User-scoped installs
don't participate in the lock file.

### Restore plugins from the lock file

To reinstall the plugins recorded in the lock file (for example, after cloning
the project on a new machine), run `thv ai-plugin sync` from the project
directory:

```bash
thv ai-plugin sync --project-root .
```

Missing or drifted plugins are reinstalled at their pinned digest. Sync prompts
for confirmation before installing; pass `--yes` in non-interactive environments
such as CI.

By default, `thv ai-plugin sync` targets every plugin-supporting client
installed on the machine. Pass `--clients` to constrain sync to a specific set:

```bash
thv ai-plugin sync --project-root . --clients claude-code
```

Use `--check` to report drift without installing or writing anything. This is
useful as a CI gate:

```bash
thv ai-plugin sync --project-root . --check
```

If you have project-scoped plugins that were installed before the lock file
existed, `--adopt` records lock entries for them so subsequent syncs include
them:

```bash
thv ai-plugin sync --project-root . --adopt
```

Use `--prune` to remove installs that are no longer in the lock file.

See the
[`thv ai-plugin sync` command reference](../reference/cli/thv_ai-plugin_sync.md)
for all options.

### Upgrade project-scoped plugins

Upgrade the project's plugins to newer pinned content:

```bash
thv ai-plugin upgrade --project-root .
```

A version or tag change within the same OCI repository proceeds without
`--allow-ref-change`, including a move to an older tag. ToolHive pins the
resolved digest and blocks signer changes. If the catalog moves the plugin to a
different repository, organization, or registry, the upgrade is blocked. Review
the new source, then repeat the command with `--allow-ref-change` if you intend
to permit that repository move. ToolHive prompts before installing the planned
changes; pass `--yes` in non-interactive environments.

Plugins pinned to an immutable reference (an OCI digest or a full Git commit
hash) are reported as not upgradable, because there is nothing newer to resolve
to.

Use `--preview` to see what would change without persisting anything, or
`--fail-on-changes` as a CI freshness gate that reports pending upgrades without
installing them.

See the
[`thv ai-plugin upgrade` command reference](../reference/cli/thv_ai-plugin_upgrade.md)
for all options.

## Manage local builds

The `builds` subcommand exposes the local OCI store where `thv ai-plugin build`
Expand Down
16 changes: 16 additions & 0 deletions docs/toolhive/guides-cli/skills-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,22 @@ a consumer installs an unsigned skill project-scoped, they must pass
`--allow-unsigned` to `thv skill install`. User-scoped installs do not enforce
signatures.

When a skill was signed with `--key` rather than keyless, the first
project-scoped install must also pass `--public-key` with the matching cosign
public key (`cosign.pub`):

```bash
thv skill install my-skill --scope project --project-root . \
--public-key ./cosign.pub
```

ToolHive verifies the signature against the key, then pins the key in the
project's `toolhive.lock.yaml` so subsequent `thv skill sync` and
`thv skill upgrade` runs reuse it automatically. Distribute the public key
alongside the artifact so users installing the skill can supply it.
Keyless-signed skills need no such out-of-band step, since the signer identity
is verifiable from the transparency-log entry.

:::note

Pushing to a remote registry uses your existing container registry credentials
Expand Down
20 changes: 10 additions & 10 deletions docs/toolhive/guides-k8s/embedded-auth-server-k8s.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,16 @@ with a generic "authorization header required" error.

**Configuration reference:**

| Field | Description |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `issuer` | HTTPS URL identifying this authorization server. Appears in the `iss` claim of issued JWTs. Must be a bare host with no path. |
| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. |
| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. |
| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). |
| `upstreamProviders` | Configuration for upstream identity providers. MCPServer and MCPRemoteProxy support one provider; VirtualMCPServer supports multiple providers for sequential authentication. |
| `cimd` | Optional Client ID Metadata Document (CIMD) configuration. When `cimd.enabled` is `true`, the auth server accepts HTTPS URLs as `client_id` values and resolves them via CIMD, letting clients (for example, VS Code) authenticate without prior Dynamic Client Registration. See [Enable CIMD for zero-registration clients](#enable-cimd-for-zero-registration-clients). |
| `baselineClientScopes` | Optional list of OAuth 2.0 scopes merged into every DCR-registered client's scope set. Use this when MCP clients register with a narrowed `scope` field but then request wider scopes at `/oauth/authorize`. See [Enable baseline scopes for DCR clients](#enable-baseline-scopes-for-dcr-clients). |
| `disableUpstreamTokenInjection` | Optional. When `true`, the embedded auth server authenticates clients normally but the proxy strips `Authorization`, `Cookie`, and `Proxy-Authorization` from forwarded requests instead of swapping the JWT for an upstream token. Use this for public backends (such as documentation servers) that you still want to gate behind client auth. Cannot be combined with `tokenExchange` or `awsSts` on the same workload. |
| Field | Description |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `issuer` | HTTPS URL identifying this authorization server. Appears in the `iss` claim of issued JWTs. Must be a bare host with no path. |
| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. |
| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. |
| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). |
| `upstreamProviders` | Configuration for upstream identity providers. MCPServer and MCPRemoteProxy support one provider; VirtualMCPServer supports multiple providers for sequential authentication. |
| `cimd` | Optional Client ID Metadata Document (CIMD) configuration. When `cimd.enabled` is `true`, the auth server accepts HTTPS URLs as `client_id` values and resolves them via CIMD, letting clients (for example, VS Code) authenticate without prior Dynamic Client Registration. See [Enable CIMD for zero-registration clients](#enable-cimd-for-zero-registration-clients). |
| `baselineClientScopes` | Optional list of OAuth 2.0 scopes merged into every DCR-registered client's scope set. Use this when MCP clients register with a narrowed `scope` field but then request wider scopes at `/oauth/authorize`. See [Enable baseline scopes for DCR clients](#enable-baseline-scopes-for-dcr-clients). |
| `disableUpstreamTokenInjection` | Optional. When `true`, the embedded auth server authenticates clients normally but the proxy strips `Authorization`, `Cookie`, and `Proxy-Authorization` from forwarded requests instead of swapping the JWT for an upstream token. Use this for public backends (such as documentation servers) that you still want to gate behind client auth. Cannot be combined with `tokenExchange`, `awsSts`, or OBO (Stacklok Enterprise) on the same workload, since each would re-add credentials after the strip. |

## Step 5: Create the MCPOIDCConfig and MCPServer resources

Expand Down
1 change: 1 addition & 0 deletions docs/toolhive/reference/cli/thv_ai-plugin_install.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ thv ai-plugin install [plugin-name] [flags]
--group string Group to add the plugin to after installation
-h, --help help for install
--project-root string Project root path for project-scoped installs
--public-key string Path to the cosign public key (cosign.pub) a key-pair-signed plugin must verify against. Required the first time such a plugin is installed project-scoped; the key is then pinned in the lock file and reused automatically
--scope string Installation scope (user, project) (default "user")
```

Expand Down
4 changes: 3 additions & 1 deletion docs/toolhive/reference/cli/thv_ai-plugin_push.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ thv ai-plugin push [reference] [flags]
### Options

```
-h, --help help for push
-h, --help help for push
--identity-token string OIDC identity token (or a path to a file containing one) for keyless signing. If omitted, one is acquired automatically: from the GitHub Actions OIDC token when running with id-token: write permission, otherwise via an interactive browser sign-in
--no-sign Push without signing (consumers will need an explicit unsigned exception to install project-scoped)
```

### Options inherited from parent commands
Expand Down
4 changes: 1 addition & 3 deletions docs/toolhive/reference/cli/thv_ai-plugin_sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ Unless --check is set, sync prompts for confirmation before installing —
plugin content is a set of AI-followed instructions. Pass --yes to skip the
prompt (required in non-interactive contexts such as CI).

Requires TOOLHIVE_PLUGINS_LOCK_ENABLED=true.

```
thv ai-plugin sync [flags]
```
Expand All @@ -36,7 +34,7 @@ thv ai-plugin sync [flags]

```
--adopt Write lock entries for existing unmanaged project-scope installs
--allow-unsigned Allow adopting plugins whose signature state cannot be established (recorded as unsigned)
--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
--format string Output format (json, text) (default "text")
Expand Down
2 changes: 0 additions & 2 deletions docs/toolhive/reference/cli/thv_ai-plugin_upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ Unless --preview is set, upgrade prompts for confirmation before installing —
plugin content is a set of AI-followed instructions. Pass --yes to skip the
prompt (required in non-interactive contexts such as CI).

Requires TOOLHIVE_PLUGINS_LOCK_ENABLED=true.

```
thv ai-plugin upgrade [plugin-name...] [flags]
```
Expand Down
1 change: 1 addition & 0 deletions docs/toolhive/reference/cli/thv_proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ thv proxy [flags] SERVER_NAME
--allowed-origins stringArray Exact-match allowlist for the HTTP Origin header (repeatable). Recommended when binding publicly; loopback binds derive a default allowlist automatically, non-loopback binds log a warning when no value is supplied. Example: https://my-mcp.example.com
-h, --help help for proxy
--host string Host for the HTTP proxy to listen on (IP or hostname) (default "127.0.0.1")
--max-request-body-size int Maximum inbound request body size in bytes; zero uses the default (8 MiB)
--oidc-audience string Expected audience for the token
--oidc-client-id string OIDC client ID
--oidc-client-secret string OIDC client secret (optional, for introspection)
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 @@ -133,6 +133,7 @@ thv run [flags] SERVER_OR_IMAGE_OR_PROTOCOL [-- ARGS...]
--jwks-allow-private-ip Allow JWKS/OIDC endpoints on private IP addresses (use with caution) (default false)
--jwks-auth-token-file string Path to file containing bearer token for authenticating JWKS/OIDC requests
-l, --label stringArray Set labels on the container (format: key=value)
--max-request-body-size int Maximum inbound request body size in bytes; zero uses the default (8 MiB)
--name string Name of the MCP server (default to auto-generated from image)
--network string Connect the container to a network (e.g., 'host' for host networking). Note: 'host' and 'none' cannot enforce network isolation, so isolation is dropped for those modes.
--oidc-audience string Expected audience for the token
Expand Down
Loading
Loading