Skip to content

Commit 703714a

Browse files
committed
Update for v1.0.1
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
1 parent 95b96ff commit 703714a

6 files changed

Lines changed: 103 additions & 50 deletions

File tree

docs/toolhive/guides-registry/authorization.mdx

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,15 @@ claims satisfy the resource's claims can access it.
123123

124124
### Source claims
125125

126-
Claims on a source are **inherited by all entries** during sync. This means
127-
every MCP server or skill ingested from that source carries the source's claims.
126+
For synced sources (Git, API, File), claims on a source are **inherited by all
127+
entries** during sync. Every MCP server or skill ingested from that source
128+
carries the source's claims.
129+
130+
For Kubernetes and managed sources, source claims control who can manage the
131+
source via the admin API but are **not** inherited by entries. Kubernetes
132+
entries get claims from the
133+
[`authz-claims` annotation](./configuration.mdx#per-entry-claims-via-annotation)
134+
on each CRD. Managed source entries get claims from the publish request payload.
128135

129136
```yaml title="config-source-claims.yaml"
130137
sources:
@@ -200,7 +207,15 @@ caller's claims must be a superset of the resource's claims. For example:
200207
| `{}` (no claims) | `{org: "acme"}` | Allowed |
201208
| `{org: "acme"}` | `{org: "contoso"}` | Denied |
202209

203-
Resources with no claims are accessible to all authenticated callers.
210+
Registries and sources with no claims are accessible to all authenticated
211+
callers. However, **entries** with no claims behave differently: they are only
212+
visible in anonymous mode. When authorization is enabled, an authenticated
213+
caller's per-entry filter requires both sides to have claims for a match — so
214+
entries without claims are invisible. To make entries visible to authenticated
215+
callers, attach claims to the source (for synced sources) or to individual
216+
entries (via the publish payload or the
217+
[`authz-claims` annotation](./configuration.mdx#per-entry-claims-via-annotation)
218+
for Kubernetes sources).
204219

205220
## Claims on published entries
206221

@@ -257,6 +272,28 @@ bypassed. There are no JWT claims to validate, so all sources, registries, and
257272
entries are accessible without restriction. This is suitable for development and
258273
testing environments only.
259274

275+
## Check your identity and permissions
276+
277+
Use the `GET /v1/me` endpoint to verify your authenticated identity and resolved
278+
roles:
279+
280+
```bash
281+
curl -H "Authorization: Bearer $TOKEN" \
282+
https://registry.example.com/v1/me
283+
```
284+
285+
```json title="Example response"
286+
{
287+
"subject": "user@example.com",
288+
"roles": ["manageSources", "manageEntries"]
289+
}
290+
```
291+
292+
This is useful for debugging authorization issues — you can confirm which roles
293+
your JWT grants and whether the expected claims are present. The endpoint
294+
returns `401 Unauthorized` in anonymous mode since there is no identity to
295+
report.
296+
260297
## Complete example
261298

262299
This example shows a multi-team setup with full RBAC and claims-based scoping:

docs/toolhive/guides-registry/configuration.mdx

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ You can create multiple registries from the same sources to serve different
8282
audiences. For example, a `public` registry with no claims and a `team-internal`
8383
registry with team-scoped claims — both backed by the same source data.
8484

85+
Source names must be valid DNS subdomain labels: lowercase alphanumeric
86+
characters and hyphens, maximum 63 characters (for example, `toolhive`,
87+
`platform-tools`, `k8s-prod`). This is required because source names are used as
88+
Kubernetes lease name suffixes for leader election.
89+
8590
## Command-line flags
8691

8792
| Flag | Description | Required | Default |
@@ -354,21 +359,18 @@ registry entries for deployed MCP servers in your cluster.
354359

355360
:::note[Operator-managed source]
356361

357-
When using the ToolHive operator, a single Kubernetes source named `default` is
358-
automatically created and managed. You cannot configure additional Kubernetes
359-
sources through the `MCPRegistry` CR or configuration file, as only one
360-
Kubernetes source instance is supported per Registry Server instance.
361-
362-
The configuration example below is for reference when running the Registry
363-
Server standalone (without the operator).
362+
When using the ToolHive operator, a Kubernetes source named `default` is
363+
automatically created and managed. The configuration examples below are for
364+
reference when running the Registry Server standalone (without the operator).
364365

365366
:::
366367

367-
By default, the Registry server discovers resources in all namespaces
368-
(cluster-wide). You can restrict discovery to specific namespaces using the
369-
`THV_REGISTRY_WATCH_NAMESPACE` environment variable. See
370-
[Workload discovery](./deploy-manual.mdx#workload-discovery) for configuration
371-
details and RBAC requirements.
368+
You can configure multiple Kubernetes sources, each watching different
369+
namespaces. By default, a Kubernetes source discovers resources in all
370+
namespaces (cluster-wide). You can restrict discovery to specific namespaces
371+
using the `namespaces` field or the `THV_REGISTRY_WATCH_NAMESPACE` environment
372+
variable. See [Workload discovery](./deploy-manual.mdx#workload-discovery) for
373+
RBAC requirements.
372374

373375
```yaml title="config-kubernetes.yaml"
374376
sources:
@@ -383,35 +385,45 @@ registries:
383385

384386
**Configuration options:**
385387

386-
- `kubernetes` (required): Empty object or claim mapping configuration (see
387-
below)
388+
- `kubernetes` (required): Empty object or namespace configuration (see below)
389+
- `kubernetes.namespaces` (optional): List of Kubernetes namespaces to watch. If
390+
empty, falls back to the `THV_REGISTRY_WATCH_NAMESPACE` environment variable,
391+
or all namespaces if neither is set
388392
- No sync policy required (Kubernetes sources query live deployments on-demand)
389-
- Only one Kubernetes source is supported per Registry Server instance
390393

391-
#### Claim mapping
394+
#### Per-entry claims via annotation
392395

393-
You can map Kubernetes labels or annotations to authorization claims using the
394-
`claimMapping` field. This allows entries discovered from Kubernetes workloads
395-
to inherit claims based on their metadata — for example, mapping a `team` label
396-
to a claim so that only members of that team can see the entry.
396+
You can set authorization claims on individual Kubernetes workloads using the
397+
`toolhive.stacklok.dev/authz-claims` JSON annotation. This controls which
398+
authenticated callers can see each entry.
397399

398-
```yaml title="config-kubernetes-claims.yaml"
399-
sources:
400-
- name: default
401-
format: toolhive
402-
kubernetes:
403-
claimMapping:
404-
'toolhive.stacklok.dev/team': 'team'
405-
406-
registries:
407-
- name: default
408-
sources: ['default']
400+
```yaml title="mcp-server-with-claims.yaml"
401+
apiVersion: toolhive.stacklok.dev/v1alpha1
402+
kind: MCPServer
403+
metadata:
404+
name: deploy-helper
405+
annotations:
406+
toolhive.stacklok.dev/registry-export: 'true'
407+
toolhive.stacklok.dev/registry-url: 'https://mcp.example.com/deploy-helper'
408+
toolhive.stacklok.dev/registry-description: 'Deployment assistant'
409+
# highlight-next-line
410+
toolhive.stacklok.dev/authz-claims: '{"org": "acme", "team": "platform"}'
411+
spec:
412+
# ...
409413
```
410414

411-
In this example, a workload annotated with
412-
`toolhive.stacklok.dev/team: platform` would produce entries with
413-
`claims: {team: "platform"}`. See [Authorization](./authorization.mdx) for how
414-
claims control entry visibility.
415+
Entry claims come exclusively from the annotation — source-level claims are
416+
**not** inherited. Entries without the annotation have no claims, which means
417+
they are visible in anonymous mode but invisible when authorization is
418+
configured.
419+
420+
If the annotation contains invalid JSON or unsupported claim value types
421+
(anything other than strings or arrays of strings), the entry is **skipped
422+
entirely** and a warning is logged. The entry will not sync until the annotation
423+
is fixed.
424+
425+
See [Authorization](./authorization.mdx) for how claims control entry
426+
visibility.
415427

416428
:::info[How does it work?]
417429

@@ -454,6 +466,7 @@ spec:
454466
| `toolhive.stacklok.dev/registry-title` | No | Human-friendly display name for the registry entry (overrides the generated name) |
455467
| `toolhive.stacklok.dev/tools` | No | JSON array of tool name strings (e.g., `["get_weather","get_forecast"]`) |
456468
| `toolhive.stacklok.dev/tool-definitions` | No | JSON array of tool definitions with MCP tool metadata (name, description, schema) |
469+
| `toolhive.stacklok.dev/authz-claims` | No | JSON object of authorization claims for per-entry visibility control |
457470

458471
**Tool definitions format:**
459472

@@ -492,8 +505,8 @@ The registry URL from the `registry-url` annotation becomes a key in the JSON
492505
structure.
493506

494507
This feature requires the Registry server to be granted access to those
495-
resources via a Service Account, check the details in the
496-
[deployment section](./deploy-manual.mdx#workload-discovery).
508+
resources via a Service Account. See the
509+
[deployment section](./deploy-manual.mdx#workload-discovery) for details.
497510

498511
:::
499512

docs/toolhive/guides-registry/deploy-operator.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,7 @@ kubectl -n <NAMESPACE> describe mcpregistry <NAME>
610610
- [Configure sources and registries](./configuration.mdx) to set up additional
611611
data sources and filtering options
612612
- [Kubernetes source](./configuration.mdx#kubernetes-source) - the operator
613-
automatically creates a single Kubernetes source named `default`; you cannot
614-
configure additional Kubernetes sources
613+
automatically creates a Kubernetes source named `default`
615614

616615
## Related information
617616

docs/toolhive/guides-registry/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: ToolHive Registry Server
33
description:
44
Deploy, configure, and secure the ToolHive Registry Server for MCP server and
5-
skill discovery.
5+
skill discovery
66
---
77

88
import DocCardList from '@theme/DocCardList';

docs/toolhive/guides-registry/intro.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,20 @@ flowchart LR
8080
- **Multi-tenant authorization**: Claims-based access control with role-based
8181
administration, enabling team-scoped registries and entry visibility
8282
- **Skills registry**: Publish and discover reusable
83-
[skills](../concepts/skills.mdx) through the extensions API
83+
[skills](../concepts/skills.mdx) via the extensions API and synced sources
8484

8585
## Registry sources
8686

8787
The server supports five registry source types:
8888

8989
1. **Managed Source** - A fully-managed MCP source
90-
- Ideal for private repositories
90+
- Ideal for hosting private MCP server catalogs
9191
- Automatically exposes entries following upstream MCP Registry format
9292
- Supports adding new MCP servers via `/publish` endpoint
9393

9494
2. **Upstream Registry** - Sync from upstream MCP Registry APIs
9595
- Supports federation and aggregation scenarios
96-
- Format conversion from upstream to ToolHive format
96+
- Syncs both MCP servers and skills from upstream sources
9797
- Does not support publishing
9898

9999
3. **Kubernetes Cluster** - Automatically creates registry entries for running
@@ -105,12 +105,12 @@ The server supports five registry source types:
105105

106106
4. **Git Repository** - Clone and sync from Git repositories
107107
- Supports branch, tag, or commit pinning
108-
- Ideal for version-controlled registries
108+
- Syncs both MCP servers and skills (upstream format)
109109
- Does not support publishing
110110

111111
5. **Local File** - Read from filesystem
112112
- Ideal for local development and testing
113-
- Supports mounted volumes in containers
113+
- Syncs both MCP servers and skills (upstream format)
114114
- Does not support publishing
115115

116116
## Next steps

docs/toolhive/guides-registry/skills.mdx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22
title: Manage skills
33
description:
44
How to publish, list, retrieve, and delete skills using the ToolHive Registry
5-
server extensions API.
5+
server extensions API
66
---
77

88
The Registry server provides an extensions API for managing
99
[skills](../concepts/skills.mdx). This guide covers the full lifecycle:
1010
publishing, listing, retrieving, and deleting skills.
1111

12+
Skills can come from two paths: **publishing** to a managed source via the API,
13+
or **syncing** from external sources (Git, API, File) that contain skills in the
14+
upstream registry format.
15+
1216
## Prerequisites
1317

1418
- A running Registry server with at least one **managed** source configured
15-
(skills can only be published to managed sources)
19+
(required for publishing; synced sources provide skills automatically)
1620
- `curl` or another HTTP client
1721
- If authentication is enabled, a valid bearer token (see
1822
[Authentication](./authentication.mdx))

0 commit comments

Comments
 (0)