Skip to content
Open
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.40.1
version: v0.41.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
6 changes: 6 additions & 0 deletions docs/toolhive/concepts/cedar-policies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ cedar:
separate from group claims. Use this when your identity provider provides
roles in a different claim than groups (for example, Entra ID `roles`
claim). If not set, roles are extracted from the same claims as groups.
- `multi_valued_claims`: Optional list of JWT claim names (for example,
`scope`, `scp`) to normalize into a canonical form so policies match the
same way whether the claim arrives as a space-delimited string or a JSON
array. See
[Multi-valued claim normalization](../reference/authz-policy-reference.mdx#multi-valued-claim-normalization)
in the policy reference for the exposed attributes and migration notes.

## Writing effective policies

Expand Down
33 changes: 33 additions & 0 deletions docs/toolhive/concepts/embedded-auth-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,39 @@ client's credential headers (`Authorization`, `Cookie`, and
`Proxy-Authorization`) instead of swapping them for an upstream token, so the
backend receives an unauthenticated request.

## Delegated tokens via RFC 8693 token exchange

Starting in v0.41.0, the embedded authorization server's token endpoint accepts
[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange
requests. A client that already holds a ToolHive-issued JWT can present it as a
`subject_token` and receive a new token, minted with a different audience or a
narrower scope set, that acts on behalf of the same user. Use this pattern when
an agent framework needs to call several downstream services under a single
user's identity without re-prompting the user.

The token endpoint uses the grant type
`urn:ietf:params:oauth:grant-type:token-exchange`. Only confidential clients
that were registered with the token-exchange grant may call it; public clients
are refused. The exchanged token's audience is bounded by the subject token's
own audience so a compromised client cannot broaden its reach.

### Delegation chain in audit logs

When the exchange succeeds, ToolHive mints an `act` claim on the new token
recording which client is acting for the user. If the subject token already
carried a prior `act`, it is nested inside the new one, giving an auditable
delegation chain rather than dropping the earlier hop. The chain depth is capped
at 10 hops; exchanges that would exceed the cap are refused.

Audit events for delegated calls carry a `delegation` object with the full
`chain` (each hop's actor client ID), so "agent X acting for Alice" is
distinguishable from "Alice directly" in the audit log. Non-delegated tokens
(plain user tokens with no `act` claim) omit the `delegation` field entirely.

Cedar policies can also read the `act` claim through the standard
`claim_`-prefixed attributes; see
[Principal attributes](../reference/authz-policy-reference.mdx#principal-attributes).

## Automatic token refresh

Upstream access tokens expire independently of the ToolHive JWT lifespan. When
Expand Down
11 changes: 7 additions & 4 deletions docs/toolhive/concepts/mcp-primer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,13 @@ combines the official registry with the curated ToolHive catalog.

## Where MCP is headed

The spec uses dated revisions (2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25)
rather than semver, with the `MCP-Protocol-Version` header allowing clients and
servers to negotiate compatibility. The core protocol is deliberately small; new
capabilities arrive as additive extensions, including a separate
The spec uses dated revisions (2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25,
2026-07-28) rather than semver, with the `MCP-Protocol-Version` header allowing
clients and servers to negotiate compatibility. The 2026-07-28 revision adds a
stateless variant that drops the `initialize` handshake and carries per-request
protocol metadata in `_meta`, sitting alongside the session-based 2025-11-25
revision that most clients use today. The core protocol is deliberately small;
new capabilities arrive as additive extensions, including a separate
[authorization extensions](https://github.com/modelcontextprotocol/ext-auth)
track for advanced auth scenarios.

Expand Down
20 changes: 20 additions & 0 deletions docs/toolhive/concepts/skills.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,26 @@ Skills can be distributed as:
- **Registry entries**: Published to a ToolHive Registry Server for centralized
discovery and installation by name

## Reproducible project-scope installs

Project-scope skill installs can be pinned in a `toolhive.lock.yaml` file at the
project root, so every team member and every CI run ends up with the same skill
content. Each entry records the skill's resolved reference, digest, content
digest, and (when available) Sigstore provenance. Commit the lock file to the
repository like `package-lock.json` or `go.sum`.

Two experimental commands operate on the lock file:

- `thv skill sync` restores a project's installs to match `toolhive.lock.yaml`
and, with `--check`, reports drift for CI without changing anything.
- `thv skill upgrade` re-resolves pinned entries to newer content in a
controlled way, with a preview mode and a freshness gate for CI.

The lock file feature is experimental in v0.41.0 and requires
`TOOLHIVE_SKILLS_LOCK_ENABLED=true` on the ToolHive API server process. For the
full workflow, flag reference, and typed exit codes, see
[Pin a project's skills for reproducible installs](../guides-cli/skills-management.mdx#pin-a-projects-skills-for-reproducible-installs).

## Next steps

- [Manage agent skills in the ToolHive UI](../guides-ui/skills.mdx)
Expand Down
31 changes: 31 additions & 0 deletions docs/toolhive/guides-cli/build-containers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,37 @@ You can include multiple build-time arguments as needed:
thv build uvx://my-package -- --transport stdio --log-level info
```

## Constrain build-time dependencies

Some Python MCP servers pull in transitive dependencies whose newer versions
break the server. Use the `--build-with` flag on `uvx://` builds to pin those
dependencies at build time. ToolHive passes each `--build-with` value through to
`uv tool install --with` as a [PEP 508](https://peps.python.org/pep-0508/)
specifier, so you can pin an exact version, cap a major version, or express any
other valid specifier:

```bash
# Cap the mcp package at 1.x while installing an MCP server
thv build --build-with 'mcp<2' uvx://mcp-server-git

# Combine multiple constraints
thv build \
--build-with 'mcp<2' \
--build-with 'anyio>=4' \
uvx://mcp-server-example
```

The same flag is available on [`thv run`](./run-mcp-servers.mdx) when you're
running directly from a protocol scheme instead of pre-building.

:::warning[uvx only]

`--build-with` is only supported for `uvx://` protocol scheme builds. Passing it
to an `npx://` or `go://` build fails with an explicit error rather than
silently ignoring the constraint.

:::

## Dockerfile generation

Use the `--dry-run` flag to generate the Dockerfile without building the image:
Expand Down
23 changes: 23 additions & 0 deletions docs/toolhive/guides-cli/network-isolation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,29 @@ thv run --isolate-network=false <SERVER>
The container's own network namespace still isolates it from the host; only
ToolHive's allowlist enforcement is disabled.

## Choose an egress proxy backend

By default, ToolHive uses Squid for egress filtering. Starting in v0.41.0,
ToolHive also supports an Envoy backend that consolidates egress and ingress
into a single sidecar and translates the permission profile's `AllowHost` and
`AllowPort` rules into Envoy RBAC policies. Squid remains the default.

Set the `TOOLHIVE_NETWORK_PROXY` environment variable before running `thv run`
(or `thv serve` if you use the API server) to select the backend:

```bash
# Default: Squid backend
export TOOLHIVE_NETWORK_PROXY=squid # or leave unset

# Opt-in: Envoy backend
export TOOLHIVE_NETWORK_PROXY=envoy
thv run <SERVER>
```

Any other value causes ToolHive to exit at startup with an error. Both backends
enforce the same permission profile semantics, so you don't need to change your
permission profile to switch backends.

## Interaction with `--network host` and `--network none`

Network isolation is a bridge-network construct: the egress, DNS, and ingress
Expand Down
32 changes: 32 additions & 0 deletions docs/toolhive/guides-cli/run-mcp-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,41 @@ thv run --runtime-add-package git --runtime-add-package ca-certificates \
uvx://mcp-server-git
```

To constrain build-time Python dependencies for `uvx://` runs, use
`--build-with` with a [PEP 508](https://peps.python.org/pep-0508/) specifier.
Each value is passed through to `uv tool install --with`, so you can pin exact
versions or cap majors:

```bash
thv run --build-with 'mcp<2' uvx://mcp-server-git
```

`--build-with` is only supported for `uvx://` builds; passing it to `npx://` or
`go://` fails with an explicit error. To pre-build the image with the same
constraints, pass identical flags to
[`thv build`](./build-containers.mdx#constrain-build-time-dependencies).

These flags apply only to protocol-scheme runs, since that's when ToolHive
builds the image.

### Reject unknown MCP protocol versions

By default, the streamable-HTTP proxy accepts any value for the
`MCP-Protocol-Version` header (an absent header is also accepted). To reject
clients that send an unknown or unsupported revision, add the
`--strict-protocol-validation` flag:

```bash
thv run --transport streamable-http --strict-protocol-validation <SERVER>
```

Requests whose `MCP-Protocol-Version` header is not one of the revisions this
ToolHive build recognizes are refused with HTTP 400. Requests with no header are
still accepted, so lenient clients continue to work.

Use this flag when you want to enforce a specific revision set at the proxy, for
example to keep a fleet on a known-good protocol version during a rollout.

### Configure network transport

When you run custom MCP servers using the SSE (`--transport sse`) or Streamable
Expand Down
98 changes: 98 additions & 0 deletions docs/toolhive/guides-cli/skills-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,102 @@ authenticated before pushing.

:::

## Pin a project's skills for reproducible installs

:::warning[Experimental in v0.41.0]

The skills lock file, `thv skill sync`, and `thv skill upgrade` are experimental
in v0.41.0. Set `TOOLHIVE_SKILLS_LOCK_ENABLED=true` on the process running the
ToolHive API server (either the `thv serve` shell or the ToolHive desktop app)
before using any lock-file command. Without it, sync and upgrade fail with:

```text
skills lock file support is experimental; set TOOLHIVE_SKILLS_LOCK_ENABLED=true to use it
```

Behavior, flag names, and file format may change before the feature stabilizes.

:::

If you install project-scoped skills across a team or a CI environment, you
usually want everyone (and every build) to end up with exactly the same skill
content. ToolHive records project-scope installs in a `toolhive.lock.yaml` file
at the project root; `thv skill sync` restores that pinned set on another
machine, and `thv skill upgrade` re-resolves pinned entries to newer content in
a controlled way.

The lock file sits next to `.git` in the project root and pins each skill's
resolved reference, digest, content digest, and (when available) Sigstore
provenance. Commit it to the repository like `package-lock.json` or `go.sum`.

### Restore pinned skills with `thv skill sync`

`thv skill sync` reads `toolhive.lock.yaml` from the project root, compares it
to what is currently installed, and reinstalls anything that's missing or has
drifted, restoring each entry to its pinned digest:

```bash
export TOOLHIVE_SKILLS_LOCK_ENABLED=true
thv skill sync --project-root /path/to/project
```

Because skill content is a set of instructions that AI agents follow, sync
prompts for confirmation before installing anything. In CI, pass `--yes` to skip
the prompt (required when stdin isn't a terminal):

```bash
thv skill sync --project-root . --yes
```

A few flags are worth knowing for CI use:

- `--check` reports drift without installing, writing, or removing anything. Use
this as a freshness gate.
- `--adopt` writes lock entries for existing project-scope installs that aren't
tracked yet; use it when onboarding an existing project.
- `--prune` removes installs no longer present in the lock file.
- `--clients <csv>` restricts sync to specific client apps (for example,
`claude-code,cursor`), or `all` for every available client.

### Upgrade pinned skills with `thv skill upgrade`

`thv skill upgrade` re-resolves lock entries and installs newer content where
available. Immutable references (an OCI digest or a full git commit hash) are
reported as not-upgradable because there is nothing newer to resolve to.

```bash
thv skill upgrade --project-root /path/to/project
```

Preview a plan without persisting anything (OCI sources are still fetched into
the local artifact store so digests can be compared):

```bash
thv skill upgrade --preview
```

`--fail-on-changes` evaluates the same plan but never installs; use it as a CI
freshness gate that fails when the pinned set is not up to date.
`--allow-ref-change` permits the resolved reference itself to change (for
example, when a registry entry has been repointed at a different repository);
without it, ref changes are refused for safety.

### Typed exit codes for CI

Both `thv skill sync` and `thv skill upgrade` use typed exit codes so CI
pipelines can distinguish outcomes:

| Exit code | Meaning |
| --------- | ------------------------------------------------------------------------------------------------------- |
| `0` | Success |
| `1` | Command usage or setup error (standard Cobra/CLI default) |
| `2` | Check failure: `sync --check` or `upgrade --fail-on-changes` found drift; nothing was applied |
| `3` | Partial failure: some (not all) targeted skills failed during sync or upgrade |
| `4` | Policy rejection: refused non-interactively without `--yes`, or ref change without `--allow-ref-change` |

When multiple failures apply, partial failure (`3`) takes precedence over check
failure (`2`), which takes precedence over policy rejection (`4`).

## List and remove locally-built skill artifacts

After building skills locally, you can view and manage the artifacts stored in
Expand Down Expand Up @@ -377,6 +473,8 @@ pointing to that digest are removed.
- [Manage agent skills in the ToolHive UI](../guides-ui/skills.mdx) to browse,
install, and build skills without leaving the app
- [`thv skill` command reference](../reference/cli/thv_skill.md)
- [`thv skill sync` command reference](../reference/cli/thv_skill_sync.md)
- [`thv skill upgrade` command reference](../reference/cli/thv_skill_upgrade.md)
- [`thv serve` command reference](../reference/cli/thv_serve.md)
- [Agent Skills specification](https://agentskills.io/specification)

Expand Down
Loading