From e258afc33e617b62a4458db8cbe4bac1691676f6 Mon Sep 17 00:00:00 2001
From: Dan Barr <6922515+danbarr@users.noreply.github.com>
Date: Thu, 14 Aug 2025 20:42:05 -0400
Subject: [PATCH 1/5] Add the JSON schema plugin
Signed-off-by: Dan Barr <6922515+danbarr@users.noreply.github.com>
---
docusaurus.config.ts | 2 +-
package-lock.json | 166 +++++++++++++++++++++++++++++++++++++++++++
package.json | 1 +
tsconfig.json | 3 +-
4 files changed, 170 insertions(+), 2 deletions(-)
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index 6150af63..d3fdc7a6 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -47,7 +47,7 @@ const config: Config = {
mermaid: true,
},
- themes: ['@docusaurus/theme-mermaid'],
+ themes: ['@docusaurus/theme-mermaid', 'docusaurus-json-schema-plugin'],
presets: [
[
diff --git a/package-lock.json b/package-lock.json
index 631d622a..6be8b6db 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"@iconify-json/logos": "^1.2.7",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
+ "docusaurus-json-schema-plugin": "^1.14.0",
"prism-react-renderer": "^2.4.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
@@ -4995,6 +4996,81 @@
"micromark-util-symbol": "^1.0.1"
}
},
+ "node_modules/@stoplight/json": {
+ "version": "3.21.7",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.7.tgz",
+ "integrity": "sha512-xcJXgKFqv/uCEgtGlPxy3tPA+4I+ZI4vAuMJ885+ThkTHFVkC+0Fm58lA9NlsyjnkpxFh4YiQWpH+KefHdbA0A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
+ "jsonc-parser": "~2.2.1",
+ "lodash": "^4.17.21",
+ "safe-stable-stringify": "^1.1"
+ },
+ "engines": {
+ "node": ">=8.3.0"
+ }
+ },
+ "node_modules/@stoplight/json-ref-resolver": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-3.1.6.tgz",
+ "integrity": "sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@stoplight/json": "^3.21.0",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^12.3.0 || ^13.0.0",
+ "@types/urijs": "^1.19.19",
+ "dependency-graph": "~0.11.0",
+ "fast-memoize": "^2.5.2",
+ "immer": "^9.0.6",
+ "lodash": "^4.17.21",
+ "tslib": "^2.6.0",
+ "urijs": "^1.19.11"
+ },
+ "engines": {
+ "node": ">=8.3.0"
+ }
+ },
+ "node_modules/@stoplight/json/node_modules/jsonc-parser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz",
+ "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==",
+ "license": "MIT"
+ },
+ "node_modules/@stoplight/ordered-object-literal": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz",
+ "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@stoplight/path": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz",
+ "integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@stoplight/types": {
+ "version": "13.20.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz",
+ "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": "^12.20 || >=14.13"
+ }
+ },
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
@@ -5936,6 +6012,12 @@
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
+ "node_modules/@types/urijs": {
+ "version": "1.19.25",
+ "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz",
+ "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==",
+ "license": "MIT"
+ },
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
@@ -9526,6 +9608,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/dependency-graph": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+ "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -9628,6 +9719,23 @@
"node": ">=0.10.0"
}
},
+ "node_modules/docusaurus-json-schema-plugin": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/docusaurus-json-schema-plugin/-/docusaurus-json-schema-plugin-1.14.0.tgz",
+ "integrity": "sha512-TyXIVqAr+rKXhRQqq/IElUogl5ABSDV+PoiHcB6+Jroy2RMsimHkvz3OyfYpxTPDVGetSlX9TvuYJgLVBLIe/w==",
+ "license": "AGPL-3.0-or-later",
+ "dependencies": {
+ "@stoplight/json-ref-resolver": "^3.1.5",
+ "monaco-editor": "^0.52.2",
+ "monaco-editor-webpack-plugin": "^7.1.0",
+ "react-monaco-editor": "^0.58.0"
+ },
+ "peerDependencies": {
+ "@docusaurus/core": "^3.7.0",
+ "@docusaurus/theme-classic": "^3.7.0",
+ "react": ">=18 < 20"
+ }
+ },
"node_modules/docusaurus-plugin-redoc": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/docusaurus-plugin-redoc/-/docusaurus-plugin-redoc-2.5.0.tgz",
@@ -10815,6 +10923,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-memoize": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
+ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==",
+ "license": "MIT"
+ },
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
@@ -12360,6 +12474,16 @@
"node": ">=16.x"
}
},
+ "node_modules/immer": {
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -16749,6 +16873,25 @@
}
}
},
+ "node_modules/monaco-editor": {
+ "version": "0.52.2",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
+ "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
+ "license": "MIT"
+ },
+ "node_modules/monaco-editor-webpack-plugin": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-7.1.0.tgz",
+ "integrity": "sha512-ZjnGINHN963JQkFqjjcBtn1XBtUATDZBMgNQhDQwd78w2ukRhFXAPNgWuacaQiDZsUr4h1rWv5Mv6eriKuOSzA==",
+ "license": "MIT",
+ "dependencies": {
+ "loader-utils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "monaco-editor": ">= 0.31.0",
+ "webpack": "^4.5.0 || 5.x"
+ }
+ },
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -19890,6 +20033,17 @@
"webpack": ">=4.41.1 || 5.x"
}
},
+ "node_modules/react-monaco-editor": {
+ "version": "0.58.0",
+ "resolved": "https://registry.npmjs.org/react-monaco-editor/-/react-monaco-editor-0.58.0.tgz",
+ "integrity": "sha512-e8JH0TQEzO96Wd/EXgzc9M9tQK1pxBECD+8GNob9slMURcCM36TiVrgc4topWCDGYxRuMj8IEkaX+s3eQcUUqw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "monaco-editor": "^0.52.0",
+ "react": ">=16.8.0 <20.0.0",
+ "react-dom": ">=16.8.0 <20.0.0"
+ }
+ },
"node_modules/react-router": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
@@ -20994,6 +21148,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-stable-stringify": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz",
+ "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==",
+ "license": "MIT"
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -23281,6 +23441,12 @@
"integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==",
"license": "MIT"
},
+ "node_modules/urijs": {
+ "version": "1.19.11",
+ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
+ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==",
+ "license": "MIT"
+ },
"node_modules/url-loader": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz",
diff --git a/package.json b/package.json
index af9a0106..f8b7750d 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@iconify-json/logos": "^1.2.7",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
+ "docusaurus-json-schema-plugin": "^1.14.0",
"prism-react-renderer": "^2.4.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
diff --git a/tsconfig.json b/tsconfig.json
index 920d7a65..a1a8843f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,8 @@
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
- "baseUrl": "."
+ "baseUrl": ".",
+ "resolveJsonModule": true
},
"exclude": [".docusaurus", "build"]
}
From 7a418259e0a75c5e0c342b83eeda85665b09da48 Mon Sep 17 00:00:00 2001
From: Dan Barr <6922515+danbarr@users.noreply.github.com>
Date: Thu, 14 Aug 2025 20:44:30 -0400
Subject: [PATCH 2/5] Add the ToolHive registry schema and update script
Signed-off-by: Dan Barr <6922515+danbarr@users.noreply.github.com>
---
.../workflows/update-toolhive-reference.yml | 2 +-
docs/toolhive/reference/crd-spec.mdx | 2 +-
docs/toolhive/reference/registry-schema.mdx | 23 +
scripts/update-toolhive-reference.sh | 50 +-
.../{crd-api.md => toolhive-crd-api.md} | 0
.../api-specs/toolhive-registry-schema.json | 551 ++++++++++++++++++
6 files changed, 612 insertions(+), 16 deletions(-)
create mode 100644 docs/toolhive/reference/registry-schema.mdx
rename static/api-specs/{crd-api.md => toolhive-crd-api.md} (100%)
create mode 100644 static/api-specs/toolhive-registry-schema.json
diff --git a/.github/workflows/update-toolhive-reference.yml b/.github/workflows/update-toolhive-reference.yml
index f6164b5c..71b9e675 100644
--- a/.github/workflows/update-toolhive-reference.yml
+++ b/.github/workflows/update-toolhive-reference.yml
@@ -70,7 +70,7 @@ jobs:
title: |
Update ToolHive reference docs for ${{ steps.imports.outputs.version }}
body: |
- This PR updates the ToolHive CLI and API reference documentation to the latest release: ${{ steps.imports.outputs.version }}.
+ This PR updates the ToolHive CLI and API reference documentation for release: ${{ steps.imports.outputs.version }}.
commit-message: |
Update ToolHive reference docs for ${{ steps.imports.outputs.version }}
labels: |
diff --git a/docs/toolhive/reference/crd-spec.mdx b/docs/toolhive/reference/crd-spec.mdx
index fbf2efe2..dffe3e77 100644
--- a/docs/toolhive/reference/crd-spec.mdx
+++ b/docs/toolhive/reference/crd-spec.mdx
@@ -6,6 +6,6 @@ description:
toc_max_heading_level: 4
---
-import CRDRef from '@site/static/api-specs/crd-api.md';
+import CRDRef from '@site/static/api-specs/toolhive-crd-api.md';
diff --git a/docs/toolhive/reference/registry-schema.mdx b/docs/toolhive/reference/registry-schema.mdx
new file mode 100644
index 00000000..ec8feb00
--- /dev/null
+++ b/docs/toolhive/reference/registry-schema.mdx
@@ -0,0 +1,23 @@
+---
+title: ToolHive registry JSON schema
+description: The JSON schema for the ToolHive registry.
+---
+
+import JSONSchemaViewer from '@theme/JSONSchemaViewer';
+import Schema from '@site/static/api-specs/toolhive-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.
+
+To use this schema in your own custom registry file, add a `$schema` property at
+the top of your JSON file, like this:
+
+```json
+{
+ "$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json",
+ ...
+}
+```
+
+
diff --git a/scripts/update-toolhive-reference.sh b/scripts/update-toolhive-reference.sh
index b3baac16..cd4b6361 100755
--- a/scripts/update-toolhive-reference.sh
+++ b/scripts/update-toolhive-reference.sh
@@ -11,6 +11,18 @@ IMPORT_DIR="./imports"
DOCS_DIR="./docs"
STATIC_DIR="./static"
+CLI_DOCS_SRC="${IMPORT_DIR}/toolhive/docs/cli"
+CLI_DOCS_DST="${DOCS_DIR}/toolhive/reference/cli"
+
+API_SPEC_SRC="${IMPORT_DIR}/toolhive/docs/server/swagger.yaml"
+API_SPEC_DST="${STATIC_DIR}/api-specs/toolhive-api.yaml"
+
+REGISTRY_SCHEMA_SRC="${IMPORT_DIR}/toolhive/pkg/registry/data/schema.json"
+REGISTRY_SCHEMA_DST="${STATIC_DIR}/api-specs/toolhive-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"
+
# Test the required directories exist
if [ ! -d "$IMPORT_DIR" ]; then
mkdir -p "$IMPORT_DIR"
@@ -76,49 +88,59 @@ if [[ "$RELEASE_VERSION" =~ ^v.* ]]; then
echo "Processing main CLI release: $RELEASE_VERSION"
## CLI reference
- echo "Updating ToolHive CLI reference documentation in ${DOCS_DIR}/toolhive/reference/cli"
+ echo "Updating ToolHive CLI reference documentation in ${CLI_DOCS_DST}"
# Remove existing CLI reference documentation files in case we remove any commands
- rm -f ${DOCS_DIR}/toolhive/reference/cli/thv_*.md
+ rm -f ${CLI_DOCS_DST}/thv_*.md
# Copy CLI documentation
- if [ -d "${IMPORT_DIR}/toolhive/docs/cli" ]; then
- cp -r ${IMPORT_DIR}/toolhive/docs/cli/* ${DOCS_DIR}/toolhive/reference/cli
+ if [ -d "${CLI_DOCS_SRC}" ]; then
+ cp -r ${CLI_DOCS_SRC}/* ${CLI_DOCS_DST}
echo "CLI reference documentation updated successfully"
else
- echo "Warning: CLI documentation not found in ${IMPORT_DIR}/toolhive/docs/cli"
+ echo "Warning: CLI documentation not found in ${CLI_DOCS_SRC}"
fi
## API reference
- echo "Updating ToolHive API reference in ${STATIC_DIR}/api-specs"
+ echo "Updating ToolHive API reference at ${API_SPEC_DST}"
# Copy API specification
- if [ -f "${IMPORT_DIR}/toolhive/docs/server/swagger.yaml" ]; then
- cp ${IMPORT_DIR}/toolhive/docs/server/swagger.yaml ${STATIC_DIR}/api-specs/toolhive-api.yaml
+ if [ -f "${API_SPEC_SRC}" ]; then
+ cp ${API_SPEC_SRC} ${API_SPEC_DST}
echo "API reference updated successfully"
else
- echo "Warning: API specification not found in ${IMPORT_DIR}/toolhive/docs/server/swagger.yaml"
+ echo "Warning: API specification not found at ${API_SPEC_SRC}"
fi
- elif [[ "$RELEASE_VERSION" =~ ^toolhive-operator-crds-.* ]]; then
+ ## Registry schema
+ echo "Updating ToolHive registry JSON schema at ${REGISTRY_SCHEMA_DST}"
+
+ if [ -f "${REGISTRY_SCHEMA_SRC}" ]; then
+ cp ${REGISTRY_SCHEMA_SRC} ${REGISTRY_SCHEMA_DST}
+ echo "Registry JSON schema updated successfully"
+ else
+ echo "Warning: Registry schema not found at ${REGISTRY_SCHEMA_SRC}"
+ fi
+
+elif [[ "$RELEASE_VERSION" =~ ^toolhive-operator-crds-.* ]]; then
echo "Processing operator CRD release: $RELEASE_VERSION"
## CRD API reference
echo "Updating ToolHive CRD API reference in ${STATIC_DIR}/api-specs"
# Copy CRD API documentation
- if [ -f "${IMPORT_DIR}/toolhive/docs/operator/crd-api.md" ]; then
+ if [ -f "${CRD_API_SRC}" ]; then
# Remove h1 title from the CRD API documentation, Docusaurus will use the title from the front matter
- sed '1{/^# /d;}' ${IMPORT_DIR}/toolhive/docs/operator/crd-api.md > ${STATIC_DIR}/api-specs/crd-api.md
+ sed '1{/^# /d;}' ${CRD_API_SRC} > ${CRD_API_DST}
echo "CRD API reference updated successfully"
else
- echo "Warning: CRD API documentation not found in ${IMPORT_DIR}/toolhive/docs/operator/crd-api.md"
+ echo "Warning: CRD API documentation not found at ${CRD_API_SRC}"
fi
elif [[ "$RELEASE_VERSION" =~ ^toolhive-operator- ]]; then
echo "Processing main operator release: $RELEASE_VERSION"
echo "Placeholder: No specific processing implemented for this release type yet"
-
+
else
echo "Unknown release type for tag: $RELEASE_VERSION"
echo "Supported release types:"
diff --git a/static/api-specs/crd-api.md b/static/api-specs/toolhive-crd-api.md
similarity index 100%
rename from static/api-specs/crd-api.md
rename to static/api-specs/toolhive-crd-api.md
diff --git a/static/api-specs/toolhive-registry-schema.json b/static/api-specs/toolhive-registry-schema.json
new file mode 100644
index 00000000..927860c3
--- /dev/null
+++ b/static/api-specs/toolhive-registry-schema.json
@@ -0,0 +1,551 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json",
+ "title": "ToolHive MCP Server Registry Schema",
+ "description": "JSON Schema for the ToolHive MCP server registry. This schema validates the structure and content of registry.json entries for MCP servers. See docs/registry/management.md and docs/registry/heuristics.md for inclusion criteria and management processes.",
+ "type": "object",
+ "required": ["last_updated", "servers", "version"],
+ "properties": {
+ "last_updated": {
+ "type": "string",
+ "description": "Timestamp when the registry was last updated, in RFC3339 format",
+ "format": "date-time"
+ },
+ "servers": {
+ "type": "object",
+ "description": "Collection of MCP server entries indexed by server name",
+ "patternProperties": {
+ "^[a-z0-9][a-z0-9-]+[a-z0-9]$": {
+ "$ref": "#/definitions/server"
+ }
+ },
+ "additionalProperties": false
+ },
+ "remote_servers": {
+ "type": "object",
+ "description": "Collection of remote MCP server entries indexed by server name",
+ "patternProperties": {
+ "^[a-z0-9][a-z0-9-]+[a-z0-9]$": {
+ "$ref": "#/definitions/remote_server"
+ }
+ },
+ "additionalProperties": false
+ },
+ "version": {
+ "type": "string",
+ "description": "Registry schema version",
+ "pattern": "^\\d+\\.\\d+\\.\\d+$"
+ }
+ },
+ "definitions": {
+ "server": {
+ "type": "object",
+ "description": "MCP server entry definition",
+ "required": [
+ "description",
+ "image",
+ "status",
+ "tier",
+ "tools",
+ "transport"
+ ],
+ "properties": {
+ "args": {
+ "type": "array",
+ "description": "Default command-line arguments passed to the MCP server container",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "custom_metadata": {
+ "type": "object",
+ "description": "Custom user-defined metadata for the MCP server, primarily for custom registries",
+ "additionalProperties": true
+ },
+ "description": {
+ "type": "string",
+ "description": "Human-readable description of the server's purpose and functionality",
+ "minLength": 10,
+ "maxLength": 500
+ },
+ "docker_tags": {
+ "type": "array",
+ "description": "Available Docker tags for this server image",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": true
+ },
+ "env_vars": {
+ "type": "array",
+ "description": "Environment variables that can be passed to the server",
+ "items": {
+ "$ref": "#/definitions/environment_variable"
+ }
+ },
+ "image": {
+ "type": "string",
+ "description": "Container image reference for the MCP server",
+ "pattern": "^[a-z0-9]([a-z0-9._-]*[a-z0-9])?(:[0-9]+)?(/[a-z0-9]([a-z0-9._-]*[a-z0-9])?)*(:([a-zA-Z0-9][a-zA-Z0-9._-]*))?$",
+ "examples": [
+ "mcp/fetch:latest",
+ "ghcr.io/github/github-mcp-server:latest",
+ "mcr.microsoft.com/playwright/mcp",
+ "example.com:5000/team/my-app:2.0"
+ ]
+ },
+ "metadata": {
+ "description": "Additional information about the server such as popularity metrics",
+ "$ref": "#/definitions/metadata"
+ },
+ "name": {
+ "type": "string",
+ "description": "Identifier for the MCP server, used when referencing the server in commands (auto-generated from the object key)"
+ },
+ "permissions": {
+ "description": "Security profile and access permissions for the server",
+ "$ref": "#/definitions/permissions"
+ },
+ "provenance": {
+ "description": "Verification and signing metadata",
+ "$ref": "#/definitions/provenance"
+ },
+ "repository_url": {
+ "type": "string",
+ "description": "URL of the source code repository for the server",
+ "format": "uri"
+ },
+ "status": {
+ "type": "string",
+ "description": "Current status of the server (Active or Deprecated)",
+ "enum": ["Active", "Deprecated"]
+ },
+ "tags": {
+ "type": "array",
+ "description": "Categorization tags for search and filtering",
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]*[a-z0-9]$"
+ },
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "target_port": {
+ "type": "integer",
+ "description": "Port for the container to expose (applicable to SSE and Streamable HTTP transports)",
+ "minimum": 1,
+ "maximum": 65535
+ },
+ "tier": {
+ "type": "string",
+ "description": "Tier classification of the server, (Official or Community)",
+ "enum": ["Official", "Community"]
+ },
+ "tools": {
+ "type": "array",
+ "description": "List of tool names provided by this MCP server",
+ "items": {
+ "type": "string",
+ "pattern": "^[\\w-]+$"
+ },
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "transport": {
+ "type": "string",
+ "description": "Communication transport protocol used by the MCP server",
+ "enum": ["stdio", "sse", "streamable-http"],
+ "default": "stdio"
+ }
+ },
+ "additionalProperties": false
+ },
+ "environment_variable": {
+ "type": "object",
+ "description": "Environment variable definition for MCP server configuration",
+ "required": ["name", "description", "required"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Environment variable name (e.g., API_KEY)",
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
+ },
+ "description": {
+ "type": "string",
+ "description": "Human-readable explanation of the variable's purpose",
+ "minLength": 5,
+ "maxLength": 200
+ },
+ "required": {
+ "type": "boolean",
+ "description": "Whether this environment variable is required for the server to function",
+ "default": false
+ },
+ "secret": {
+ "type": "boolean",
+ "description": "Whether this environment variable contains sensitive information that should be stored as a secret",
+ "default": false
+ },
+ "default": {
+ "type": "string",
+ "description": "Value to use if the environment variable is not explicitly provided (only used for non-required variables)"
+ }
+ },
+ "additionalProperties": false
+ },
+ "permissions": {
+ "type": "object",
+ "description": "Security permissions applied to the MCP server",
+ "required": [],
+ "properties": {
+ "network": {
+ "$ref": "#/definitions/network_permissions"
+ },
+ "read": {
+ "type": "array",
+ "description": "File system paths the server needs read access to (will be mounted from the host)",
+ "items": {
+ "type": "string",
+ "pattern": "^(/[^/\\0]+)+/?$"
+ },
+ "uniqueItems": true,
+ "default": []
+ },
+ "write": {
+ "type": "array",
+ "description": "File system paths the server needs write access to (will be mounted from the host)",
+ "items": {
+ "type": "string",
+ "pattern": "^(/[^/\\0]+)+/?$"
+ },
+ "uniqueItems": true,
+ "default": []
+ },
+ "privileged": {
+ "type": "boolean",
+ "description": "Whether the container should run in privileged mode. When true, the container has access to all host devices and capabilities. Use with extreme caution as this removes most security isolation.",
+ "default": false
+ }
+ },
+ "additionalProperties": false
+ },
+ "network_permissions": {
+ "type": "object",
+ "description": "Network access permissions for the MCP server",
+ "required": [],
+ "properties": {
+ "outbound": {
+ "$ref": "#/definitions/outbound_permissions"
+ }
+ },
+ "additionalProperties": false
+ },
+ "outbound_permissions": {
+ "type": "object",
+ "description": "Outbound network access permissions",
+ "required": [],
+ "properties": {
+ "allow_host": {
+ "type": "array",
+ "description": "Allowed hostnames or domain patterns for outbound connections",
+ "items": {
+ "type": "string",
+ "anyOf": [
+ {
+ "format": "hostname"
+ },
+ {
+ "pattern": "^\\.[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?$"
+ }
+ ]
+ },
+ "uniqueItems": true,
+ "default": []
+ },
+ "allow_port": {
+ "type": "array",
+ "description": "Allowed port numbers for outbound connections",
+ "items": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 65535
+ },
+ "uniqueItems": true,
+ "default": []
+ },
+ "insecure_allow_all": {
+ "type": "boolean",
+ "description": "Whether to allow all outbound connections (insecure, use with caution)",
+ "default": false
+ }
+ },
+ "additionalProperties": false
+ },
+ "metadata": {
+ "type": "object",
+ "description": "Metadata about the MCP server from external sources",
+ "properties": {
+ "last_updated": {
+ "type": "string",
+ "description": "Timestamp when the metadata was last updated, in RFC3339 format",
+ "format": "date-time"
+ },
+ "pulls": {
+ "type": "integer",
+ "description": "Number of container image pulls",
+ "minimum": 0
+ },
+ "stars": {
+ "type": "integer",
+ "description": "Number of repository stars",
+ "minimum": 0
+ }
+ },
+ "additionalProperties": false
+ },
+ "provenance": {
+ "type": "object",
+ "description": "Software supply chain provenance information for verified servers",
+ "properties": {
+ "cert_issuer": {
+ "type": "string",
+ "description": "Certificate issuer for provenance verification",
+ "format": "uri",
+ "examples": ["https://token.actions.githubusercontent.com"]
+ },
+ "repository_uri": {
+ "type": "string",
+ "description": "Repository URI used for provenance verification",
+ "format": "uri"
+ },
+ "repository_ref": {
+ "type": "string",
+ "description": "Repository reference used for provenance verification"
+ },
+ "runner_environment": {
+ "type": "string",
+ "description": "Build environment where the server was built",
+ "examples": ["github-hosted", "gitlab-hosted", "self-hosted"]
+ },
+ "signer_identity": {
+ "type": "string",
+ "description": "Identity of the signer for provenance verification"
+ },
+ "sigstore_url": {
+ "type": "string",
+ "description": "Sigstore TUF repository host for provenance verification",
+ "format": "hostname",
+ "default": "tuf-repo-cdn.sigstore.dev",
+ "examples": ["tuf-repo.github.com", "tuf-repo-cdn.sigstore.dev"]
+ },
+ "attestation": {
+ "description": "Verified attestation information",
+ "$ref": "#/definitions/verified_attestation"
+ }
+ },
+ "additionalProperties": false
+ },
+ "verified_attestation": {
+ "type": "object",
+ "description": "Verified attestation information",
+ "properties": {
+ "predicate_type": {
+ "type": "string",
+ "description": "Type of the attestation predicate",
+ "format": "uri",
+ "examples": [
+ "https://slsa.dev/provenance/v0.2",
+ "https://slsa.dev/provenance/v1"
+ ]
+ },
+ "predicate": {
+ "description": "Attestation predicate data"
+ }
+ },
+ "additionalProperties": false
+ },
+ "header": {
+ "type": "object",
+ "description": "HTTP header definition for remote MCP server authentication",
+ "required": ["name", "description", "required"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Header name (e.g., X-API-Key, Authorization)",
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9-]*$"
+ },
+ "description": {
+ "type": "string",
+ "description": "Human-readable explanation of the header's purpose",
+ "minLength": 5,
+ "maxLength": 200
+ },
+ "required": {
+ "type": "boolean",
+ "description": "Whether this header is required for the server to function",
+ "default": false
+ },
+ "secret": {
+ "type": "boolean",
+ "description": "Whether this header contains sensitive information that should be stored as a secret",
+ "default": false
+ },
+ "default": {
+ "type": "string",
+ "description": "Value to use if the header is not explicitly provided (only used for non-required headers)"
+ },
+ "choices": {
+ "type": "array",
+ "description": "List of valid values for the header",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": true
+ }
+ },
+ "additionalProperties": false
+ },
+ "oauth_config": {
+ "type": "object",
+ "description": "OAuth/OIDC configuration for remote server authentication",
+ "properties": {
+ "issuer": {
+ "type": "string",
+ "description": "OAuth/OIDC issuer URL for OIDC discovery",
+ "format": "uri"
+ },
+ "authorize_url": {
+ "type": "string",
+ "description": "OAuth authorization endpoint URL (for non-OIDC OAuth)",
+ "format": "uri"
+ },
+ "token_url": {
+ "type": "string",
+ "description": "OAuth token endpoint URL (for non-OIDC OAuth)",
+ "format": "uri"
+ },
+ "client_id": {
+ "type": "string",
+ "description": "OAuth client ID for authentication"
+ },
+ "scopes": {
+ "type": "array",
+ "description": "OAuth scopes to request",
+ "items": {
+ "type": "string"
+ }
+ },
+ "use_pkce": {
+ "type": "boolean",
+ "description": "Whether to use PKCE for the OAuth flow",
+ "default": true
+ }
+ },
+ "additionalProperties": false
+ },
+ "remote_server": {
+ "type": "object",
+ "description": "Remote MCP server entry definition accessed via HTTP/HTTPS",
+ "required": [
+ "url",
+ "description",
+ "status",
+ "tier",
+ "tools",
+ "transport"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Identifier for the remote MCP server (auto-generated from the object key)"
+ },
+ "url": {
+ "type": "string",
+ "description": "Endpoint URL for the remote MCP server",
+ "format": "uri",
+ "examples": [
+ "https://api.example.com/mcp",
+ "https://mcp-server.example.com/sse",
+ "http://localhost:8080/stream"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "description": "Human-readable description of the server's purpose and functionality",
+ "minLength": 10,
+ "maxLength": 500
+ },
+ "tier": {
+ "type": "string",
+ "description": "Tier classification of the server (Official or Community)",
+ "enum": ["Official", "Community"]
+ },
+ "status": {
+ "type": "string",
+ "description": "Current status of the server (Active or Deprecated)",
+ "enum": ["Active", "Deprecated"]
+ },
+ "transport": {
+ "type": "string",
+ "description": "Communication transport protocol used by the remote MCP server",
+ "enum": ["sse", "streamable-http"],
+ "default": "sse"
+ },
+ "tools": {
+ "type": "array",
+ "description": "List of tool names provided by this MCP server",
+ "items": {
+ "type": "string",
+ "pattern": "^[\\w-]+$"
+ },
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers for authentication to the remote server",
+ "items": {
+ "$ref": "#/definitions/header"
+ }
+ },
+ "oauth_config": {
+ "description": "OAuth/OIDC configuration for authentication",
+ "$ref": "#/definitions/oauth_config"
+ },
+ "env_vars": {
+ "type": "array",
+ "description": "Environment variables for client-side configuration",
+ "items": {
+ "$ref": "#/definitions/environment_variable"
+ }
+ },
+ "metadata": {
+ "description": "Additional information about the server",
+ "$ref": "#/definitions/metadata"
+ },
+ "repository_url": {
+ "type": "string",
+ "description": "URL of the source code repository for the server",
+ "format": "uri"
+ },
+ "tags": {
+ "type": "array",
+ "description": "Categorization tags for search and filtering",
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z0-9][a-z0-9_-]+[a-z0-9]$"
+ },
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "custom_metadata": {
+ "type": "object",
+ "description": "Custom user-defined metadata for the remote MCP server",
+ "additionalProperties": true
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+}
From 19eaa0e9bbc391c9a5adb77071e801647e253176 Mon Sep 17 00:00:00 2001
From: Dan Barr <6922515+danbarr@users.noreply.github.com>
Date: Fri, 15 Aug 2025 19:30:33 -0400
Subject: [PATCH 3/5] Add custom registry tutorial
Signed-off-by: Dan Barr <6922515+danbarr@users.noreply.github.com>
---
docs/toolhive/tutorials/custom-registry.mdx | 338 ++++++++++++++++++++
sidebars.ts | 18 +-
2 files changed, 354 insertions(+), 2 deletions(-)
create mode 100644 docs/toolhive/tutorials/custom-registry.mdx
diff --git a/docs/toolhive/tutorials/custom-registry.mdx b/docs/toolhive/tutorials/custom-registry.mdx
new file mode 100644
index 00000000..d546f93d
--- /dev/null
+++ b/docs/toolhive/tutorials/custom-registry.mdx
@@ -0,0 +1,338 @@
+---
+title: Create a custom MCP registry
+description: Learn how to create a custom MCP registry for ToolHive.
+---
+
+import JSONSchemaViewer from '@theme/JSONSchemaViewer';
+import Schema from '@site/static/api-specs/toolhive-registry-schema.json';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Overview
+
+ToolHive includes a built-in registry of MCP servers with verified
+configurations that meet a
+[minimum quality standard](../concepts/registry-criteria.md).
+
+But you can also create your own custom registry to include the MCP servers that
+are relevant to your organization or specific use cases. This allows you to
+curate a list of servers that meet your specific needs.
+
+## Why create a custom registry?
+
+Creating a custom registry allows you to:
+
+- Curate a list of MCP servers tailored to your organization's needs
+- Include private or internal servers not listed in the public registry
+- Pre-configure server settings for easier deployment
+- Ensure all servers meet your organization's quality and security standards
+
+## Create your first custom registry
+
+In this tutorial, you'll create a custom MCP registry for ToolHive and configure
+it to use your own curated list of MCP servers. By the end, you'll have a
+working custom registry that you can extend with your organization's specific
+MCP servers.
+
+### What you'll build
+
+You'll create a JSON registry file containing a simple MCP server entry,
+configure ToolHive to use your custom registry, and verify it works by listing
+and running servers from your registry.
+
+### Prerequisites
+
+Before you start, make sure you have:
+
+- The ToolHive UI or CLI installed and working on your system
+ - [ToolHive UI quickstart](./quickstart-ui.mdx)
+ - [ToolHive CLI quickstart](./quickstart-cli.mdx)
+- Basic familiarity with JSON format
+- A text editor for creating the registry file
+
+### Step 1: Create the registry file
+
+First, create a new directory for your custom registry and navigate to it:
+
+```bash
+mkdir my-custom-registry
+cd my-custom-registry
+```
+
+Create a new file called `registry.json` and add the following content:
+
+```json title='registry.json'
+{
+ "$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json",
+ "version": "1.0.0",
+ "last_updated": "2025-08-15T10:00:00Z",
+ "servers": {
+ "my-fetch": {
+ "description": "A custom web content fetching MCP server for our organization",
+ "image": "ghcr.io/stackloklabs/gofetch/server:latest",
+ "status": "Active",
+ "tier": "Community",
+ "transport": "streamable-http",
+ "args": ["--user-agent", "Mozilla/5.0 (compatible;MyOrgFetchBot/1.0)"],
+ "tags": ["web", "fetch", "content"],
+ "tools": ["fetch"],
+ "permissions": {
+ "network": {
+ "outbound": {
+ "allow_host": [".example.com", "api.mycompany.com"],
+ "allow_port": [80, 443]
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+This registry defines a single MCP server called `my-fetch` with:
+
+- A description explaining its purpose
+- The container image to use
+- Required metadata like status and tier
+- A list of tools it provides
+- Command-line arguments for customization
+- Network permissions for security
+
+### Step 2: Configure ToolHive to use your registry
+
+Configure ToolHive to use your custom registry.
+
+
+
+
+1. Open the ToolHive application
+2. Navigate to **Settings** → **Registry**
+3. Select the **Local Registry** option
+4. Enter the full path to your `registry.json` file (for example:
+ `/Users//my-custom-registry/registry.json`)
+5. Click **Save** to apply the configuration
+
+The UI will validate the registry file and confirm it's been set successfully.
+
+
+
+
+Set the registry file using the full path:
+
+```bash
+thv config set-registry /Users//my-custom-registry/registry.json
+```
+
+Verify the configuration was applied:
+
+```bash
+thv config get-registry
+```
+
+You should see the path to your registry file displayed.
+
+
+
+
+### Step 3: Test your custom registry
+
+Verify your custom registry is working.
+
+
+
+
+1. Navigate to the **Registry** page from the menu bar
+2. You should see your `my-fetch` server displayed alongside any other servers
+ in your custom registry
+3. Click on the server to view its details, including description, tools, and
+ configuration options
+
+
+
+
+List the servers in your custom registry:
+
+```bash
+thv registry list
+```
+
+You should see your `my-fetch` server listed. Get detailed information about it:
+
+```bash
+thv registry info my-fetch
+```
+
+This displays the server's configuration, tools, and permissions as defined in
+your registry.
+
+
+
+
+### Step 4: Run a server from your registry
+
+Finally, run the MCP server from your custom registry.
+
+
+
+
+1. On the **Registry** page, click on your `my-fetch` server
+2. Click the **Install server** button
+3. Configure any required settings (the defaults from your registry will be
+ pre-populated)
+4. Click **Install server** to start the MCP server
+5. Navigate to the **MCP Servers** page to see your running server and manage it
+
+The server will appear in your MCP servers list, where you can start, stop, view
+logs, and manage it like any other MCP server.
+
+
+
+
+```bash
+thv run my-fetch
+```
+
+The server should start successfully, demonstrating that your custom registry is
+working correctly.
+
+
+
+
+### Step 5: Add more servers (optional)
+
+You can extend your registry by adding more servers to the `servers` object. For
+example, add a second server (note this is just an example, it will not function
+if you try to run it):
+
+```json title='registry.json'
+{
+ "$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json",
+ "version": "1.0.0",
+ "last_updated": "2025-08-15T10:00:00Z",
+ "servers": {
+ "my-fetch": {
+ // ... existing server configuration
+ },
+ "company-tools": {
+ "description": "Internal company tools and utilities MCP server",
+ "image": "registry.company.com/mcp/company-tools:v1.2.0",
+ "status": "Active",
+ "tier": "Community",
+ "transport": "stdio",
+ "tools": ["get_employee_info", "create_ticket", "check_inventory"],
+ "tags": ["internal", "company", "tools"],
+ "env_vars": [
+ {
+ "name": "COMPANY_API_KEY",
+ "description": "API key for accessing company internal services",
+ "required": true,
+ "secret": true
+ }
+ ]
+ }
+ }
+}
+```
+
+After updating the file, the new server is immediately available for ToolHive
+CLI commands. For the UI, navigate to the registry settings and click **Save**
+to see the new server listed.
+
+## Production considerations
+
+While this tutorial uses a local file for simplicity, in production environments
+you should:
+
+- **Host your registry on a secure HTTP server**
+- **Use version control** to track changes to your registry
+- **Implement proper access controls** to prevent unauthorized modifications
+- **Set up automated validation** to ensure registry entries follow the schema
+- **Regularly update** the registry with new servers and remove deprecated ones
+
+For production use, configure ToolHive to use a remote registry URL:
+
+```bash
+thv config set-registry https://registry.example.com/mcp-registry.json
+```
+
+## What you've learned
+
+You've successfully:
+
+- Created a custom MCP registry following the JSON schema
+- Configured ToolHive to use your custom registry
+- Added MCP servers with proper metadata and permissions
+- Tested your registry by listing and running servers
+
+You can now maintain your own curated list of MCP servers tailored to your
+organization's needs. The registry can include both public servers from
+container registries and private servers hosted within your organization.
+
+## Next steps
+
+- Explore the full [schema reference](#schema-reference) below 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
+ requiring API keys
+
+## Cleanup: Revert to the default registry
+
+If you want to switch back to using ToolHive's built-in registry after
+completing this tutorial, you can easily revert your configuration.
+
+
+
+
+To revert to the default registry through the UI:
+
+1. Navigate to **Settings** → **Registry**
+2. Select the **Default Registry** option
+3. Click **Save** to apply the configuration
+
+The UI will confirm that you're now using the built-in ToolHive registry.
+
+
+
+
+To revert to the default registry using the CLI:
+
+```bash
+thv config unset-registry
+```
+
+Verify that you're back to using the default registry:
+
+```bash
+thv config get-registry
+```
+
+You should see a message indicating that the built-in registry is being used.
+
+
+
+
+After reverting, all registry commands
+([`thv registry list`](../reference/cli/thv_registry_list.md),
+[`thv registry info`](../reference/cli/thv_registry_info.md),
+[`thv search`](../reference/cli/thv_search.md)) will use ToolHive's built-in
+registry instead of your custom one.
+
+## Schema reference
+
+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.
+
+To use this schema in your own custom registry file, add a `$schema` property at
+the top of your JSON file, like this:
+
+```json
+{
+ "$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json",
+ ...
+}
+```
+
+
diff --git a/sidebars.ts b/sidebars.ts
index 6d0bb4a2..0b496683 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -19,11 +19,11 @@ const sidebars: SidebarsConfig = {
{
type: 'category',
- label: 'Tutorials',
+ label: 'Quickstarts',
description: 'Step-by-step guides to get started with ToolHive',
link: {
type: 'generated-index',
- slug: 'toolhive/tutorials',
+ slug: 'toolhive/quickstart',
description:
'Learn how to use ToolHive with these step-by-step tutorials',
},
@@ -138,6 +138,20 @@ const sidebars: SidebarsConfig = {
],
},
+ {
+ type: 'category',
+ label: 'Tutorials',
+ description: 'Step-by-step guides to using ToolHive effectively',
+ link: {
+ type: 'generated-index',
+ slug: 'toolhive/tutorials',
+ description:
+ 'Learn how to use ToolHive with these step-by-step tutorials',
+ },
+ collapsed: false,
+ items: ['toolhive/tutorials/custom-registry'],
+ },
+
'toolhive/faq',
],
};
From 3227665e4e9ece647474ffb1d95edb14725bf1c7 Mon Sep 17 00:00:00 2001
From: Dan Barr <6922515+danbarr@users.noreply.github.com>
Date: Mon, 18 Aug 2025 17:44:56 -0400
Subject: [PATCH 4/5] Add references menu to the navbar
Signed-off-by: Dan Barr <6922515+danbarr@users.noreply.github.com>
---
docusaurus.config.ts | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index d3fdc7a6..04b89519 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -138,6 +138,29 @@ const config: Config = {
},
],
},
+ {
+ type: 'dropdown',
+ label: 'References',
+ position: 'left',
+ items: [
+ {
+ label: 'ToolHive CLI commands',
+ to: 'toolhive/reference/cli/thv',
+ },
+ {
+ label: 'ToolHive API',
+ to: 'toolhive/reference/api',
+ },
+ {
+ label: 'ToolHive registry schema',
+ to: 'toolhive/reference/registry-schema',
+ },
+ {
+ label: 'ToolHive Operator CRD',
+ to: 'toolhive/reference/crd-spec',
+ },
+ ],
+ },
{
href: 'https://github.com/stacklok',
className: 'fa-brands fa-github fa-lg',
From bd52f30cc4aa8f0cc1e2622b28b8c29911b834b7 Mon Sep 17 00:00:00 2001
From: Dan Barr <6922515+danbarr@users.noreply.github.com>
Date: Mon, 18 Aug 2025 17:45:16 -0400
Subject: [PATCH 5/5] Update sidebar and add references to JSON schema
Signed-off-by: Dan Barr <6922515+danbarr@users.noreply.github.com>
---
docs/toolhive/guides-cli/registry.md | 19 +++++++++-------
docs/toolhive/reference/registry-schema.mdx | 3 ++-
docs/toolhive/tutorials/custom-registry.mdx | 24 ++-------------------
sidebars.ts | 17 ++++++++++-----
4 files changed, 27 insertions(+), 36 deletions(-)
diff --git a/docs/toolhive/guides-cli/registry.md b/docs/toolhive/guides-cli/registry.md
index 5725b194..5a9150b2 100644
--- a/docs/toolhive/guides-cli/registry.md
+++ b/docs/toolhive/guides-cli/registry.md
@@ -132,6 +132,15 @@ By default, ToolHive uses a built-in registry of verified MCP servers. You can
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`.
+
+Refer to the
+[built-in registry file](https://github.com/stacklok/toolhive/blob/main/pkg/registry/data/registry.json)
+for examples of MCP server entries.
+
### Set a remote registry URL
To configure ToolHive to use a remote registry, set the registry URL:
@@ -154,14 +163,6 @@ To configure ToolHive to use a local registry, set the registry file:
thv config set-registry
```
-The registry must be a JSON file that follows the same format as the
-[built-in registry](https://github.com/stacklok/toolhive/blob/main/pkg/registry/data/registry.json).
-Once you configure a remote registry, all registry commands
-([`thv registry list`](../reference/cli/thv_registry_list.md),
-[`thv registry info`](../reference/cli/thv_registry_info.md),
-[`thv search`](../reference/cli/thv_search.md)) will use the remote registry
-instead of the built-in one.
-
### Check the current registry location
To see which registry (URL or path) is currently configured:
@@ -189,6 +190,8 @@ This restores the default behavior of using ToolHive's built-in registry.
See [Run MCP servers](./run-mcp-servers.mdx) to run an MCP server from the
registry.
+Learn how to [create a custom MCP registry](../tutorials/custom-registry.mdx).
+
## Related information
- [`thv registry` command reference](../reference/cli/thv_registry.md)
diff --git a/docs/toolhive/reference/registry-schema.mdx b/docs/toolhive/reference/registry-schema.mdx
index ec8feb00..7e302406 100644
--- a/docs/toolhive/reference/registry-schema.mdx
+++ b/docs/toolhive/reference/registry-schema.mdx
@@ -1,6 +1,7 @@
---
title: ToolHive registry JSON schema
description: The JSON schema for the ToolHive registry.
+displayed_sidebar: toolhiveSidebar
---
import JSONSchemaViewer from '@theme/JSONSchemaViewer';
@@ -11,7 +12,7 @@ constraints for the registry entries, ensuring that all entries conform to a
consistent format.
To use this schema in your own custom registry file, add a `$schema` property at
-the top of your JSON file, like this:
+the top of your JSON file:
```json
{
diff --git a/docs/toolhive/tutorials/custom-registry.mdx b/docs/toolhive/tutorials/custom-registry.mdx
index d546f93d..7c2ca492 100644
--- a/docs/toolhive/tutorials/custom-registry.mdx
+++ b/docs/toolhive/tutorials/custom-registry.mdx
@@ -3,8 +3,6 @@ title: Create a custom MCP registry
description: Learn how to create a custom MCP registry for ToolHive.
---
-import JSONSchemaViewer from '@theme/JSONSchemaViewer';
-import Schema from '@site/static/api-specs/toolhive-registry-schema.json';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
@@ -270,8 +268,8 @@ container registries and private servers hosted within your organization.
## Next steps
-- Explore the full [schema reference](#schema-reference) below to understand all
- available configuration options
+- Explore the full [schema reference](../reference/registry-schema.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
@@ -318,21 +316,3 @@ After reverting, all registry commands
[`thv registry info`](../reference/cli/thv_registry_info.md),
[`thv search`](../reference/cli/thv_search.md)) will use ToolHive's built-in
registry instead of your custom one.
-
-## Schema reference
-
-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.
-
-To use this schema in your own custom registry file, add a `$schema` property at
-the top of your JSON file, like this:
-
-```json
-{
- "$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json",
- ...
-}
-```
-
-
diff --git a/sidebars.ts b/sidebars.ts
index 0b496683..5ee03d89 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -19,13 +19,13 @@ const sidebars: SidebarsConfig = {
{
type: 'category',
- label: 'Quickstarts',
+ label: 'Get started',
description: 'Step-by-step guides to get started with ToolHive',
link: {
type: 'generated-index',
slug: 'toolhive/quickstart',
description:
- 'Learn how to use ToolHive with these step-by-step tutorials',
+ 'Learn how to use ToolHive with these step-by-step tutorials.',
},
collapsed: false,
items: [
@@ -129,7 +129,7 @@ const sidebars: SidebarsConfig = {
type: 'generated-index',
slug: 'toolhive/concepts',
description:
- 'Learn about the key concepts behind ToolHive and the Model Context Protocol (MCP)',
+ 'Learn about the key concepts behind ToolHive and the Model Context Protocol (MCP).',
},
items: [
'toolhive/concepts/mcp-primer',
@@ -146,10 +146,17 @@ const sidebars: SidebarsConfig = {
type: 'generated-index',
slug: 'toolhive/tutorials',
description:
- 'Learn how to use ToolHive with these step-by-step tutorials',
+ 'Learn how to use ToolHive with these step-by-step tutorials.',
},
collapsed: false,
- items: ['toolhive/tutorials/custom-registry'],
+ items: [
+ {
+ type: 'link',
+ href: '/toolhive/quickstart',
+ label: 'Quickstart guides',
+ },
+ 'toolhive/tutorials/custom-registry',
+ ],
},
'toolhive/faq',