Lists the MCP servers the public registries publish, as we hold them: our…
Lists the MCP servers the public registries publish, as we hold them: our canonical copy of registry.modelcontextprotocol.io, plus what we decided about…
GET /v1/tools/catalog
| Address | https://api.hanzo.ai/v1/tools/catalog |
| Method | GET |
| Operation | get_tools_catalog |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Lists the MCP servers the public registries publish, as we hold them: our canonical copy of registry.modelcontextprotocol.io, plus what we decided about each entry.
This is the SHELF an org picks from. A listing with a streamable-http endpoint can be enabled as-is — POST /v1/tools/mcp/servers with its id — and its tools then join the org's tool plane and the fleet's MCP door. A listing that only ships a stdio package needs a process to run it, which is why the transports are on every entry rather than implied.
Hidden entries are absent: they are the ones we took off the shelf. A platform SuperAdmin sees them, because the same query answers "what is on the shelf" and "what is in the catalog" and two queries would drift apart.
It is PAGED — 50 by default, 200 at most. The public registry publishes tens of thousands of servers, so an unbounded answer is a twenty-megabyte response and a storefront that renders in a minute. total is the whole match, not the page.
Request
5 fields.
| Field | In | Type | Required | Description |
|---|---|---|---|---|
q | query | string | — | Q matches the name, title or description, case-insensitively. |
featured | query | string | — | Featured keeps only the listings we put on the front of the shelf, and only when it is exactly the string "true". |
official | query | string | — | Official keeps only the vendors' OWN servers — not third-party copies of them — and only when it is exactly the string "true". |
limit | query | integer | — | Limit bounds the page: default 50, maximum 200. |
offset | query | integer | — | Offset skips that many listings. |
Response
| Status | Body | Meaning |
|---|---|---|
200 | mcpCatalog | ok |
200 body — 28 fields.
| Field | In | Type | Always | Description |
|---|---|---|---|---|
catalog | body | MCPListing[] | — | Catalog is this page of listings, featured first, then by name. |
catalog[].description | body | string | — | Description is the publisher's one-line summary. |
catalog[].featured | body | boolean | — | Featured puts the listing on the front of the shelf. |
catalog[].hidden | body | boolean | — | Hidden keeps the listing out of the org-visible catalog. Curation: a sync never changes it. |
catalog[].id | body | string | — | ID addresses the listing in a URL. It is the reverse-DNS NAME with its one slash written as an underscore — reversible, because a namespace never contains an… |
catalog[].logo | body | string | — | Logo is the brand mark to render for the listing — the publisher's icon when the entry carries one, or the one an admin set. |
catalog[].name | body | string | — | Name is the publisher's reverse-DNS name, e.g. |
catalog[].official | body | boolean | — | Official is whether this is the vendor's OWN server rather than someone else's copy of it. |
catalog[].packages | body | MCPPackage[] | — | Packages are the runnable package forms — npm, pypi, oci — each with the runtime that launches it and the transport it then speaks. |
catalog[].packages[].identifier | body | string | — | Identifier is the package name or download URL. |
catalog[].packages[].registry | body | string | — | Registry is where the package is fetched from: npm, pypi, oci, nuget, mcpb. |
catalog[].packages[].runtime | body | string | — | Runtime is the publisher's hint for what launches it: npx, uvx, docker. |
catalog[].packages[].transport | body | string | — | Transport is what the launched process speaks: usually "stdio". |
catalog[].packages[].version | body | string | — | Version is the exact published package version. |
catalog[].registry | body | string | — | Registry is the upstream this row was synced from. |
catalog[].remotes | body | MCPRemote[] | — | Remotes are the hosted endpoints the publisher serves the server at. |
catalog[].remotes[].transport | body | string | — | Transport is "streamable-http" or "sse". |
catalog[].remotes[].url | body | string | — | URL is the endpoint. |
catalog[].repo | body | string | — | Repo is the source repository URL, when the entry names one. |
catalog[].site | body | string | — | Site is the project's homepage, when the entry names one. |
catalog[].synced | body | integer | — | Synced is when this row was last confirmed against upstream, Unix seconds. |
catalog[].title | body | string | — | Title is the human-readable display name, when the entry carries one. |
catalog[].transports | body | string[] | — | Transports are the distinct transports this server can be reached over, sorted: some of "stdio", "streamable-http", "sse". |
catalog[].vendor | body | string | — | Vendor is the namespace half of Name — the publisher, e.g. |
catalog[].version | body | string | — | Version is the published version of this listing. |
limit | body | integer | — | Limit is the page size that was actually applied — the default or the clamp, when the request asked for neither or for too much. |
offset | body | integer | — | Offset is where this page started, so a caller pages from what the server did rather than from what it asked for. |
total | body | integer | — | Total is how many listings the filter matched, which is more than this page holds whenever there is a next one. |
Failure carries the platform error shape — see Errors.
Examples
hanzo tools catalog listimport { Configuration, ToolsApi } from 'hanzoai';
const api = new ToolsApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getToolsCatalog();from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import ToolsApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = ToolsApi(client).get_tools_catalog()cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.ToolsAPI.GetToolsCatalog(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, tools_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = tools_api::get_tools_catalog(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.ToolsApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new ToolsApi(client).getToolsCatalog();curl https://api.hanzo.ai/v1/tools/catalog \
-H "Authorization: Bearer $HANZO_API_KEY"Tool tools, op get_tools_catalog — POST the JSON-RPC envelope to https://api.hanzo.ai/v1/mcp.
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tools",
"arguments": {
"op": "get_tools_catalog",
"input": {}
}
}
}'How is this guide?