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/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/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..7e302406 --- /dev/null +++ b/docs/toolhive/reference/registry-schema.mdx @@ -0,0 +1,24 @@ +--- +title: ToolHive registry JSON schema +description: The JSON schema for the ToolHive registry. +displayed_sidebar: toolhiveSidebar +--- + +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: + +```json +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive/main/pkg/registry/data/schema.json", + ... +} +``` + + diff --git a/docs/toolhive/tutorials/custom-registry.mdx b/docs/toolhive/tutorials/custom-registry.mdx new file mode 100644 index 00000000..7c2ca492 --- /dev/null +++ b/docs/toolhive/tutorials/custom-registry.mdx @@ -0,0 +1,318 @@ +--- +title: Create a custom MCP registry +description: Learn how to create a custom MCP registry for ToolHive. +--- + +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](../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 + 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. diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 6150af63..04b89519 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: [ [ @@ -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', 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/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/sidebars.ts b/sidebars.ts index 6d0bb4a2..5ee03d89 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -19,13 +19,13 @@ const sidebars: SidebarsConfig = { { type: 'category', - label: 'Tutorials', + label: 'Get started', 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', + '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', @@ -138,6 +138,27 @@ 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: [ + { + type: 'link', + href: '/toolhive/quickstart', + label: 'Quickstart guides', + }, + 'toolhive/tutorials/custom-registry', + ], + }, + 'toolhive/faq', ], }; 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 + } + } +} 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"] }