Skip to content

Commit 787a36b

Browse files
reyortiz3clauderdimitrov
authored
Update docs for ToolHive v0.19.0 (#704)
* Update docs for ToolHive v0.19.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Address PR review comments Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix webhook url/insecure_skip_verify table descriptions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Address editorial review comments Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix webhook docs: patch path prefix and hmac_secret_ref status - Fix JSON Patch example path from /params/context to /mcp_request/params/context (required by middleware) - Note that hmac_secret_ref is not yet implemented upstream Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
1 parent 492b960 commit 787a36b

7 files changed

Lines changed: 339 additions & 39 deletions

File tree

docs/toolhive/guides-cli/skills-management.mdx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,31 @@ thv skill install my-skill --force
113113

114114
If you have multiple supported clients, ToolHive installs the skill for the
115115
first one it detects. To control which client receives the skill, use the
116-
`--client` flag:
116+
`--clients` flag:
117117

118118
```bash
119-
thv skill install my-skill --client claude-code
119+
thv skill install my-skill --clients claude-code
120120
```
121121

122+
### Install to multiple clients at once
123+
124+
You can install a skill to multiple clients in a single command by
125+
comma-separating client names:
126+
127+
```bash
128+
thv skill install my-skill --clients claude-code,cursor
129+
```
130+
131+
To install to every skill-supporting client at once, use the special `all`
132+
value:
133+
134+
```bash
135+
thv skill install my-skill --clients all
136+
```
137+
138+
If any client installation fails, ToolHive rolls back all changes for that
139+
install operation.
140+
122141
See the [client compatibility reference](../reference/client-compatibility.mdx)
123142
for the full list of clients that support skills.
124143

@@ -391,9 +410,11 @@ If your AI client doesn't see an installed skill:
391410
thv skill info <SKILL_NAME>
392411
```
393412

394-
3. Verify the skill files exist in the expected directory. For Claude Code,
395-
user-scoped skills are at `~/.claude/skills/<SKILL_NAME>/` and project-scoped
396-
skills are at `<PROJECT_ROOT>/.claude/skills/<SKILL_NAME>/`.
413+
3. Verify the skill files exist in the expected directory. For example:
414+
- **Claude Code**: `~/.claude/skills/<SKILL_NAME>/` (user) or
415+
`<PROJECT_ROOT>/.claude/skills/<SKILL_NAME>/` (project)
416+
- **Cursor**: `~/.cursor/skills/<SKILL_NAME>/` (user) or
417+
`<PROJECT_ROOT>/.cursor/skills/<SKILL_NAME>/` (project)
397418

398419
4. Restart your AI client to trigger skill discovery.
399420

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
---
2+
title: Delegate tool authorization with webhooks
3+
sidebar_label: Webhook authorization
4+
description:
5+
Configure webhook middleware to delegate MCP tool call authorization to an
6+
external HTTP service when running MCP servers with the ToolHive CLI.
7+
---
8+
9+
Webhook middleware lets you delegate MCP tool call authorization to an external
10+
HTTP service. When a client calls an MCP tool, ToolHive sends a request to your
11+
webhook endpoint, which decides whether to allow or deny the call, and
12+
optionally modify it.
13+
14+
Use webhooks when your authorization logic is too complex for static Cedar
15+
policies, or when you need to enforce rules managed by an external system (such
16+
as a policy engine or an OPA server).
17+
18+
## Prerequisites
19+
20+
- The ToolHive CLI installed. See [Install ToolHive](./install.mdx).
21+
- An HTTP endpoint that accepts webhook requests and returns allow/deny
22+
responses in the ToolHive webhook format.
23+
24+
## How it works
25+
26+
When webhook middleware is active, every incoming MCP tool call passes through
27+
two middleware types in order:
28+
29+
1. **Mutating webhooks** can transform the request before it reaches the MCP
30+
server (for example, to add context or rewrite arguments)
31+
2. **Validating webhooks** accept or deny the (possibly mutated) request
32+
33+
If a validating webhook denies the request, ToolHive returns an error to the
34+
client without calling the MCP server.
35+
36+
## Create a webhook configuration file
37+
38+
Webhook configuration is defined in a YAML or JSON file. The file has two
39+
top-level keys: `validating` and `mutating`. Each key maps to a list of webhook
40+
definitions.
41+
42+
```yaml title="webhooks.yaml"
43+
validating:
44+
- name: policy-check
45+
url: https://policy.example.com/validate
46+
failure_policy: fail
47+
timeout: 5s
48+
tls_config:
49+
ca_bundle_path: /etc/toolhive/pki/webhook-ca.crt
50+
51+
mutating:
52+
- name: request-enricher
53+
url: https://enrichment.example.com/mutate
54+
failure_policy: ignore
55+
# Omitting timeout uses the default of 10s.
56+
tls_config:
57+
insecure_skip_verify: true
58+
```
59+
60+
### Webhook fields
61+
62+
| Field | Required | Description |
63+
| ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
64+
| `name` | Yes | Unique identifier for this webhook. Used for deduplication when merging multiple config files. |
65+
| `url` | Yes | HTTPS endpoint to call. Plain HTTP is accepted for in-cluster or development use (see `insecure_skip_verify` below). |
66+
| `failure_policy` | Yes | `fail` (deny the request on webhook error) or `ignore` (allow through on error). |
67+
| `timeout` | No | Maximum wait time for a response. Accepts duration strings like `5s` or `30s`. Minimum: `1s`, maximum: `30s`. Default: `10s`. |
68+
| `tls_config` | No | TLS options for the webhook HTTP client (see below). |
69+
| `hmac_secret_ref` | No | Environment variable name containing an HMAC secret for payload signing. **Not yet implemented** - accepted in config but currently has no effect. |
70+
71+
### TLS configuration
72+
73+
| Field | Description |
74+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
75+
| `ca_bundle_path` | Path to a CA certificate bundle for server certificate verification. |
76+
| `client_cert_path` | Path to a client certificate for mutual TLS (mTLS). |
77+
| `client_key_path` | Path to the client private key for mTLS. Both `client_cert_path` and `client_key_path` must be set together. |
78+
| `insecure_skip_verify` | Disable TLS certificate verification for HTTPS connections, and also allow plain HTTP endpoint URLs. Use only in development or trusted in-cluster environments. |
79+
80+
### JSON format
81+
82+
The same configuration works in JSON format. Timeout values can be either a
83+
duration string (`"5s"`) or a numeric value in nanoseconds:
84+
85+
```json title="webhooks.json"
86+
{
87+
"validating": [
88+
{
89+
"name": "policy-check",
90+
"url": "https://policy.example.com/validate",
91+
"failure_policy": "fail",
92+
"timeout": "5s",
93+
"tls_config": {
94+
"ca_bundle_path": "/etc/toolhive/pki/webhook-ca.crt"
95+
}
96+
}
97+
],
98+
"mutating": [
99+
{
100+
"name": "request-enricher",
101+
"url": "https://enrichment.example.com/mutate",
102+
"failure_policy": "ignore",
103+
"tls_config": {
104+
"insecure_skip_verify": true
105+
}
106+
}
107+
]
108+
}
109+
```
110+
111+
## Run an MCP server with webhook middleware
112+
113+
Pass your webhook configuration file to `thv run` using the `--webhook-config`
114+
flag:
115+
116+
```bash
117+
thv run fetch --webhook-config /path/to/webhooks.yaml
118+
```
119+
120+
You can specify `--webhook-config` multiple times to merge configurations from
121+
several files. If two files define a webhook with the same `name`, the last file
122+
takes precedence:
123+
124+
```bash
125+
thv run fetch \
126+
--webhook-config /etc/toolhive/base-webhooks.yaml \
127+
--webhook-config /etc/toolhive/team-webhooks.yaml
128+
```
129+
130+
ToolHive validates all webhook configurations at startup and exits with an error
131+
if any are invalid, so configuration problems surface before the server starts.
132+
133+
## Failure policies
134+
135+
The `failure_policy` field controls what happens when ToolHive cannot reach the
136+
webhook endpoint:
137+
138+
- `fail` denies the MCP tool call. Use this when your webhook is authoritative
139+
and a connectivity failure should be treated as a security event.
140+
- `ignore` allows the tool call through. Use this for non-critical webhooks like
141+
logging or enrichment, where availability is not a hard requirement.
142+
143+
:::warning
144+
145+
A `422 Unprocessable Entity` response from a webhook is always treated as a
146+
deny, regardless of the `failure_policy`. This prevents malformed payloads from
147+
accidentally being allowed through.
148+
149+
:::
150+
151+
## Webhook request format
152+
153+
ToolHive sends a JSON `POST` request to your webhook URL with this structure:
154+
155+
```json
156+
{
157+
"version": "v0.1.0",
158+
"uid": "550e8400-e29b-41d4-a716-446655440000",
159+
"timestamp": "2025-04-13T10:15:30.123Z",
160+
"principal": {
161+
"sub": "user@example.com",
162+
"email": "user@example.com"
163+
},
164+
"mcp_request": { ... },
165+
"context": {
166+
"server_name": "fetch",
167+
"source_ip": "127.0.0.1",
168+
"transport": "streamable-http"
169+
}
170+
}
171+
```
172+
173+
## Validating webhook response format
174+
175+
Your validating webhook must respond with HTTP 200 and a JSON body:
176+
177+
```json
178+
{
179+
"version": "v0.1.0",
180+
"uid": "550e8400-e29b-41d4-a716-446655440000",
181+
"allowed": true
182+
}
183+
```
184+
185+
To deny a request, set `"allowed": false` and optionally include a `message` and
186+
`reason`:
187+
188+
```json
189+
{
190+
"version": "v0.1.0",
191+
"uid": "550e8400-e29b-41d4-a716-446655440000",
192+
"allowed": false,
193+
"message": "Tool call denied by policy",
194+
"reason": "insufficient_permissions"
195+
}
196+
```
197+
198+
## Mutating webhook response format
199+
200+
Your mutating webhook must respond with HTTP 200. To pass the request through
201+
unchanged, return an allow response with no patch:
202+
203+
```json
204+
{
205+
"version": "v0.1.0",
206+
"uid": "550e8400-e29b-41d4-a716-446655440000",
207+
"allowed": true
208+
}
209+
```
210+
211+
To modify the request, include a `patch_type` and a `patch` containing
212+
[JSON Patch](https://jsonpatch.com/) operations. All patch paths must be
213+
prefixed with `/mcp_request/` because the middleware wraps the MCP body in an
214+
envelope before applying patches:
215+
216+
```json
217+
{
218+
"version": "v0.1.0",
219+
"uid": "550e8400-e29b-41d4-a716-446655440000",
220+
"allowed": true,
221+
"patch_type": "json_patch",
222+
"patch": [
223+
{
224+
"op": "add",
225+
"path": "/mcp_request/params/context",
226+
"value": "injected-by-webhook"
227+
}
228+
]
229+
}
230+
```
231+
232+
A mutating webhook can also deny a request by setting `"allowed": false`, in
233+
which case `patch_type` and `patch` are ignored.
234+
235+
## Next steps
236+
237+
- [Custom permissions](./custom-permissions.mdx) for Cedar-based authorization
238+
policies for MCP servers
239+
- [Authentication](./auth.mdx) to set up OIDC authentication for MCP servers
240+
241+
## Related information
242+
243+
- [`thv run` command reference](../reference/cli/thv_run.md)

0 commit comments

Comments
 (0)