Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
6 changes: 2 additions & 4 deletions .github/workflows/update-toolhive-reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ jobs:
update-reference:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0
- name: Setup
uses: ./.github/actions/setup

- name: Set up Git
run: |
Expand Down
7 changes: 4 additions & 3 deletions docs/toolhive/guides-cli/registry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,10 @@ configure ToolHive to use a custom registry instead. This is useful for
organizations that want to maintain their own private registry of MCP servers.

The registry is a JSON file that follows the
[ToolHive registry schema](../reference/registry-schema.mdx). Once you configure
a custom registry, ToolHive uses it for all commands that interact with the
registry, such as `thv registry list`, `thv registry info`, and `thv run`.
[ToolHive registry schema](../reference/registry-schema-toolhive.mdx). Once you
configure a custom registry, ToolHive uses it for all commands that interact
with the registry, such as `thv registry list`, `thv registry info`, and
`thv run`.

Refer to the
[built-in registry file](https://github.com/stacklok/toolhive/blob/main/pkg/registry/data/registry.json)
Expand Down
13 changes: 12 additions & 1 deletion docs/toolhive/guides-registry/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ registryName: my-registry
# Registries configuration (required, can have multiple registries)
registries:
- name: toolhive
# Data format: upstream (MCP registry format) or toolhive (legacy)
# Data format: upstream or toolhive (see below)
format: upstream

# Git repository configuration
Expand Down Expand Up @@ -63,6 +63,17 @@ database:
| `--config` | Path to YAML configuration file | Yes | - |
| `--address` | Server listen address | No | `:8080` |

## Registry data formats

The `format` field specifies the JSON schema format for the registry data:

- **`upstream`**: The official
[upstream MCP registry format](../reference/registry-schema-upstream.mdx),
used by the MCP Registry API and recommended for new registries.
- **`toolhive`**: The
[ToolHive-native format](../reference/registry-schema-toolhive.mdx), used by
the built-in ToolHive registry.

## Registries

The server supports five registry types, each with its own configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ displayed_sidebar: toolhiveSidebar
import JSONSchemaViewer from '@theme/JSONSchemaViewer';
import Schema from '@site/static/api-specs/toolhive-legacy-registry.schema.json';

This is the JSON schema for the ToolHive registry. It defines the structure and
constraints for the registry entries, ensuring that all entries conform to a
consistent format.
This is the JSON schema for the ToolHive-native registry format. It defines the
structure and constraints for registry entries, ensuring that all entries
conform to a consistent format.

:::info

This format is considered legacy now that an
[official upstream registry format](registry-schema-upstream.mdx) exists.
ToolHive supports both formats, and the ToolHive-native format continues to work
for the built-in registry, custom file-based registries, and the ToolHive
Registry Server.

:::

To use this schema in your own custom registry file, add a `$schema` property at
the top of your JSON file:
Expand Down
44 changes: 44 additions & 0 deletions docs/toolhive/reference/registry-schema-upstream.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Upstream registry JSON schema
description: The JSON schema for the official upstream MCP registry format.
displayed_sidebar: toolhiveSidebar
---

import JSONSchemaViewer from '@theme/JSONSchemaViewer';
import RegistrySchema from '@site/static/api-specs/upstream-registry.schema.json';
import McpServerSchema from '@site/static/api-specs/mcp-server.schema.json';

This is the JSON schema for the official upstream MCP registry format. It
defines the structure and constraints for registry entries, ensuring that all
entries conform to a consistent format. The registry wraps the official MCP
server schema, which is documented separately below.

:::info

ToolHive also supports the
[ToolHive-native registry format](./registry-schema-toolhive.mdx), which is used
by the built-in registry. Both formats work with custom file-based registries
and the ToolHive Registry Server.

:::

To use this schema in your own custom registry file, add a `$schema` property at
the top of your JSON file:

```json
{
"$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/upstream-registry.schema.json",
...
}
```

## Registry schema

<JSONSchemaViewer schema={RegistrySchema} />

## MCP server object schema

The `servers` array in the registry contains objects that conform to the
official MCP server schema:

<JSONSchemaViewer schema={McpServerSchema} />
4 changes: 2 additions & 2 deletions docs/toolhive/tutorials/custom-registry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,8 @@ registries and private servers hosted within your organization.

## Next steps

- Explore the full [schema reference](../reference/registry-schema.mdx) to
understand all available configuration options
- Explore the full [schema reference](../reference/registry-schema-toolhive.mdx)
to understand all available configuration options
- Learn about [custom permissions](../guides-cli/custom-permissions.mdx) for
fine-grained security control
- Set up [secrets management](../guides-cli/secrets-management.mdx) for servers
Expand Down
6 changes: 5 additions & 1 deletion docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,11 @@ const config: Config = {
},
{
label: 'ToolHive Registry schema',
to: 'toolhive/reference/registry-schema',
to: 'toolhive/reference/registry-schema-toolhive',
},
{
label: 'Upstream Registry schema',
to: 'toolhive/reference/registry-schema-upstream',
},
{
label: 'ToolHive Registry API',
Expand Down
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"redocusaurus": "2.5.0"
},
"devDependencies": {
"@apidevtools/json-schema-ref-parser": "^15.2.1",
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/tsconfig": "3.9.2",
"@docusaurus/types": "3.9.2",
Expand Down
100 changes: 100 additions & 0 deletions scripts/bundle-upstream-schema.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env node
// SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc.
// SPDX-License-Identifier: Apache-2.0

/**
* This script dereferences the upstream registry schema, resolving all $ref
* references (including remote ones) into a single bundled schema file.
*
* It also fetches and dereferences the official MCP server schema separately
* for improved rendering on the documentation page. The MCP server schema URL
* is extracted from the $ref in the upstream registry schema.
*
* This is necessary because the upstream schema references the official MCP
* server schema, which contains internal $ref references that the JSON Schema
* Viewer plugin cannot resolve at runtime.
*/

import { readFileSync, writeFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import $RefParser from '@apidevtools/json-schema-ref-parser';

const __dirname = dirname(fileURLToPath(import.meta.url));
const STATIC_DIR = join(__dirname, '..', 'static', 'api-specs');

const REGISTRY_SCHEMA = join(STATIC_DIR, 'upstream-registry.schema.json');
const MCP_SERVER_SCHEMA = join(STATIC_DIR, 'mcp-server.schema.json');

/**
* Extract the MCP server schema URL from the upstream registry schema.
* Looks for $ref in data.properties.servers.items.
*/
function extractMcpServerUrl(registrySchemaPath) {
const schema = JSON.parse(readFileSync(registrySchemaPath, 'utf-8'));
const ref = schema?.properties?.data?.properties?.servers?.items?.$ref;

if (!ref || !ref.startsWith('http')) {
throw new Error(
'Could not find MCP server schema $ref in upstream registry schema'
);
}

return ref;
}

/**
* Replace the MCP server $ref in the registry with a placeholder that directs
* readers to the separate MCP server schema section.
*/
function simplifyRegistrySchema(schema, mcpServerUrl) {
const serverPlaceholder = {
type: 'object',
description: `MCP server object (see MCP server object schema section below). Source: ${mcpServerUrl}`,
};

// Replace servers.items $ref
if (schema?.properties?.data?.properties?.servers?.items) {
schema.properties.data.properties.servers.items = serverPlaceholder;
}

// Replace groups.items.properties.servers.items $ref if present
if (
schema?.properties?.data?.properties?.groups?.items?.properties?.servers
?.items
) {
schema.properties.data.properties.groups.items.properties.servers.items =
serverPlaceholder;
}

return schema;
}

async function main() {
try {
const mcpServerUrl = extractMcpServerUrl(REGISTRY_SCHEMA);

// Simplify registry schema by replacing $refs with placeholders
const registrySchema = JSON.parse(readFileSync(REGISTRY_SCHEMA, 'utf-8'));
const simplifiedSchema = simplifyRegistrySchema(
registrySchema,
mcpServerUrl
);
writeFileSync(REGISTRY_SCHEMA, JSON.stringify(simplifiedSchema, null, 2));
echo('Upstream registry schema processed successfully');

// Fetch and dereference the MCP server schema
const mcpServerSchema = await $RefParser.dereference(mcpServerUrl);
writeFileSync(MCP_SERVER_SCHEMA, JSON.stringify(mcpServerSchema, null, 2));
echo('MCP server schema fetched successfully');
} catch (error) {
console.error('Failed to process schemas:', error.message);
process.exit(1);
}
}

function echo(msg) {
console.log(msg);
}

Comment thread
danbarr marked this conversation as resolved.
Outdated
main();
18 changes: 17 additions & 1 deletion scripts/update-toolhive-reference.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ API_SPEC_DST="${STATIC_DIR}/api-specs/toolhive-api.yaml"

REGISTRY_SCHEMA_SRC="${IMPORT_DIR}/toolhive/pkg/registry/data/toolhive-legacy-registry.schema.json"
REGISTRY_SCHEMA_DST="${STATIC_DIR}/api-specs/toolhive-legacy-registry.schema.json"
UPSTREAM_REGISTRY_SCHEMA_SRC="${IMPORT_DIR}/toolhive/pkg/registry/data/upstream-registry.schema.json"
UPSTREAM_REGISTRY_SCHEMA_DST="${STATIC_DIR}/api-specs/upstream-registry.schema.json"


CRD_API_SRC="${IMPORT_DIR}/toolhive/docs/operator/crd-api.md"
CRD_API_DST="${STATIC_DIR}/api-specs/toolhive-crd-api.md"
Expand Down Expand Up @@ -112,7 +115,7 @@ if [[ "$RELEASE_VERSION" =~ ^v.* ]]; then
echo "Warning: API specification not found at ${API_SPEC_SRC}"
fi

## Registry schema
## Registry schemas
echo "Updating ToolHive registry JSON schema at ${REGISTRY_SCHEMA_DST}"

if [ -f "${REGISTRY_SCHEMA_SRC}" ]; then
Expand All @@ -122,6 +125,19 @@ if [[ "$RELEASE_VERSION" =~ ^v.* ]]; then
echo "Warning: Registry schema not found at ${REGISTRY_SCHEMA_SRC}"
fi

echo "Updating upstream registry JSON schema at ${UPSTREAM_REGISTRY_SCHEMA_DST}"

if [ -f "${UPSTREAM_REGISTRY_SCHEMA_SRC}" ]; then
cp ${UPSTREAM_REGISTRY_SCHEMA_SRC} ${UPSTREAM_REGISTRY_SCHEMA_DST}
echo "Upstream registry JSON schema updated successfully"

# Bundle the upstream schema to resolve remote $ref references
echo "Bundling upstream registry schema (resolving remote references)..."
node "${REPO_ROOT}/scripts/bundle-upstream-schema.mjs"
else
echo "Warning: Registry schema not found at ${UPSTREAM_REGISTRY_SCHEMA_SRC}"
fi

elif [[ "$RELEASE_VERSION" =~ ^toolhive-operator-crds-.* ]]; then
echo "Processing operator CRD release: $RELEASE_VERSION"

Expand Down
34 changes: 34 additions & 0 deletions src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,37 @@ details summary:hover {
[data-theme='light'] .navbar-sidebar .navbar__logo img {
content: url('/img/logos/stacklok-default-dark-green.svg');
}

/* JSON Schema Viewer overrides
* Reset the brand-colored alert styling for the docusaurus-json-schema-plugin
* since the nested details blocks are overwhelming with the green theme.
*/
.json-schema-viewer.alert,
.json-schema-viewer details.alert {
--docusaurus-details-decoration-color: var(--ifm-color-gray-500);
--ifm-alert-background-color: var(--ifm-background-surface-color);
--ifm-alert-border-color: var(--ifm-color-gray-300);
}

.json-schema-viewer details summary {
color: var(--ifm-color-content);
}

.json-schema-viewer details summary:hover {
color: var(--ifm-color-primary);
}

[data-theme='dark'] .json-schema-viewer.alert,
[data-theme='dark'] .json-schema-viewer details.alert {
--docusaurus-details-decoration-color: var(--ifm-color-gray-600);
--ifm-alert-background-color: var(--ifm-background-color);
--ifm-alert-border-color: var(--ifm-color-gray-700);
}

[data-theme='dark'] .json-schema-viewer details summary {
color: var(--ifm-color-content);
}

[data-theme='dark'] .json-schema-viewer details summary:hover {
color: var(--ifm-color-primary);
}
Loading
Loading