Skip to content

Commit b9da28d

Browse files
rdimitrovclaude
andcommitted
Update docs for ToolHive v0.12.3–v0.13.0
Catch up documentation with features shipped in v0.12.3 through v0.13.0. Auto-generated CLI/CRD reference docs were already current; these changes cover manual doc updates verified against source code at each release tag. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e917bd4 commit b9da28d

9 files changed

Lines changed: 258 additions & 46 deletions

File tree

docs/toolhive/concepts/backend-auth.mdx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,25 @@ deployments using the ToolHive Operator.
211211
- **Direct upstream redirect:** The embedded authorization server redirects
212212
clients directly to the upstream provider for authentication (for example,
213213
GitHub or Atlassian).
214-
- **Single upstream provider:** Currently supports one upstream identity
215-
provider per configuration.
216-
217-
:::info[Chained authentication not yet supported]
218-
219-
The embedded authorization server redirects clients directly to the upstream
220-
provider. This means the upstream provider must be the service whose API the MCP
221-
server calls. Chained authentication—where a client authenticates with a
214+
- **Multiple upstream providers (VirtualMCPServer):** VirtualMCPServer supports
215+
configuring multiple upstream identity providers with sequential
216+
authentication. When multiple providers are configured, the authorization
217+
server chains the authentication flow through each provider in sequence,
218+
collecting tokens from all of them. This enables scenarios where backend tools
219+
require tokens from different providers (such as a corporate IdP and
220+
GitHub). MCPServer and MCPRemoteProxy support a single upstream provider per
221+
configuration.
222+
223+
:::info[Chained authentication for MCPServer]
224+
225+
MCPServer and MCPRemoteProxy support only one upstream provider. The embedded
226+
authorization server redirects clients directly to that provider, so the
227+
provider must be the service whose API the MCP server calls. If your MCPServer
228+
deployment requires chained authentication—where a client authenticates with a
222229
corporate IdP like Okta, which then federates to an external provider like
223-
GitHub—is not yet supported. If your deployment requires this pattern, consider
224-
using [token exchange](#same-idp-with-token-exchange) with a federated identity
225-
provider instead.
230+
GitHub—consider using
231+
[token exchange](#same-idp-with-token-exchange) with a federated identity
232+
provider instead, or use a VirtualMCPServer with multiple upstream providers.
226233

227234
:::
228235

docs/toolhive/concepts/skills.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,17 @@ an older version does not change the latest pointer.
9595
You can retrieve a specific version or request `latest` to get the most recent
9696
one.
9797

98-
## Current status and what's next
98+
## Current status
9999

100100
The skills API is available as an extension endpoint on the Registry server
101101
(`/{registryName}/v0.1/x/dev.toolhive/skills`). You can publish, list, search,
102102
retrieve, and delete skills through this API.
103103

104-
Skill installation via agent clients (such as the ToolHive CLI or IDE
105-
extensions) is planned for a future release. For now, the registry serves as a
106-
discovery and distribution layer where you can browse available skills and
107-
retrieve their package references.
104+
The ToolHive CLI supports installing skills with `thv skill install`. You can
105+
install skills by plain name (resolved from the registry) or from a git
106+
repository. See
107+
[Manage skills](../guides-registry/skills.mdx#install-skills-with-the-cli) for
108+
details and examples.
108109

109110
## Next steps
110111

docs/toolhive/guides-k8s/auth-k8s.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ kubectl apply -f embedded-auth-config.yaml
470470
| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. |
471471
| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. |
472472
| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). |
473-
| `upstreamProviders` | Configuration for the upstream identity provider. Currently supports one provider. |
473+
| `upstreamProviders` | Configuration for upstream identity providers. MCPServer and MCPRemoteProxy support one provider; VirtualMCPServer supports multiple providers for sequential authentication. |
474474

475475
**Step 5: Create the MCPServer resource**
476476

docs/toolhive/guides-k8s/redis-session-storage.mdx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Redis Sentinel session storage
33
description:
44
How to deploy Redis Sentinel and configure persistent session storage for the
5-
ToolHive embedded authorization server.
5+
ToolHive embedded authorization server and horizontal scaling.
66
---
77

88
Deploy Redis Sentinel and configure it as the session storage backend for the
@@ -12,6 +12,11 @@ re-authenticate. Redis Sentinel provides persistent storage with automatic
1212
master discovery, ACL-based access control, and optional failover when replicas
1313
are configured.
1414

15+
Redis session storage is also required for
16+
[horizontal scaling](../guides-vmcp/scaling-and-performance.mdx#session-storage-for-multi-replica-deployments)
17+
when running multiple MCPServer or VirtualMCPServer replicas, so that sessions
18+
are shared across pods.
19+
1520
:::info[Prerequisites]
1621

1722
Before you begin, ensure you have:

docs/toolhive/guides-k8s/run-mcp-k8s.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ kubectl -n <NAMESPACE> describe mcpserver <NAME>
455455

456456
- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) -
457457
Reference for the `MCPServer` Custom Resource Definition (CRD)
458+
- [Scaling and performance](../guides-vmcp/scaling-and-performance.mdx#mcpserver-horizontal-scaling) -
459+
Configure horizontal scaling with `replicas` and `backendReplicas`
458460
- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator
459461
- [Build MCP containers](../guides-cli/build-containers.mdx) - Create custom MCP
460462
server container images

docs/toolhive/guides-registry/skills.mdx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,45 @@ The API returns standard HTTP status codes:
189189
| 409 | Version already exists |
190190
| 500 | Internal server error |
191191

192-
## Next steps
192+
## Install skills with the CLI
193+
194+
The ToolHive CLI can install skills directly from the registry or from git
195+
repositories.
196+
197+
### Install from the registry
198+
199+
Install a skill by its plain name. The CLI resolves the name against the
200+
configured registry:
201+
202+
```bash
203+
thv skill install <SKILL_NAME>
204+
```
205+
206+
For example:
207+
208+
```bash
209+
thv skill install osv
210+
```
211+
212+
### Install from a git repository
213+
214+
Install a skill from a git repository using the `git://` prefix:
215+
216+
```bash
217+
thv skill install git://<REPOSITORY_URL>
218+
```
219+
220+
### CLI flags
193221

194-
Skill installation via agent clients (such as the ToolHive CLI or IDE
195-
extensions) is planned for a future release. For now, the registry serves as a
196-
discovery and distribution layer.
222+
| Flag | Description |
223+
| ---------------- | ------------------------------------------------------ |
224+
| `--client` | Target client application (for example, `claude-code`) |
225+
| `--scope` | Installation scope: `user` or `project` |
226+
| `--force` | Overwrite an existing skill directory |
227+
| `--project-root` | Project root path for project-scoped installs |
228+
| `--group` | Group to add the skill to (defaults to `default`) |
229+
230+
## Next steps
197231

198232
- [Configure telemetry](./telemetry-metrics.mdx) to monitor your registry
199233
deployment

docs/toolhive/guides-vmcp/composite-tools.mdx

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ backend MCP servers, handling dependencies and collecting results.
1919
wait for their prerequisites
2020
- **Template expansion**: Dynamic arguments using step outputs
2121
- **Elicitation**: Request user input mid-workflow (approval gates, choices)
22+
- **Iteration**: Loop over collections with forEach steps
2223
- **Error handling**: Configurable abort, continue, or retry behavior
2324
- **Timeouts**: Workflow and per-step timeout configuration
2425

@@ -290,7 +291,7 @@ spec:
290291

291292
### Steps
292293

293-
Each step can be a tool call or an elicitation:
294+
Each step can be a tool call, an elicitation, or a forEach loop:
294295

295296
```yaml title="VirtualMCPServer resource"
296297
spec:
@@ -344,6 +345,61 @@ spec:
344345
timeout: '5m'
345346
```
346347

348+
### forEach steps
349+
350+
Iterate over a collection from a previous step's output and execute a tool call
351+
for each item:
352+
353+
```yaml title="VirtualMCPServer resource"
354+
spec:
355+
config:
356+
compositeTools:
357+
- name: scan_repositories
358+
description: Check each repository for security advisories
359+
parameters:
360+
type: object
361+
properties:
362+
org:
363+
type: string
364+
required:
365+
- org
366+
steps:
367+
- id: list_repos
368+
tool: github_list_repos
369+
arguments:
370+
org: '{{.params.org}}'
371+
# highlight-start
372+
- id: check_advisories
373+
type: forEach
374+
collection: '{{json .steps.list_repos.output.repositories}}'
375+
itemVar: repo
376+
maxParallel: 5
377+
step:
378+
type: tool
379+
tool: github_list_security_advisories
380+
arguments:
381+
repo: '{{.forEach.repo.name}}'
382+
onError:
383+
action: continue
384+
dependsOn: [list_repos]
385+
# highlight-end
386+
```
387+
388+
**forEach fields:**
389+
390+
| Field | Description | Default |
391+
| --------------- | --------------------------------------------------- | ------- |
392+
| `collection` | Template expression that produces an array | — |
393+
| `itemVar` | Variable name for the current item | — |
394+
| `maxParallel` | Maximum concurrent iterations (max 50) | 10 |
395+
| `maxIterations` | Maximum total iterations (max 1000) | 100 |
396+
| `step` | Inner step definition (tool call to execute per item) | — |
397+
| `onError` | Error handling: `abort` (stop) or `continue` (skip) | abort |
398+
399+
Access the current item inside the inner step using
400+
`{{.forEach.<itemVar>.<field>}}`. In the example above, `{{.forEach.repo.name}}`
401+
accesses the `name` field of the current repository.
402+
347403
### Error handling
348404

349405
Configure behavior when steps fail:
@@ -507,13 +563,15 @@ without defaultResults defined
507563

508564
Access workflow context in arguments:
509565

510-
| Template | Description |
511-
| --------------------------- | ------------------------------------------ |
512-
| `{{.params.name}}` | Input parameter |
513-
| `{{.steps.id.output}}` | Step output (map) |
514-
| `{{.steps.id.output.text}}` | Text content from step output |
515-
| `{{.steps.id.content}}` | Elicitation response content |
516-
| `{{.steps.id.action}}` | Elicitation action (accept/decline/cancel) |
566+
| Template | Description |
567+
| --------------------------------- | ------------------------------------------ |
568+
| `{{.params.name}}` | Input parameter |
569+
| `{{.steps.id.output}}` | Step output (map) |
570+
| `{{.steps.id.output.text}}` | Text content from step output |
571+
| `{{.steps.id.content}}` | Elicitation response content |
572+
| `{{.steps.id.action}}` | Elicitation action (accept/decline/cancel) |
573+
| `{{.forEach.<itemVar>}}` | Current forEach item |
574+
| `{{.forEach.<itemVar>.<field>}}` | Field on current forEach item |
517575

518576
### Template functions
519577

docs/toolhive/guides-vmcp/scaling-and-performance.mdx

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
---
22
title: Scaling and Performance
33
description:
4-
How to scale Virtual MCP Server deployments vertically and horizontally.
4+
How to scale MCPServer and Virtual MCP Server deployments vertically and
5+
horizontally.
56
---
67

7-
This guide explains how to scale Virtual MCP Server (vMCP) deployments.
8+
This guide explains how to scale MCPServer and Virtual MCP Server (vMCP)
9+
deployments.
810

911
## Vertical scaling
1012

@@ -37,24 +39,91 @@ higher request volumes.
3739
3840
### How to scale horizontally
3941
40-
The VirtualMCPServer CRD does not have a `replicas` field. The operator creates
41-
a Deployment named `vmcp-<NAME>` (where `<NAME>` is your VirtualMCPServer name)
42-
with 1 replica and preserves the replicas count, allowing you to manage scaling
43-
separately.
42+
Set the `replicas` field in your VirtualMCPServer spec to control the number of
43+
vMCP pods:
44+
45+
```yaml title="VirtualMCPServer resource"
46+
spec:
47+
replicas: 3
48+
```
49+
50+
When `replicas` is not set, the operator does not manage the replica count,
51+
leaving it to an HPA or other external controller. You can also scale manually
52+
or with an HPA:
4453

4554
**Option 1: Manual scaling**
4655

4756
```bash
48-
kubectl scale deployment vmcp-<vmcp-name> -n <NAMESPACE> --replicas=3
57+
kubectl scale deployment vmcp-<VMCP_NAME> -n <NAMESPACE> --replicas=3
4958
```
5059

5160
**Option 2: Autoscaling with HPA**
5261

5362
```bash
54-
kubectl autoscale deployment vmcp-<vmcp-name> -n <NAMESPACE> \
63+
kubectl autoscale deployment vmcp-<VMCP_NAME> -n <NAMESPACE> \
5564
--min=2 --max=5 --cpu-percent=70
5665
```
5766

67+
### Session storage for multi-replica deployments
68+
69+
When running multiple replicas, configure Redis session storage so that sessions
70+
are shared across pods. Without session storage, a request routed to a different
71+
replica than the one that established the session will fail.
72+
73+
```yaml title="VirtualMCPServer resource"
74+
spec:
75+
replicas: 3
76+
sessionStorage:
77+
provider: redis
78+
address: redis-master.toolhive-system.svc.cluster.local:6379
79+
db: 0
80+
keyPrefix: vmcp-sessions
81+
passwordRef:
82+
name: redis-secret
83+
key: password
84+
```
85+
86+
See
87+
[Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx)
88+
for a complete Redis deployment guide.
89+
90+
:::warning
91+
92+
The operator warns if you configure multiple replicas without session storage.
93+
Ensure Redis is available before scaling beyond a single replica.
94+
95+
:::
96+
97+
### MCPServer horizontal scaling
98+
99+
MCPServer creates two separate Deployments: one for the proxy runner and one for
100+
the MCP server backend. You can scale each independently:
101+
102+
- `spec.replicas` controls the proxy runner pod count
103+
- `spec.backendReplicas` controls the backend MCP server pod count
104+
105+
```yaml title="MCPServer resource"
106+
spec:
107+
replicas: 2
108+
backendReplicas: 3
109+
sessionStorage:
110+
provider: redis
111+
address: redis-master.toolhive-system.svc.cluster.local:6379
112+
db: 0
113+
keyPrefix: mcp-sessions
114+
passwordRef:
115+
name: redis-secret
116+
key: password
117+
```
118+
119+
:::warning[Stdio transport limitation]
120+
121+
Backends using the `stdio` transport are limited to a single replica. The
122+
operator rejects configurations with `backendReplicas` greater than 1 for stdio
123+
backends.
124+
125+
:::
126+
58127
### When horizontal scaling is challenging
59128

60129
Horizontal scaling works well for **stateless backends** (fetch, search,
@@ -63,22 +132,22 @@ read-only operations) where sessions can be resumed on any instance.
63132
However, **stateful backends** make horizontal scaling difficult:
64133

65134
- **Stateful backends** (Playwright browser sessions, database connections, file
66-
system operations) require requests to be routed to the same vMCP instance
67-
that established the session
135+
system operations) require requests to be routed to the same instance that
136+
established the session
68137
- Session resumption may not work reliably for stateful backends
69138

70-
The `VirtualMCPServer` CRD includes a `sessionAffinity` field that controls how
71-
the Kubernetes Service routes repeated client connections. By default, it uses
72-
`ClientIP` affinity, which routes connections from the same client IP to the
73-
same pod. You can configure this using the `sessionAffinity` field:
139+
The `VirtualMCPServer` and `MCPServer` CRDs include a `sessionAffinity` field
140+
that controls how the Kubernetes Service routes repeated client connections. By
141+
default, it uses `ClientIP` affinity, which routes connections from the same
142+
client IP to the same pod:
74143

75144
```yaml
76145
spec:
77146
sessionAffinity: ClientIP # default
78147
```
79148

80-
For stateful backends, vertical scaling or dedicated vMCP instances per team/use
81-
case are recommended instead of horizontal scaling.
149+
For stateful backends, vertical scaling or dedicated instances per team/use case
150+
are recommended instead of horizontal scaling.
82151

83152
## Next steps
84153

0 commit comments

Comments
 (0)