diff --git a/hive-mcp-registry/.github/workflows/validate.yml b/hive-mcp-registry/.github/workflows/validate.yml new file mode 100644 index 0000000000..ab48279d74 --- /dev/null +++ b/hive-mcp-registry/.github/workflows/validate.yml @@ -0,0 +1,47 @@ +name: Validate Registry + +on: + pull_request: + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 1 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install validator dependencies + run: python -m pip install --disable-pip-version-check jsonschema requests + + - name: Validate changed server manifests + shell: bash + run: | + set -euo pipefail + base_sha="${{ github.event.pull_request.base.sha }}" + head_sha="${{ github.event.pull_request.head.sha }}" + mapfile -t manifests < <(git diff --name-only "$base_sha" "$head_sha" -- registry/servers | grep '/manifest\.json$' | sort -u || true) + + if [ "${#manifests[@]}" -eq 0 ]; then + echo "No server manifests changed." + else + for manifest in "${manifests[@]}"; do + python scripts/validate.py "$manifest" + done + fi + + - name: Validate bundled examples + shell: bash + run: | + set -euo pipefail + for manifest in registry/_examples/*/manifest.json; do + python scripts/validate.py "$manifest" + done diff --git a/hive-mcp-registry/CONTRIBUTING.md b/hive-mcp-registry/CONTRIBUTING.md new file mode 100644 index 0000000000..4df38b0ddc --- /dev/null +++ b/hive-mcp-registry/CONTRIBUTING.md @@ -0,0 +1,183 @@ +# Contributing to hive-mcp-registry + +## 5-Minute Quickstart + +1. Fork the repository and clone your fork locally. +2. Copy `registry/_template/` to `registry/servers//`. +3. Update `manifest.json` with your real package, transport, credentials, and docs. +4. Add a sibling `README.md` that explains install, authentication, and a quick usage example. +5. Run: + +```bash +python scripts/validate.py registry/servers//manifest.json +python scripts/build_index.py +``` + +6. Commit your changes and open a pull request. + +## Manifest Reference + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `$schema` | `string` | No | Canonical schema URL for editor tooling and validation. | +| `name` | `string` | Yes | Registry identifier. Use lowercase letters, numbers, and hyphens only. | +| `display_name` | `string` | Yes | Human-friendly title shown in search results and UIs. | +| `version` | `string` | Yes | Server version in semver form, such as `1.2.0`. | +| `description` | `string` | Yes | Short summary of the server's purpose. | +| `author` | `object` | Yes | Original creator metadata with `name`, `github`, and `url`. | +| `maintainer` | `object` | Yes | Current maintainer metadata with `github` and `email`. | +| `repository` | `string` | Yes | Source repository URL. | +| `license` | `string` | Yes | License label or SPDX-like identifier. | +| `status` | `string` | Yes | One of `official`, `verified`, or `community`. | +| `docs_url` | `string` | No | Canonical documentation URL for install and auth setup. | +| `supported_os` | `string[]` | No | Supported OS values: `linux`, `macos`, `windows`. | +| `deprecated` | `boolean` | No | Whether the entry should no longer be installed. | +| `deprecated_by` | `string` | No | Registry name of the preferred replacement. | +| `transport` | `object` | Yes | Transport metadata with `supported` and `default`. | +| `install.pip` | `string \| null` | Yes | PyPI package name, or `null` if unavailable. | +| `install.docker` | `string \| null` | Yes | Docker image reference, or `null` if unavailable. | +| `install.npm` | `string \| null` | Yes | npm package name, or `null` if unavailable. | +| `stdio` | `object` | No | Launch config with `command` and `args`. | +| `http` | `object` | No | Launch config with `default_port`, `health_path`, `command`, and `args`. | +| `unix` | `object` | No | Launch config with `socket_template`, `command`, and `args`. | +| `sse` | `object` | No | Launch config with `url_template`, `command`, and `args`. | +| `tools` | `object[]` | Yes | Tool definitions with `name` and `description`. Tool names must be snake_case. | +| `credentials` | `object[]` | Yes | Credential definitions with `id`, `env_var`, `description`, optional `help_url`, and `required`. | +| `tags` | `string[]` | Yes | Search taxonomy tags. Every tag must come from the allowed list below. | +| `categories` | `string[]` | Yes | High-level category labels used by clients. | +| `mcp_protocol_version` | `string` | Yes | MCP protocol version date, such as `2024-11-05`. | +| `hive.min_version` | `string` | If `hive` present | Minimum Hive version supported. | +| `hive.max_version` | `string \| null` | If `hive` present | Maximum Hive version supported, or `null`. | +| `hive.profiles` | `string[]` | If `hive` present | Hive profiles where this server is a strong fit. | +| `hive.tool_namespace` | `string` | If `hive` present | Preferred Hive namespace prefix for tools. | +| `hive.example_agent` | `string` | If `hive` present | URL to an example Hive agent using the server. | + +`_comment` fields are allowed anywhere in the manifest and are ignored by tooling. Use them sparingly to make templates clearer. + +## Transport Examples + +### stdio + +Use stdio when the client launches the MCP server as a subprocess. + +```json +{ + "transport": { "supported": ["stdio"], "default": "stdio" }, + "stdio": { + "command": "uvx", + "args": ["my-server", "--stdio"] + } +} +``` + +### http + +Use HTTP when the server exposes a local or remote web service and can report health. + +```json +{ + "transport": { "supported": ["http"], "default": "http" }, + "http": { + "default_port": 4010, + "health_path": "/health", + "command": "uvx", + "args": ["my-server", "--http", "--port", "{port}"] + } +} +``` + +### unix + +Use Unix sockets for low-overhead local communication on Linux or macOS. + +```json +{ + "transport": { "supported": ["unix"], "default": "unix" }, + "unix": { + "socket_template": "/tmp/mcp-{name}.sock", + "command": "uvx", + "args": ["my-server", "--unix", "{socket_path}"] + } +} +``` + +### sse + +Use SSE when the server already exposes a long-lived stream endpoint. + +```json +{ + "transport": { "supported": ["sse"], "default": "sse" }, + "sse": { + "url_template": "http://localhost:{port}/sse", + "command": "npx", + "args": ["@scope/my-server", "--sse", "--port", "{port}"] + } +} +``` + +## Tag Taxonomy + +| Tag | Use when the server is primarily about... | +| --- | --- | +| `project-management` | Planning, task orchestration, boards, or delivery tracking | +| `communication` | Chat, messaging, notifications, or collaboration workflows | +| `crm` | Customer records, deal pipelines, or account management | +| `finance` | Accounting, budgets, billing, procurement, or payments | +| `developer-tools` | Engineering workflows, source control, build systems, or local tooling | +| `productivity` | General business operations and task execution | +| `data` | Data retrieval, transformation, enrichment, or warehousing | +| `atlassian` | Jira, Confluence, or Atlassian ecosystem integrations | +| `google` | Google Workspace or Google Cloud ecosystem integrations | +| `microsoft` | Microsoft 365, Azure, or Microsoft platform integrations | +| `aws` | Amazon Web Services infrastructure or data services | +| `github` | GitHub repositories, issues, or pull requests | +| `issue-tracking` | Tickets, bugs, defects, or incident queues | +| `calendar` | Scheduling, events, attendees, or meeting coordination | +| `email` | Mailboxes, drafts, sending, or inbox triage | +| `database` | Relational or document database access | +| `search` | Web search, enterprise search, or retrieval APIs | +| `ai` | LLM-adjacent workflows, model providers, or AI-native services | +| `analytics` | Reporting, dashboards, BI, or event analysis | +| `storage` | File storage, buckets, objects, or knowledge repositories | +| `monitoring` | Observability, incidents, uptime, or alerting | +| `security` | Security testing, auth, secrets, posture, or compliance | +| `ecommerce` | Orders, products, storefronts, or fulfillment | +| `hr` | Recruiting, people ops, interviews, or employee systems | +| `marketing` | Campaigns, content, attribution, or lead generation | + +## CI Checks + +The GitHub Action validates: + +1. JSON syntax and schema compliance. +2. Registry naming rules and duplicate names. +3. PyPI package existence when `install.pip` is set. +4. snake_case tool names and UPPER_SNAKE_CASE credential env vars. +5. Allowed tag taxonomy usage. +6. Example manifests under `registry/_examples/`. + +Common fixes: + +- `✗ [SCHEMA]`: compare your manifest against `schema/manifest.schema.json`. +- `✗ [NAME FORMAT]`: rename `name` to lowercase letters, numbers, and hyphens only. +- `✗ [PYPI]`: publish the package first, correct the package name, or set `install.pip` to `null`. +- `✗ [TAGS]`: swap any unsupported tag for one from the taxonomy table above. +- `✗ [README]`: add a sibling `README.md` for real submissions under `registry/servers/`. + +## Review Process + +Maintainers aim to review straightforward submissions within 3-5 business days. + +Reviewers look for: + +- Accurate install instructions and transport metadata. +- Clear credential guidance and working docs. +- Tool descriptions that are specific enough for discovery UIs. +- Appropriate trust tier selection. + +Trust tier promotion usually works like this: + +- New third-party submissions start as `community`. +- Stable entries with reproducible install and auth steps can move to `verified`. +- `official` is reserved for Aden Hive-maintained or explicitly endorsed servers. diff --git a/hive-mcp-registry/LICENSE b/hive-mcp-registry/LICENSE new file mode 100644 index 0000000000..8c94bfd5bd --- /dev/null +++ b/hive-mcp-registry/LICENSE @@ -0,0 +1,215 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +Copyright 2026 Aden Hive Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/hive-mcp-registry/README.md b/hive-mcp-registry/README.md new file mode 100644 index 0000000000..d588e96aab --- /dev/null +++ b/hive-mcp-registry/README.md @@ -0,0 +1,45 @@ +# hive-mcp-registry + +![CI](https://img.shields.io/badge/CI-validate.yml-lightgrey) +![License](https://img.shields.io/badge/license-Apache%202.0-blue) +![Servers](https://img.shields.io/badge/servers-0-lightgrey) + +## What is this? + +`hive-mcp-registry` is the community registry for MCP servers that work well with Hive agents. It provides a portable manifest format, trust metadata, validation tooling, and a generated index so users can discover, compare, and install compatible MCP servers without guessing at transport, credentials, or compatibility details. + +## For Users — Discover & Install Servers + +Use the registry from Hive CLI workflows: + +```bash +hive mcp search github +hive mcp install jira +hive mcp list +``` + +Those commands consume the validated manifests in this repository and the generated `registry_index.json` file. + +## For Contributors — Submit Your Server + +1. Copy [`registry/_template/manifest.json`](registry/_template/manifest.json) into a new folder under `registry/servers//`. +2. Fill in the metadata, add a sibling `README.md`, and run `python scripts/validate.py registry/servers//manifest.json`. +3. Open a pull request and include enough docs for maintainers to verify installation, credentials, and trust tier. + +Full contributor guidance lives in [`CONTRIBUTING.md`](CONTRIBUTING.md). + +## Registry Index + +`registry_index.json` is the machine-readable export that clients can cache or mirror. Once hosted from the default branch, the canonical raw URL will be: + +`https://raw.githubusercontent.com/aden-hive/hive-mcp-registry/main/registry_index.json` + +The index contains the complete manifest payload for every server under `registry/servers/`, sorted alphabetically by name. + +## Trust Tiers + +| Tier | Meaning | Typical criteria | +| --- | --- | --- | +| `official` | Maintained by Aden Hive or an explicitly endorsed upstream owner | Actively maintained, documented, and tested by core maintainers | +| `verified` | Reviewed by maintainers and confirmed to install and authenticate successfully | Clear docs, stable metadata, working install path, and responsive maintainer | +| `community` | Contributor-submitted entry that passes schema and CI checks | Complete manifest, useful README, and no unresolved validation issues | diff --git a/hive-mcp-registry/registry/_examples/http/README.md b/hive-mcp-registry/registry/_examples/http/README.md new file mode 100644 index 0000000000..f85c5a64da --- /dev/null +++ b/hive-mcp-registry/registry/_examples/http/README.md @@ -0,0 +1,3 @@ +# Brave Search Example + +This example shows an HTTP manifest for a search-focused MCP server with a default port, health check path, and one required API credential. It is intended to model externally hosted or locally served HTTP integrations. diff --git a/hive-mcp-registry/registry/_examples/http/manifest.json b/hive-mcp-registry/registry/_examples/http/manifest.json new file mode 100644 index 0000000000..bd6222851d --- /dev/null +++ b/hive-mcp-registry/registry/_examples/http/manifest.json @@ -0,0 +1,95 @@ +{ + "$schema": "https://raw.githubusercontent.com/aden-hive/hive-mcp-registry/main/schema/manifest.schema.json", + "name": "brave-search", + "display_name": "Brave Search MCP Server", + "version": "1.1.0", + "description": "HTTP MCP server for Brave Search web, news, and local discovery APIs.", + "author": { + "name": "Brave Search Team", + "github": "brave", + "url": "https://github.com/brave" + }, + "maintainer": { + "github": "brave", + "email": "support@search.brave.com" + }, + "repository": "https://github.com/brave/brave-search-mcp-server", + "license": "MIT", + "status": "verified", + "docs_url": "https://github.com/brave/brave-search-mcp-server/blob/main/README.md", + "supported_os": [ + "linux", + "macos", + "windows" + ], + "deprecated": false, + "transport": { + "supported": [ + "http" + ], + "default": "http" + }, + "install": { + "pip": "brave-search", + "docker": "ghcr.io/brave/brave-search-mcp-server:latest", + "npm": null + }, + "http": { + "default_port": 4010, + "health_path": "/health", + "command": "uvx", + "args": [ + "brave-search", + "--http", + "--port", + "{port}" + ] + }, + "tools": [ + { + "name": "brave_web_search", + "description": "Search the public web using Brave Search." + }, + { + "name": "brave_news_search", + "description": "Return recent news coverage for a topic or company." + }, + { + "name": "brave_local_search", + "description": "Look up local businesses and points of interest." + }, + { + "name": "brave_image_search", + "description": "Find images related to a search query." + } + ], + "credentials": [ + { + "id": "brave_api_key", + "env_var": "BRAVE_API_KEY", + "description": "Brave Search API key for authenticated search requests.", + "help_url": "https://api.search.brave.com/app/keys", + "required": true + } + ], + "tags": [ + "search", + "data", + "analytics", + "ai" + ], + "categories": [ + "data" + ], + "mcp_protocol_version": "2024-11-05", + "hive": { + "min_version": "0.5.0", + "max_version": null, + "profiles": [ + "research", + "marketing" + ], + "tool_namespace": "brave", + "example_agent": "https://github.com/aden-hive/hive/tree/main/examples/templates/tech_news_reporter" + } +} diff --git a/hive-mcp-registry/registry/_examples/sse/README.md b/hive-mcp-registry/registry/_examples/sse/README.md new file mode 100644 index 0000000000..a5cc06d8d1 --- /dev/null +++ b/hive-mcp-registry/registry/_examples/sse/README.md @@ -0,0 +1,3 @@ +# GitHub SSE Example + +This example demonstrates an SSE-capable GitHub MCP manifest with a local launch command, a streaming endpoint template, and a realistic GitHub authentication requirement for repository and issue workflows. diff --git a/hive-mcp-registry/registry/_examples/sse/manifest.json b/hive-mcp-registry/registry/_examples/sse/manifest.json new file mode 100644 index 0000000000..e75863cadd --- /dev/null +++ b/hive-mcp-registry/registry/_examples/sse/manifest.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://raw.githubusercontent.com/aden-hive/hive-mcp-registry/main/schema/manifest.schema.json", + "name": "github-mcp", + "display_name": "GitHub MCP Server", + "version": "1.4.0", + "description": "SSE-enabled GitHub MCP server for repository browsing, issue workflows, and pull request automation.", + "author": { + "name": "GitHub Integrations Team", + "github": "github", + "url": "https://github.com/github" + }, + "maintainer": { + "github": "github", + "email": "support@github.com" + }, + "repository": "https://github.com/github/github-mcp-server", + "license": "MIT", + "status": "verified", + "docs_url": "https://github.com/github/github-mcp-server/blob/main/README.md", + "supported_os": [ + "linux", + "macos", + "windows" + ], + "deprecated": false, + "transport": { + "supported": [ + "sse" + ], + "default": "sse" + }, + "install": { + "pip": null, + "docker": "ghcr.io/github/github-mcp-server:latest", + "npm": "@modelcontextprotocol/server-github" + }, + "sse": { + "url_template": "http://localhost:{port}/sse", + "command": "npx", + "args": [ + "@modelcontextprotocol/server-github", + "--sse", + "--port", + "{port}" + ] + }, + "tools": [ + { + "name": "github_search_repositories", + "description": "Search public and private GitHub repositories." + }, + { + "name": "github_list_pull_requests", + "description": "List pull requests for a repository." + }, + { + "name": "github_create_issue", + "description": "Create a new GitHub issue." + }, + { + "name": "github_get_file_contents", + "description": "Fetch file contents from a repository path and ref." + } + ], + "credentials": [ + { + "id": "github_token", + "env_var": "GITHUB_TOKEN", + "description": "GitHub personal access token used for API access.", + "help_url": "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens", + "required": true + } + ], + "tags": [ + "github", + "developer-tools", + "issue-tracking", + "communication" + ], + "categories": [ + "developer-tools" + ], + "mcp_protocol_version": "2024-11-05", + "hive": { + "min_version": "0.5.0", + "max_version": null, + "profiles": [ + "developer", + "productivity" + ], + "tool_namespace": "github", + "example_agent": "https://github.com/aden-hive/hive/tree/main/examples/templates/email_reply_agent" + } +} diff --git a/hive-mcp-registry/registry/_examples/stdio/README.md b/hive-mcp-registry/registry/_examples/stdio/README.md new file mode 100644 index 0000000000..ef6a20eda4 --- /dev/null +++ b/hive-mcp-registry/registry/_examples/stdio/README.md @@ -0,0 +1,3 @@ +# Hive Tools Server Example + +This example shows a realistic stdio-only manifest for Hive's built-in tools server. It demonstrates local subprocess launch metadata, a minimal optional credential, and a Hive extension block suitable for developer and research workflows. diff --git a/hive-mcp-registry/registry/_examples/stdio/manifest.json b/hive-mcp-registry/registry/_examples/stdio/manifest.json new file mode 100644 index 0000000000..dcb5dd8f9d --- /dev/null +++ b/hive-mcp-registry/registry/_examples/stdio/manifest.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://raw.githubusercontent.com/aden-hive/hive-mcp-registry/main/schema/manifest.schema.json", + "name": "hive-tools", + "display_name": "Hive Tools Server", + "version": "0.7.1", + "description": "Hive's built-in MCP tools server for file operations, browser automation, and web research.", + "author": { + "name": "Aden Hive Contributors", + "github": "aden-hive", + "url": "https://github.com/aden-hive" + }, + "maintainer": { + "github": "aden-hive", + "email": "opensource@aden.ai" + }, + "repository": "https://github.com/aden-hive/hive", + "license": "Apache-2.0", + "status": "official", + "docs_url": "https://github.com/aden-hive/hive/blob/main/tools/README.md", + "supported_os": [ + "linux", + "macos", + "windows" + ], + "deprecated": false, + "transport": { + "supported": [ + "stdio" + ], + "default": "stdio" + }, + "install": { + "pip": null, + "docker": "ghcr.io/aden-hive/hive-tools:latest", + "npm": null + }, + "stdio": { + "command": "uvx", + "args": [ + "hive-tools", + "--stdio" + ] + }, + "tools": [ + { + "name": "read_file", + "description": "Read files from the local workspace." + }, + { + "name": "write_file", + "description": "Write files into the local workspace." + }, + { + "name": "browser_navigate", + "description": "Open and control browser pages during agent execution." + }, + { + "name": "web_search", + "description": "Search the web for current information when browsing is needed." + } + ], + "credentials": [ + { + "id": "hive_api_token", + "env_var": "HIVE_API_TOKEN", + "description": "Optional token used for Hive Cloud-authenticated tool sessions.", + "help_url": "https://github.com/aden-hive/hive/blob/main/docs/configuration.md", + "required": false + } + ], + "tags": [ + "developer-tools", + "productivity", + "ai" + ], + "categories": [ + "developer-tools" + ], + "mcp_protocol_version": "2024-11-05", + "hive": { + "min_version": "0.5.0", + "max_version": null, + "profiles": [ + "developer", + "research", + "automation" + ], + "tool_namespace": "hive_tools", + "example_agent": "https://github.com/aden-hive/hive/tree/main/examples/templates/deep_research_agent" + } +} diff --git a/hive-mcp-registry/registry/_examples/unix/README.md b/hive-mcp-registry/registry/_examples/unix/README.md new file mode 100644 index 0000000000..23252ea92c --- /dev/null +++ b/hive-mcp-registry/registry/_examples/unix/README.md @@ -0,0 +1,3 @@ +# PostgreSQL Unix Socket Example + +This example models a PostgreSQL MCP server that communicates over a Unix domain socket. It demonstrates socket templates, database-oriented tools, and credentials typically required for secure database access. diff --git a/hive-mcp-registry/registry/_examples/unix/manifest.json b/hive-mcp-registry/registry/_examples/unix/manifest.json new file mode 100644 index 0000000000..88c117fe46 --- /dev/null +++ b/hive-mcp-registry/registry/_examples/unix/manifest.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://raw.githubusercontent.com/aden-hive/hive-mcp-registry/main/schema/manifest.schema.json", + "name": "postgres-mcp", + "display_name": "PostgreSQL MCP Server", + "version": "0.9.0", + "description": "Unix socket MCP server for PostgreSQL schema inspection and read-heavy database workflows.", + "author": { + "name": "Database Tooling Guild", + "github": "dbtools", + "url": "https://github.com/dbtools" + }, + "maintainer": { + "github": "dbtools", + "email": "maintainers@dbtools.dev" + }, + "repository": "https://github.com/dbtools/postgres-mcp", + "license": "Apache-2.0", + "status": "community", + "docs_url": "https://github.com/dbtools/postgres-mcp/blob/main/README.md", + "supported_os": [ + "linux", + "macos" + ], + "deprecated": false, + "transport": { + "supported": [ + "unix" + ], + "default": "unix" + }, + "install": { + "pip": "postgres-mcp", + "docker": "ghcr.io/dbtools/postgres-mcp:latest", + "npm": null + }, + "unix": { + "socket_template": "/tmp/mcp-{name}.sock", + "command": "uvx", + "args": [ + "postgres-mcp", + "--unix", + "{socket_path}" + ] + }, + "tools": [ + { + "name": "postgres_list_tables", + "description": "List tables and views available in the database." + }, + { + "name": "postgres_describe_schema", + "description": "Inspect columns, indexes, and foreign keys for a table." + }, + { + "name": "postgres_query", + "description": "Run a SQL query against the configured PostgreSQL database." + }, + { + "name": "postgres_explain_query", + "description": "Get an execution plan for a SQL query." + } + ], + "credentials": [ + { + "id": "postgres_dsn", + "env_var": "POSTGRES_DSN", + "description": "Connection string for the PostgreSQL database.", + "help_url": "https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING", + "required": true + }, + { + "id": "postgres_sslmode", + "env_var": "POSTGRES_SSLMODE", + "description": "Optional SSL mode override for secure connections.", + "help_url": "https://www.postgresql.org/docs/current/libpq-ssl.html", + "required": false + } + ], + "tags": [ + "database", + "data", + "developer-tools", + "storage" + ], + "categories": [ + "data" + ], + "mcp_protocol_version": "2024-11-05", + "hive": { + "min_version": "0.5.0", + "max_version": null, + "profiles": [ + "developer", + "analytics" + ], + "tool_namespace": "postgres", + "example_agent": "https://github.com/aden-hive/hive/tree/main/examples/templates/local_business_extractor" + } +} diff --git a/hive-mcp-registry/registry/_template/README.md b/hive-mcp-registry/registry/_template/README.md new file mode 100644 index 0000000000..721bf6cf38 --- /dev/null +++ b/hive-mcp-registry/registry/_template/README.md @@ -0,0 +1,3 @@ +# Template README + +Use this folder as a copy-ready starting point for a new registry submission. Replace the placeholder metadata in `manifest.json`, add your real installation and authentication details, and then copy both files into `registry/servers//`. diff --git a/hive-mcp-registry/registry/_template/manifest.json b/hive-mcp-registry/registry/_template/manifest.json new file mode 100644 index 0000000000..bbb4345eda --- /dev/null +++ b/hive-mcp-registry/registry/_template/manifest.json @@ -0,0 +1,141 @@ +{ + "$schema": "https://raw.githubusercontent.com/aden-hive/hive-mcp-registry/main/schema/manifest.schema.json", + "_comment": "Copy this file into registry/servers//manifest.json and replace the placeholder values.", + "name": "my-server", + "display_name": "My Server MCP", + "version": "1.0.0", + "description": "Example MCP server manifest showing the full registry format.", + "_comment_author": "Author identifies the original creator of the server package.", + "author": { + "name": "Example Author", + "github": "example-author", + "url": "https://github.com/example-author" + }, + "_comment_maintainer": "Maintainer identifies who answers review follow-up and keeps the manifest fresh.", + "maintainer": { + "github": "example-maintainer", + "email": "maintainer@example.com" + }, + "repository": "https://github.com/example-author/my-server-mcp", + "license": "Apache-2.0", + "status": "community", + "_comment_trust": "Trust metadata helps users understand support level and lifecycle state.", + "docs_url": "https://github.com/example-author/my-server-mcp/blob/main/README.md", + "supported_os": [ + "linux", + "macos", + "windows" + ], + "deprecated": false, + "deprecated_by": "my-server-v2", + "_comment_transport": "List every transport you support and choose one default for clients.", + "transport": { + "supported": [ + "stdio", + "http", + "unix", + "sse" + ], + "default": "stdio" + }, + "_comment_install": "Set package managers you support and use null when a channel does not exist.", + "install": { + "pip": "my-server", + "docker": "ghcr.io/example-author/my-server:latest", + "npm": "@example/my-server-mcp" + }, + "_comment_stdio": "stdio is the most portable transport for local MCP clients.", + "stdio": { + "command": "uvx", + "args": [ + "my-server", + "--stdio" + ] + }, + "_comment_http": "Use {port} in args when the launch command needs the bound port value.", + "http": { + "default_port": 4020, + "health_path": "/health", + "command": "uvx", + "args": [ + "my-server", + "--http", + "--port", + "{port}" + ] + }, + "_comment_unix": "Use {socket_path} or {name} when the server can bind to a Unix socket.", + "unix": { + "socket_template": "/tmp/mcp-{name}.sock", + "command": "uvx", + "args": [ + "my-server", + "--unix", + "{socket_path}" + ] + }, + "_comment_sse": "SSE endpoints are useful when a server already exposes a long-lived event stream.", + "sse": { + "url_template": "http://localhost:{port}/sse", + "command": "uvx", + "args": [ + "my-server", + "--sse", + "--port", + "{port}" + ] + }, + "_comment_tools": "Describe every tool in plain language so clients can render them well.", + "tools": [ + { + "name": "my_server_search", + "description": "Search records managed by the example server." + }, + { + "name": "my_server_create_record", + "description": "Create a new record in the example service." + }, + { + "name": "my_server_get_status", + "description": "Return the current status and health summary." + } + ], + "_comment_credentials": "Each credential maps to one environment variable that users can set.", + "credentials": [ + { + "id": "my_server_api_key", + "env_var": "MY_SERVER_API_KEY", + "description": "API key used to authenticate to My Server.", + "help_url": "https://github.com/example-author/my-server-mcp#authentication", + "required": true + }, + { + "id": "my_server_workspace", + "env_var": "MY_SERVER_WORKSPACE", + "description": "Workspace slug or tenant identifier for multi-tenant deployments.", + "help_url": "https://github.com/example-author/my-server-mcp#workspace-selection", + "required": false + } + ], + "_comment_discovery": "Tags drive search filtering; categories group the server by broader workflow.", + "tags": [ + "developer-tools", + "data", + "ai" + ], + "categories": [ + "developer-tools" + ], + "mcp_protocol_version": "2024-11-05", + "_comment_hive": "The hive block is optional and only needed for Hive-aware clients.", + "hive": { + "min_version": "0.5.0", + "max_version": null, + "profiles": [ + "developer", + "automation" + ], + "tool_namespace": "my_server", + "example_agent": "https://github.com/example-author/my-server-mcp/tree/main/examples/hive-agent" + } +} diff --git a/hive-mcp-registry/registry/servers/.gitkeep b/hive-mcp-registry/registry/servers/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hive-mcp-registry/registry_index.json b/hive-mcp-registry/registry_index.json new file mode 100644 index 0000000000..264d0fb12e --- /dev/null +++ b/hive-mcp-registry/registry_index.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.0", + "updated_at": "2026-03-14T17:51:56Z", + "servers": [] +} diff --git a/hive-mcp-registry/schema/manifest.schema.json b/hive-mcp-registry/schema/manifest.schema.json new file mode 100644 index 0000000000..76d7c01a3e --- /dev/null +++ b/hive-mcp-registry/schema/manifest.schema.json @@ -0,0 +1,565 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Hive MCP Registry Manifest", + "description": "Schema for portable MCP server manifests with optional Hive-specific extensions.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "type": "string", + "description": "Human-readable note ignored by registry tooling." + } + }, + "required": [ + "name", + "display_name", + "version", + "description", + "author", + "maintainer", + "repository", + "license", + "status", + "transport", + "install", + "tools", + "credentials", + "tags", + "categories", + "mcp_protocol_version" + ], + "properties": { + "$schema": { + "type": "string", + "format": "uri", + "description": "Canonical URL for this manifest schema." + }, + "name": { + "type": "string", + "description": "Portable registry identifier for the MCP server." + }, + "display_name": { + "type": "string", + "description": "Human-friendly name shown in UIs and docs." + }, + "version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$", + "description": "Semantic version of the MCP server manifest entry." + }, + "description": { + "type": "string", + "description": "Short summary of what the MCP server does." + }, + "author": { + "$ref": "#/definitions/author", + "description": "Original creator of the MCP server." + }, + "maintainer": { + "$ref": "#/definitions/maintainer", + "description": "Current maintainer responsible for registry updates." + }, + "repository": { + "type": "string", + "format": "uri", + "description": "Source repository URL for the MCP server." + }, + "license": { + "type": "string", + "description": "Open source license identifier or text label for the server." + }, + "status": { + "type": "string", + "enum": [ + "official", + "verified", + "community" + ], + "description": "Trust tier assigned by the registry maintainers." + }, + "docs_url": { + "type": "string", + "format": "uri", + "description": "Primary documentation URL for installation and usage." + }, + "supported_os": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "linux", + "macos", + "windows" + ], + "description": "Operating system supported by this server." + }, + "description": "Operating systems the server is expected to support." + }, + "deprecated": { + "type": "boolean", + "description": "Whether this manifest points to a deprecated server." + }, + "deprecated_by": { + "type": "string", + "description": "Registry name of the preferred replacement server." + }, + "transport": { + "$ref": "#/definitions/transport", + "description": "Transport modes supported by this MCP server." + }, + "install": { + "$ref": "#/definitions/install", + "description": "Install sources for common package ecosystems." + }, + "stdio": { + "$ref": "#/definitions/stdioTransport", + "description": "Configuration for launching the server over stdio." + }, + "http": { + "$ref": "#/definitions/httpTransport", + "description": "Configuration for launching or reaching the server over HTTP." + }, + "unix": { + "$ref": "#/definitions/unixTransport", + "description": "Configuration for launching or reaching the server over a Unix domain socket." + }, + "sse": { + "$ref": "#/definitions/sseTransport", + "description": "Configuration for launching or reaching the server over Server-Sent Events." + }, + "tools": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/tool" + }, + "description": "Tool declarations exposed by the MCP server." + }, + "credentials": { + "type": "array", + "items": { + "$ref": "#/definitions/credential" + }, + "description": "Credentials and environment variables required by the server." + }, + "tags": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "description": "Discovery tag describing the server domain or ecosystem." + }, + "description": "Search tags used for catalog filtering." + }, + "categories": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "description": "High-level category label for the server." + }, + "description": "Broad product or workflow categories used by clients." + }, + "mcp_protocol_version": { + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", + "description": "MCP protocol version implemented by the server." + }, + "hive": { + "$ref": "#/definitions/hive", + "description": "Optional Hive-specific compatibility metadata." + } + }, + "definitions": { + "commentField": { + "type": "string", + "description": "Human-readable note ignored by registry tooling." + }, + "author": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "name", + "github", + "url" + ], + "properties": { + "name": { + "type": "string", + "description": "Display name of the original author." + }, + "github": { + "type": "string", + "description": "GitHub username of the original author." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Primary homepage or profile URL for the original author." + } + }, + "description": "Author identity information." + }, + "maintainer": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "github", + "email" + ], + "properties": { + "github": { + "type": "string", + "description": "GitHub username for the current maintainer." + }, + "email": { + "type": "string", + "format": "email", + "description": "Contact email for registry review follow-up." + } + }, + "description": "Maintainer contact information." + }, + "transport": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "supported", + "default" + ], + "properties": { + "supported": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "stdio", + "http", + "unix", + "sse" + ], + "description": "One supported transport for this server." + }, + "description": "Transports that this server can speak." + }, + "default": { + "type": "string", + "enum": [ + "stdio", + "http", + "unix", + "sse" + ], + "description": "Default transport recommended to clients." + } + }, + "description": "Transport selection metadata." + }, + "install": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "pip", + "docker", + "npm" + ], + "properties": { + "pip": { + "type": [ + "string", + "null" + ], + "description": "PyPI package name, or null when pip is not supported." + }, + "docker": { + "type": [ + "string", + "null" + ], + "description": "Docker image reference, or null when Docker is not supported." + }, + "npm": { + "type": [ + "string", + "null" + ], + "description": "npm package name, or null when npm is not supported." + } + }, + "description": "Install coordinates for package managers." + }, + "tool": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "type": "string", + "description": "Portable snake_case tool name exposed by the server." + }, + "description": { + "type": "string", + "description": "Short explanation of what the tool does." + } + }, + "description": "One tool exposed by the MCP server." + }, + "credential": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "id", + "env_var", + "description", + "required" + ], + "properties": { + "id": { + "type": "string", + "description": "Stable credential identifier used by clients." + }, + "env_var": { + "type": "string", + "description": "Environment variable name used to supply the credential." + }, + "description": { + "type": "string", + "description": "Explanation of what the credential is for." + }, + "help_url": { + "type": "string", + "format": "uri", + "description": "URL explaining how to obtain or configure the credential." + }, + "required": { + "type": "boolean", + "description": "Whether this credential is required for core functionality." + } + }, + "description": "One credential declaration." + }, + "stdioTransport": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "command", + "args" + ], + "properties": { + "command": { + "type": "string", + "description": "Executable used to launch the server in stdio mode." + }, + "args": { + "type": "array", + "items": { + "type": "string", + "description": "One launch argument passed to the stdio command." + }, + "description": "Arguments passed when launching the stdio server." + } + }, + "description": "Launch information for stdio transport." + }, + "httpTransport": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "default_port", + "health_path", + "command", + "args" + ], + "properties": { + "default_port": { + "type": "integer", + "minimum": 1, + "maximum": 65535, + "description": "Default TCP port used by the HTTP server." + }, + "health_path": { + "type": "string", + "description": "Relative health-check path exposed by the HTTP server." + }, + "command": { + "type": "string", + "description": "Executable used to launch the server in HTTP mode." + }, + "args": { + "type": "array", + "items": { + "type": "string", + "description": "One launch argument passed to the HTTP command." + }, + "description": "Arguments passed when launching the HTTP server." + } + }, + "description": "Launch and connection information for HTTP transport." + }, + "unixTransport": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "socket_template", + "command", + "args" + ], + "properties": { + "socket_template": { + "type": "string", + "description": "Socket path template, optionally using {socket_path} or {name}." + }, + "command": { + "type": "string", + "description": "Executable used to launch the server in Unix socket mode." + }, + "args": { + "type": "array", + "items": { + "type": "string", + "description": "One launch argument passed to the Unix socket command." + }, + "description": "Arguments passed when launching the Unix socket server." + } + }, + "description": "Launch and connection information for Unix domain socket transport." + }, + "sseTransport": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "url_template", + "command", + "args" + ], + "properties": { + "url_template": { + "type": "string", + "description": "SSE endpoint template, optionally using {port} or {name}." + }, + "command": { + "type": "string", + "description": "Executable used to launch the server in SSE mode." + }, + "args": { + "type": "array", + "items": { + "type": "string", + "description": "One launch argument passed to the SSE command." + }, + "description": "Arguments passed when launching the SSE server." + } + }, + "description": "Launch and connection information for SSE transport." + }, + "hive": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^_comment(?:_[A-Za-z0-9-]+)?$": { + "$ref": "#/definitions/commentField" + } + }, + "required": [ + "min_version", + "max_version", + "profiles", + "tool_namespace", + "example_agent" + ], + "properties": { + "min_version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$", + "description": "Minimum Hive version that can consume this manifest." + }, + "max_version": { + "type": [ + "string", + "null" + ], + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$", + "description": "Maximum Hive version supported, or null when open-ended." + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "description": "One Hive profile where this server fits well." + }, + "description": "Hive profile recommendations for this server." + }, + "tool_namespace": { + "type": "string", + "description": "Preferred Hive namespace prefix for the server's tools." + }, + "example_agent": { + "type": "string", + "format": "uri", + "description": "URL to an example Hive agent that uses this server." + } + }, + "description": "Hive-specific compatibility metadata." + } + } +} diff --git a/hive-mcp-registry/scripts/build_index.py b/hive-mcp-registry/scripts/build_index.py new file mode 100644 index 0000000000..7d2e41a14e --- /dev/null +++ b/hive-mcp-registry/scripts/build_index.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json +from datetime import datetime, timezone +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parent.parent +SERVERS_DIR = REPO_ROOT / "registry" / "servers" +INDEX_PATH = REPO_ROOT / "registry_index.json" + + +def load_manifest(path: Path) -> dict: + with path.open("r", encoding="utf-8") as handle: + return json.load(handle) + + +def main() -> int: + manifests = [load_manifest(path) for path in sorted(SERVERS_DIR.glob("*/manifest.json"))] + manifests.sort(key=lambda item: item["name"]) + + payload = { + "version": "1.0.0", + "updated_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"), + "servers": manifests, + } + + with INDEX_PATH.open("w", encoding="utf-8") as handle: + json.dump(payload, handle, indent=2) + handle.write("\n") + + print(f"Built index with {len(manifests)} servers.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/hive-mcp-registry/scripts/validate.py b/hive-mcp-registry/scripts/validate.py new file mode 100644 index 0000000000..53f87395f7 --- /dev/null +++ b/hive-mcp-registry/scripts/validate.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json +import re +import sys +from pathlib import Path +from typing import Iterable + +import requests +from jsonschema import Draft7Validator, FormatChecker + + +REPO_ROOT = Path(__file__).resolve().parent.parent +SCHEMA_PATH = REPO_ROOT / "schema" / "manifest.schema.json" +SERVERS_DIR = REPO_ROOT / "registry" / "servers" +ALLOWED_TAGS = { + "project-management", + "communication", + "crm", + "finance", + "developer-tools", + "productivity", + "data", + "atlassian", + "google", + "microsoft", + "aws", + "github", + "issue-tracking", + "calendar", + "email", + "database", + "search", + "ai", + "analytics", + "storage", + "monitoring", + "security", + "ecommerce", + "hr", + "marketing", +} +NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$") +TOOL_NAME_RE = re.compile(r"^[a-z][a-z0-9_]*$") +ENV_VAR_RE = re.compile(r"^[A-Z][A-Z0-9_]*$") +REQUIRED_FIELDS = ("name", "version", "description", "status") + + +class ValidationError(Exception): + def __init__(self, check_name: str, message: str) -> None: + super().__init__(message) + self.check_name = check_name + self.message = message + + +def fail(check_name: str, message: str) -> None: + raise ValidationError(check_name, message) + + +def manifest_targets(path_arg: str) -> list[Path]: + candidate = Path(path_arg).expanduser() + if not candidate.is_absolute(): + candidate = (Path.cwd() / candidate).resolve() + + if not candidate.exists(): + fail("PATH", f"{candidate} does not exist. Pass a manifest.json file or a directory.") + + if candidate.is_file(): + return [candidate] + + direct_manifest = candidate / "manifest.json" + if direct_manifest.is_file(): + return [direct_manifest] + + manifests = sorted(candidate.rglob("manifest.json")) + if not manifests: + fail("PATH", f"No manifest.json files found under {candidate}. Add a manifest or point to a valid directory.") + return manifests + + +def load_schema() -> dict: + with SCHEMA_PATH.open("r", encoding="utf-8") as handle: + return json.load(handle) + + +def load_json(path: Path) -> dict: + try: + with path.open("r", encoding="utf-8") as handle: + return json.load(handle) + except json.JSONDecodeError as exc: + fail( + "JSON", + f"{path} is not valid JSON (line {exc.lineno}, column {exc.colno}). Fix the syntax and try again.", + ) + except OSError as exc: + fail("FILE", f"Could not read {path}: {exc}. Check the file path and permissions.") + return {} + + +def validate_schema(path: Path, manifest: dict, schema: dict) -> None: + validator = Draft7Validator(schema, format_checker=FormatChecker()) + errors = sorted(validator.iter_errors(manifest), key=lambda err: list(err.absolute_path)) + if not errors: + return + + first = errors[0] + location = ".".join(str(part) for part in first.absolute_path) or "root" + fail( + "SCHEMA", + f"{path} failed schema validation at '{location}': {first.message}. Update the manifest to match schema/manifest.schema.json.", + ) + + +def validate_name_format(path: Path, manifest: dict) -> None: + name = manifest.get("name", "") + if not NAME_RE.fullmatch(name): + fail( + "NAME FORMAT", + f"{path} has name '{name}'. Use only lowercase letters, numbers, and hyphens, for example 'my-server'.", + ) + + +def server_manifests() -> Iterable[Path]: + return sorted(SERVERS_DIR.glob("*/manifest.json")) + + +def validate_name_uniqueness(path: Path, manifest: dict) -> None: + name = manifest["name"] + for other_path in server_manifests(): + if other_path.resolve() == path.resolve(): + continue + other_manifest = load_json(other_path) + if other_manifest.get("name") == name: + fail( + "NAME UNIQUENESS", + f"{path} reuses name '{name}', which is already claimed by {other_path}. Choose a globally unique registry name.", + ) + + +def validate_pypi_package(path: Path, manifest: dict) -> None: + package = manifest.get("install", {}).get("pip") + if package is None: + return + + url = f"https://pypi.org/pypi/{package}/json" + try: + response = requests.get(url, timeout=10) + except requests.RequestException as exc: + fail( + "PYPI", + f"Could not verify PyPI package '{package}' for {path}: {exc}. Check your network connection or use install.pip = null.", + ) + + if response.status_code != 200: + fail( + "PYPI", + f"install.pip references '{package}', but {url} returned HTTP {response.status_code}. Publish the package first or set install.pip to null.", + ) + + +def validate_tool_names(path: Path, manifest: dict) -> None: + for tool in manifest.get("tools", []): + tool_name = tool.get("name", "") + if not TOOL_NAME_RE.fullmatch(tool_name): + fail( + "TOOL NAMES", + f"{path} has tool name '{tool_name}'. Use snake_case names like 'jira_create_issue'.", + ) + + +def validate_credential_env_vars(path: Path, manifest: dict) -> None: + for credential in manifest.get("credentials", []): + env_var = credential.get("env_var", "") + if not ENV_VAR_RE.fullmatch(env_var): + fail( + "ENV VAR", + f"{path} has env_var '{env_var}'. Use UPPER_SNAKE_CASE names like 'JIRA_API_TOKEN'.", + ) + + +def validate_tags(path: Path, manifest: dict) -> None: + invalid_tags = sorted(tag for tag in manifest.get("tags", []) if tag not in ALLOWED_TAGS) + if invalid_tags: + fail( + "TAGS", + f"{path} uses unsupported tags {invalid_tags}. Pick only from the documented registry taxonomy in CONTRIBUTING.md.", + ) + + +def validate_readme(path: Path) -> None: + readme_path = path.with_name("README.md") + if not readme_path.is_file(): + fail( + "README", + f"{readme_path} is missing. Add a non-empty README.md alongside the manifest explaining installation and usage.", + ) + + if not readme_path.read_text(encoding="utf-8").strip(): + fail( + "README", + f"{readme_path} is empty. Add installation and usage guidance before opening a PR.", + ) + + +def validate_required_fields(path: Path, manifest: dict) -> None: + for field_name in REQUIRED_FIELDS: + value = manifest.get(field_name) + if value in (None, "", []): + fail( + "REQUIRED FIELDS", + f"{path} is missing '{field_name}' or it is empty. Fill in all required metadata before submitting.", + ) + + +def validate_manifest(path: Path, schema: dict) -> None: + if not path.is_file(): + fail("FILE", f"{path} does not exist. Create the manifest file and try again.") + + manifest = load_json(path) + validate_schema(path, manifest, schema) + validate_name_format(path, manifest) + validate_name_uniqueness(path, manifest) + validate_pypi_package(path, manifest) + validate_tool_names(path, manifest) + validate_credential_env_vars(path, manifest) + validate_tags(path, manifest) + validate_readme(path) + validate_required_fields(path, manifest) + print("✓ manifest.json is valid") + + +def main() -> int: + if len(sys.argv) != 2: + print("Usage: python scripts/validate.py ") + return 1 + + try: + schema = load_schema() + manifests = manifest_targets(sys.argv[1]) + for manifest_path in manifests: + validate_manifest(manifest_path, schema) + except ValidationError as exc: + print(f"✗ [{exc.check_name}] {exc.message}") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main())