Tools
Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers.
Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers.
| Base URL | https://api.hanzo.ai |
| Operations | 19 |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Specification
HIP-1213 · Tools — The Tool Plane — Draft · read the specification →
/v1/tools is the one registry of everything an org can call: each entry is a
Tool with a Source, a JSON-Schema, a per-(org, project) activation state
and an optional price. Cloud's own typed operations are not here — they are
code, projected onto the fleet's MCP door (HIP-0300); what lives here is rows
(apps/tools/LLM.md:1-9). It is implemented in hanzoai/cloud at apps/tools,
and this HIP states the surface under which its skills, plugins and
external-MCP-server views answer.
Motivation
The plane's views grew their own roots — /v1/skills, /v1/plugins,
/v1/mcp/servers — and the last of these left the /v1/mcp root answered by
two apps, the host's agent door and this one's server collection. The store
decides all three (HIP-0139 §7): every row behind those roots is tools' own, so
each view folds home rather than splitting into an app that would share this
one's store.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
The surface
Every address is under /v1/tools: the registry reads and dispatch at the
root, then three views that are the same rows narrowed. /v1/tools/skills is
the registry narrowed to one source, not a second store
(apps/tools/zipdoc_gen.go:70), and skill activation is a tools row.
/v1/tools/plugins lists this deployment's mounted subsystems and the org's
authored plugins, and POST /v1/tools/plugins/build builds one
(apps/tools/tools.go:43); the build route is the plane's one untyped
operation, declared with its bodies and its prose because its 422 carries
diagnostics outside the declared success shape (apps/tools/tools.go:44-71).
/v1/tools/mcp/servers is the org's registered external MCP servers — the one
way an org gains a server, by url or by picked listing, both writing the
same row (apps/tools/external_mcp.go). Every other operation is typed.
Today's router serves the three views at /v1/skills, /v1/plugins and
/v1/mcp/servers (manifest/apps.go:429); each pair is a line in
hanzoai/cloud openapi/misfiled.txt until the fold lands, and the last fold
vacates /v1/mcp entirely to the host's agent door. The skills app keeps
only /.well-known/agent-skills discovery and the plugins app only
/v1/admin/plugins, both exempt by HIP-0139 §3.2.
The store
The capability owns the tools-* system SQLite set — catalog, activation,
plugins, skills, mcp — each opened only by this package
(apps/tools/catalog.go:171, activation.go:41, pluginstore.go:50,
skillstore.go:57, external_mcp.go:87). Sources register their own
Provider from their own Mount (apps/tools/registry.go:75), so this package
never learns how another source lists or runs its tools; what it owns is the
rows and the door.
Tenancy
Every write and dispatch resolves the caller's org from the validated
principal; activation is keyed (org, project), and an authored plugin is
stored under the authoring principal's org. External-server credentials live in
KMS under the org's custody, never in the row.
Money
A tools-plane dispatch is metered per call: CLOUD_TOOLS_FEE_CENTS prices the
unit per deployment, zero making it free, attributed under kind call
(apps/tools/tools.go:83-84, plugin/tools/main.go:22). A marketplace-priced
call is offered — free ones included, so the gate and the settlement read one
table — to the x402 rail over the internal plane; what a call costs, who is
paid and whether a signature verifies stay entirely on the rail's side
(apps/tools/charge_peer.go:16-33).
Events and observability
It publishes no events on the tenant bus, so a customer's webhooks receive
nothing from it. Beyond the request span, every dispatch appends an audit
record — actor, resource, auth context, outcome — through the shared recorder
(apps/tools/http.go:704-715).
Stage
ga. The manifest row carries no stage field, which is ga by HIP-0139 §8.
Upstreams
Two OSS libraries are embedded on the authored-plugin build path:
evanw/esbuild (MIT) bundles the source to one CommonJS program and
dop251/goja (MIT) is the runtime that compiles — and later executes — it
(apps/tools/pluginbuild.go:24-26). The catalog mirrors the public MCP
registry's listings (registry.modelcontextprotocol.io) as data, replaced
wholesale on sync with local curation held in columns the sync cannot touch
(apps/tools/LLM.md:11-32). Nothing else derives from an OSS upstream.
Rationale
The tempting resolution was a split: a skills app for /v1/skills, this one
for the rest. But skill activation lives in tools-activation, so the split
puts two apps on one store — the defect HIP-0139 §7.2 refuses — and buys a
name for a view that is one WHERE clause. The store decides, and it decides
fold, three times.
Security Considerations
A tool dispatch is credentialed execution on a caller's behalf, so the wrong
implementation hands one org's credentials or activations to another. The
guards: credentials are KMS refs read at run time, never fields of a plugin or
a row; source that contains something shaped like a key is refused rather than
scrubbed; and a plugin in the store is one this deployment has already
compiled and loaded once — the build pipeline is the gate, so a model's claim
that code is fine never substitutes for the runtime accepting it
(apps/tools/tools.go:53-60). Deregistering a server destroys its credential
material even though the KMS interface lacks delete: the ref is overwritten
empty, which is the half that matters for rotation
(apps/tools/LLM.md:48-58).
Four surfaces
| Surface | Reaches this capability as | Coverage |
|---|---|---|
| REST | tools at its own prefix | 19 operations |
| CLI | hanzo tools … | 8 of 19 — the CLI pins the document on its own clock |
| SDK | ToolsApi in every published client | 8 of 19 — the clients are generated at their own release |
| MCP | tool tools on https://api.hanzo.ai/v1/mcp | 19 operations, 2 under the document's own id — ask describe for the rest |
Quickstart
export HANZO_API_KEY=sk-... # console.hanzo.ai → API keysThen the first call — a read that needs nothing but the key. GET /v1/tools, operation get_tools:
hanzo tools getimport { Configuration, ToolsApi } from 'hanzoai';
const api = new ToolsApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getTools();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()cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.ToolsAPI.GetTools(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(&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).getTools();curl https://api.hanzo.ai/v1/tools \
-H "Authorization: Bearer $HANZO_API_KEY"The door reaches tools through the tools tool, which names its 19 operations with its own verbs — this one among them, under a name only the door declares. describe explains any of them:
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "describe",
"arguments": {
"op": "list_mcp_servers"
}
}
}'Answers 200 with object — ok.
Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/tools/activation | Reports which tools are switched on for the caller's org and project. |
PUT /v1/tools/activation | Switches tools on and off for the caller's org and project, and answers with the resulting activated set. |
POST /v1/tools/call | Runs one of the caller's activated tools and answers with its output. |
GET /v1/tools/catalog/{id} | Returns one catalog entry in full: the publisher's description, its repository and site, every package form with the runtime that launches it, and… |
PATCH /v1/tools/catalog/{id} | Sets what WE say about one catalog entry — hidden, featured, official, logo — and answers with the stored listing. |
POST /v1/tools/catalog/sync | Pulls the public MCP registry into our canonical copy and reports what changed. |
GET /v1/tools/catalog | Lists the MCP servers the public registries publish, as we hold them: our canonical copy of registry.modelcontextprotocol.io, plus what we decided… |
DELETE /v1/tools/mcp/servers/{id} | Deregisters one of the caller org's external MCP servers, so its tools leave the registry. |
GET /v1/tools/mcp/servers | Lists the external MCP servers the caller's org has registered. |
POST /v1/tools/mcp/servers | Gives the caller's org one more external MCP server, so its tools join the org's tool plane and the fleet's MCP door. |
DELETE /v1/tools/plugins/authored/{id} | Removes one of the caller org's built plugins, so the runtime can no longer load it. |
GET /v1/tools/plugins/authored | Lists the plugins the caller's org BUILT, newest first, each with the TypeScript as authored. |
POST /v1/tools/plugins/build | Builds and stores one plugin for the caller's org. |
GET /v1/tools/plugins | Reports what this deployment actually mounted: every subsystem the composition root declared and whether it is switched on. |
DELETE /v1/tools/skills/{id} | Removes one of the caller org's authored skills. |
GET /v1/tools/skills/authored | Lists the caller org's OWN skills with their SKILL.md bodies. |
GET /v1/tools/skills | Lists the skills the caller's org can reach — the brand's embedded catalogue plus the org's own authored ones — with each one's activation flag. |
POST /v1/tools/skills | Adds or revises one of the caller org's own skills, and answers 201 with the stored record. |
GET /v1/tools | Lists every tool the caller's org and project can reach, from every source, each flagged with whether it is activated. |
How is this guide?