iit-mcp-hudu
A read-first MCP server for Hudu IT documentation: it discovers the SDK operation catalog, dispatches reads, searches bodies, and optionally enables gated writes.
Discover operations —
hudu_list_operationspages the curated catalog (filter by resource/effect, mark unexposed rows) andhudu_describe_operationreturns one operation's input contract.Run any read —
hudu_readexecutes ~90 registry read keys (get, resolve, search, getContext, jump, list, etc.) with a closed input contract.Fuzzy search —
hudu_searchsearches article titles/bodies and asset/company/website fields, with index/vendor/typesense tiers, filters, snippets and degradation metadata.Resolve unknown identifiers —
hudu_resolve_anyfans out an id/name/slug/domain across resources and reportsunauthorizedones.Fetch rich context —
hudu_get_company_context,hudu_get_article_context,hudu_get_asset_contextreturn a record with bounded related data (withopts.skip/expand).Look up by slug/id — dedicated tools for asset passwords, websites, folders, password folders, groups and asset layouts.
List collections —
hudu_listreturns one bounded page of companies, articles, assets or asset layouts with per-resource filters and compact summaries (expand:truefor full records).Batch reads —
hudu_fetch_manyfetches up to 20 mixed-resource records in one call with per-item errors and optional field projection.Write (opt-in) —
hudu_write/hudu_deleteappear only underHUDU_WRITE_POLICYallow_list/all; writes default to dry-run and destructive ones need exactconfirm.Safety controls — secret reads (
asset_passwords,password_folders) denied by default, credential fields redacted, results wrapped as untrusted data, stdio/http transports with per-caller Hudu credentials.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@iit-mcp-hudufind Acme Corp's Hudu article about VPN access"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
iit-mcp-hudu
MCP server for Hudu IT documentation, built on the node-hudu SDK.
Tool surface
17 tools by default: hudu_list_operations, hudu_describe_operation, hudu_read,
hudu_fetch_many, twelve core reads, and hudu_list. Setting HUDU_WRITE_POLICY allow_list or
all adds hudu_write and hudu_delete (19 tools). Each dispatcher uses a separate SDK client in
its matching mode; names, annotations and the dispatcher field text are the SDK's curated metadata.
SDK enforcement rejects cross-effect calls before any request. The old hudu_invoke,
hudu_catalog and hudu_describe names are removed; consumers must resync their tool catalog.
hudu_list(resource) replaces the four hudu_list_companies / hudu_list_articles /
hudu_list_assets / hudu_list_asset_layouts tools of 0.2.x (a call to an old name answers with a
pointer to hudu_list). resource selects one of four fixed registry operations (companies.list /
articles.list / assets.listAcrossCompanies / asset_layouts.list); the tool takes exactly one
page and accepts no operation selector or nested invocation, so an ungated reader can page these
collections without the registry dispatcher and without exposing the write surface. Filters go in
filters; each resource accepts only its own set, and any other key is refused naming the accepted
ones (never dropped, which would turn a company-scoped request into an account-wide one). Companies,
articles and assets take page / page_size (capped at 100); asset_layouts takes page only,
because /asset_layouts ignores page_size: a caller bound would truncate the vendor page and skip
rows as page advanced, so the tool returns the vendor page whole and page N is always the
vendor's Nth page. For searchable resources (companies / articles / assets) a resource
excluded by HUDU_SEARCH_RESOURCES is refused by the list tool too, before any request;
asset_layouts is not searchable (the SDK declares no vendor text filter for it), so it follows the
same reachability as the existing ungated hudu_get_asset_layout tool. The tool set is the SDK's
CORE_TOOLS plus hudu_list, which is this server's own; a future SDK upgrade must not re-add the
four old names. Rows are compact summaries by default (id, name, key scalar fields, url,
created_at, updated_at; no custom-field values, article bodies or layout field definitions), which keeps a
25-row assets page under 20,000 characters instead of ~88,000; pass expand: true for the full
records (hudu_read articles.list rows are always full). Article rows carry no body unless expanded. Paging is unchanged (asset_layouts still takes no page_size).
These tools run on node-hudu 0.24.0; the SDK behaviour (0.12.0) section below covers the behaviours that shape them: redirect refusal, the closed input contract, the error envelope, and the client opt-ins. Numeric ids must be positive integers on every tool; anything else is refused before a request is made.
hudu_fetch_many (node-hudu 0.12.0) fetches up to 20 single records across mixed resources in
one call: each item is exactly { resource, id, fields? } — one of the 22 bare-id resources, a
numeric vendor id, and an optional client-side field projection. A shape violation is refused at
the schema layer before any request, and failure is per-item: a miss or a vendor fault lands on
its own row as a typed error while the rest of the batch is served. The deployment's secret-read
policy applies to the batch's items exactly as to a single read, so a credential item is
refused pre-request under the default HUDU_SECRET_READS=deny; HUDU_WRITE_POLICY=deny keeps the tool
— it is a read. The same batch stays reachable through hudu_read with operation
operations.fetchMany, gated the same way.
Related MCP server: Hudu MCP Server
Transports
Transport | For | Caller auth | Hudu credential |
| Claude Code, Claude Desktop, VS Code | none — the process boundary is the trust boundary |
|
| Remote MCP clients | none — private, unpublished network | per request: |
Local use (stdio)
Needs Node 24+, a Hudu API key and your Hudu origin. stdio is the default transport, so no
MCP_TRANSPORT is needed. Mutation tools stay off unless you set HUDU_WRITE_POLICY.
From npm (recommended):
claude mcp add hudu \
--env HUDU_API_KEY=your-key --env HUDU_BASE_URL=https://your.hudu.host \
-- npx -y iit-mcp-huduor in .mcp.json / Claude Desktop config:
{
"mcpServers": {
"hudu": {
"command": "npx",
"args": ["-y", "iit-mcp-hudu"],
"env": { "HUDU_API_KEY": "your-key", "HUDU_BASE_URL": "https://your.hudu.host" }
}
}
}npx fetches the package on first run and caches it; use iit-mcp-hudu@<version> to pin one.
From a clone (for development): see Development. Point your client at
node /absolute/path/to/iit-mcp-hudu/dist/main.js after pnpm run build.
Loading credential values (read this before a live session)
A Hudu credential whose bytes reach this server mangled costs a real authentication failure against your Hudu account, and repeated ones risk an account lock. Two rules keep that from happening.
Load an env file the way the file documents — shell sourcing, not a generic dotenv parse.
set -a; . ./.env; set +a # the .env.example recipe, for a file of single-quoted values.env.example writes its example values in single quotes, which is the normal .env style. A
shell consumes those quotes when the file is sourced, so the value the process sees is exactly
the secret. A quote-preserving dotenv parser (dotenv-style KEY=VALUE splitting that keeps
the quotes) does not: it hands '…' to the server, quotes included, and the vendor rejects it.
The trap is sharpest when the secret itself contains $ — HUDU_API_KEY='a$b' becomes a$b when
sourced and 'a$b' (with quotes) when naively parsed, and both look correct in the file.
The client-config and --env paths shown under Local use pass values verbatim and never parse a
file, so they are already safe — this rule is for anyone loading a .env.
Run doctor before the first live call. It answers "did my credential load intact?" from the
environment alone, with zero Hudu requests:
npx -y iit-mcp-hudu doctor # or: node dist/main.js doctorIt prints, per credential field, the value's length, its two-character prefix2 and a shapeOk
flag with the anomaly names when it is not; plus the origin that will actually be dialed. It exits
non-zero when the credential would be refused, so a runbook step can gate on it:
node dist/main.js doctor && <start the live session>The contract: a green doctor makes any later 401 vendor-side. With doctor clean, a 401 means
Hudu refused a well-shaped credential — a rotated, revoked, or role-restricted key — and the fix is
at the vendor, not in the load recipe. Do not re-dial to test it: a 401 at the auth gate is the
signal to stop and check, because repeated failures risk locking the account. Conversely, a 401
whose envelope names a shape anomaly (credentialShape: "anomalous") points back at the load.
The server enforces the shape itself, so a bad load cannot reach the wire even if doctor is
skipped: a malformed HUDU_API_KEY fails boot; a malformed X-Hudu-Api-Key header is refused
before any client is built, with the typed CREDENTIAL_MALFORMED envelope (value-free, and
retryable: false). Neither path dials. Since node-hudu 0.14.0 the SDK enforces credential shape
itself too — refusing a contaminated value at client construction and attaching a value-free shape
diagnostic to a refused credential — and this server consumes that diagnostic directly on a 401;
the server's own gate stays as the boundary guard, a superset of the SDK's checks.
The server inspects shape only — length, a two-character prefix, and anomaly names. A credential
value is never logged, never echoed in an error, and never returned by /doctor. The anomalies that
fail a value are: preserved surrounding quotes (single or double), an embedded quote, a carriage
return, a newline, an embedded control character (any non-printable ASCII byte besides those),
edge whitespace, and a NUL byte.
Configuration
Copy .env.example. Every variable is validated at boot; the process exits non-zero on an invalid
config rather than failing at the first tool call.
Under http the caller normally supplies both per request: its own Hudu API key in X-Hudu-Api-Key
and the origin that key belongs to in X-Hudu-Base-Url (an absolute http/https origin, no
path — validated by the SDK when the scoped client is built). A client can scope the key
per user or per agent to match how the connection is configured, so a caller can read exactly
what its own Hudu role permits and no more. There is no shared token: the listener is
unauthenticated and must stay on a private, unpublished network.
Both may instead be set as defaults. HUDU_BASE_URL and HUDU_API_KEY are required under
stdio and optional under http, where a request's headers win and the defaults fill in whatever
it omits. Setting HUDU_API_KEY under http means every request without its own key runs as that
key, so set it only when that is intended: with no shared token, the private network is the only
gate on who can use it. It is only ever presented to the default
HUDU_BASE_URL: a request that names some other origin and brings no key of its own gets no
credential and fails closed, so a caller cannot aim the server's key at a host it controls. (A
blank value, as a copied .env.example leaves it, is treated as unset.) Each (origin, credential)
pair gets its own scoped client (its own search index) cached by the pair's hash and capped — at most
two pairs stay warm in EACH mode cache, so a credential occupies one client per cache (read, plus
write and delete when writes are enabled) and a further scope rebuilds its index on demand — so one
caller's indexed article bodies can never answer another's search.
HUDU_ALLOWED_BASE_HOSTS (http only; required unless HUDU_ALLOW_ANY_BASE_HOST=true) restricts which origins a caller may name in
X-Hudu-Base-Url: a comma-separated list of exact hostnames or *.example.com subdomain suffixes
(no scheme, port or path; write an internationalised name in punycode). When it is unset and
HUDU_BASE_URL is set, only that default origin is accepted. When both are unset, any caller-named
origin would be accepted, so anyone who can reach the listener could make the server send requests
(carrying their key) to hosts of their choosing; the server refuses to boot in that case unless
HUDU_ALLOW_ANY_BASE_HOST=true opts in, for a listener only trusted callers can reach. The check is
on the hostname string: it does not resolve DNS or block private addresses, so do not list a name you
do not control. A request naming a host outside the effective policy fails closed on every tool call
without dialing it. Only the hostname is checked for an explicit list: an allowed host is accepted on
any port and over http. The default HUDU_BASE_URL is never checked against an explicit list.
With no usable key (none in the header and no HUDU_API_KEY default) a request is still let
through: initialize and tools/list never reach Hudu, and a client's catalog sync needs them. Such
a request resolves to a credential-less client that throws before any Hudu request is built, so
every tool call fails closed rather than borrowing an identity. A request that has a key but no
origin (no header and no HUDU_BASE_URL default) is let through the same way and fails closed on
every tool call, with the error naming the missing header. A
credential header sent twice (which Node would otherwise join with ", ") is treated as absent
for the same reason.
HUDU_WRITE_POLICY is the write switch. deny (the default) omits hudu_write and
hudu_delete entirely; allow_list permits only the canonical keys in HUDU_WRITE_ALLOW and
registers hudu_write only if the list holds at least one write-effect operation, and hudu_delete
only if it holds at least one destructive-effect one (per the SDK registry's effect) (a write allowed here also allows the writes it delegates to: allowing
articles.patchSection allows articles.update); all permits the SDK's whole mutation surface.
An empty or invalid allow-list fails startup. The SDK clients receive the same allow-list plus
reads needed by helper dependencies.
Writes default to dry-run previews; execution requires dry_run:false, and destructive
operations additionally require exact confirm. A confirmation string is not human consent.
Discovery reports deployment-policy refusals as unreachable.
HUDU_READ_ONLY=true is a hard override of the write policy: no hudu_write / hudu_delete tool is
registered and every mutation is refused, whatever HUDU_WRITE_POLICY and HUDU_WRITE_ALLOW say. Unset or
false leaves HUDU_WRITE_POLICY in charge (default deny). The effective policy is logged once at boot.
Because the two tools are not registered, a client that calls them gets the protocol-level JSON-RPC error
-32602 (Tool hudu_write not found), not a tool error envelope.
HUDU_WRITE_POLICY stays a deployment-wide decision, not a per-caller one. Per-caller
credentials make each caller's Hudu role an external authority too, but that authority is not ours
to trust and cannot be allowed to widen this server's surface: the server-side policy is the floor,
deny by default, and it holds even when a caller presents a key whose Hudu role could write. The
same applies to HUDU_SECRET_READS and HUDU_SEARCH_RESOURCES — all three narrow what this
server will ever offer, independent of what any one caller's key might otherwise permit.
HUDU_SECRET_READS governs the other thing no write policy covers: reads that return a plaintext
credential. asset_passwords.get returns password and otp_secret as an ordinary read, and the
curated tools hudu_find_asset_passwords_by_slug and hudu_get_password_folder reach the same
records. It is deny by default and refuses every operation on the asset_passwords and
password_folders resources — through hudu_read and through those two tools alike, before any
request is issued — so a deployment cannot hand a secret to a model by omission. The batch read
operations.fetchMany (reachable through hudu_read) is refused the same way, per item: its
operation key is not itself a credential read, so each item's resource is checked before any
request, and a mixed batch is refused whole rather than partially dialed. The
cross-resource resolve operations.resolveAny (also reachable through hudu_read) is gated the
same way: under deny an explicitly named credential resource is refused, and an omitted resource
list resolves to the permitted set, which excludes the credential resources from the fan-out. It
is scoped to the
whole resource rather than to the expand flag, because asset_passwords.get returns the secret
without expand. It is independent of HUDU_WRITE_POLICY: all does not imply open secrets, and
allow does not open writes. hudu_search is unaffected — it already redacts hits on those two
resources. Refusals are POLICY_DENIED and appear as unreachable rows in hudu_list_operations.
Password share links (node-hudu 0.16.0): asset_passwords.listShares, getShare, createShare and
deleteShare are credential-resource operations, so they are refused under the default
HUDU_SECRET_READS=deny; the two writes also need HUDU_WRITE_POLICY (or HUDU_WRITE_ALLOW) to permit
them. Under allow, listShares / getShare work and the share url (a bearer link) is masked.
createShare is never runnable here: the SDK refuses it (POLICY_DENIED, reveal_required, dry-run
included, no request) without its reveal capability, this server never grants reveal, and
hudu_list_operations marks the row unreachable. Link-shaped text is scrubbed from share errors.
Observed live, deleteShare of a share that does not exist returns 404 NOT_FOUND on every attempt, repeats included.
This server never creates a share (createShare is unreachable), but under HUDU_SECRET_READS=allow and a permitting
write policy deleteShare will delete any existing share, including one created in the Hudu UI.
hudu_get_company_context accepts opts.skip (assets, articles, websites, asset_passwords); pass
["asset_passwords"] when the key cannot read credentials. The skipped list comes back empty with
skipped set, and no request is made for it. hudu_resolve_any returns unauthorized (the resources the
credential may not read) beside the permitted hits; it errors only when every requested resource refuses.
All SDK clients keep default redaction enabled, including when HUDU_SECRET_READS=allow.
That flag permits credential-resource operations; it never enables plaintext results. The
SDK masks credential fields and confidential or unclassified custom fields as [REDACTED].
Untyped fields may therefore be masked even when an external layout calls them public.
Under deny, both company-context paths additionally project password rows to the summary
field allow-list, dropping free-text descriptions and unknown future fields. No extra layout
lookups are needed. The common result
boundary also uses the SDK redactor with otp_seed, recovery_code, and recovery_codes
as additional secret keys, preserving protections absent from the SDK's default key list.
Result text is a JSON envelope labelled untrusted_data; its fixed instruction marks both
text and structuredContent as data, never instructions or authorization to invoke tools.
HUDU_SEARCH_RESOURCES is the read-edge counterpart: it narrows which resources the cross-resource
reads — hudu_search and hudu_resolve_any, plus the same operations reached through hudu_read
— may offer and query, because a Hudu API key inherits its user's role and may read fewer than the
SDK's eight searchable resources. Unset means the full set; set it to what the credential can read so
the schema never advertises a guaranteed-empty read. A per-resource read failure, and an auth failure
thrown by the resolve fan-out, are reported as unavailable — never with Hudu's own authorization
message, which is a fact about the calling credential, not the caller's query.
Under http each caller's search scope starts cold, so hudu_search uses tier:"auto" rather than
"index": it answers from Hudu search immediately and builds the body index in the background, so a
caller's later search is body-aware without the first one awaiting a corpus-wide walk. The tier
decides whether a call may wait, and the calling model can observe neither the index's warmth nor
the walk's duration, so under http the tier is deployment policy, not a call parameter: the served
schema offers neither tier nor refresh, and a request that still sends either is answered by the
policy path, which records the stripped fields in meta.policyOverride. That path returns
immediately only when the resolved policy is auto or vendor:
with HUDU_SEARCH_TIER_POLICY=index an http call still waits for a complete body-index answer,
which is exactly that knob's documented purpose. meta.index reports the index state (state: cold,
refreshing, partial, warm), and meta.degraded says when the answer was body-blind. Under
stdio the one long-lived index stays warm, both fields stay in the schema, and the default is
"index"; a caller there can still pass tier:"index" for a complete first answer.
HUDU_SEARCH_TIER_POLICY=auto|index|vendor|typesense sets that policy explicitly (default auto
under http, index under stdio, and typesense under both when the Typesense pair is
configured — it has no cold start, so the transport asymmetry does not apply): vendor forces the
vendor tier and never uses a body tier, index lets a call wait for a complete body-index answer,
typesense searches the whole corpus through Hudu's own index. Naming typesense without the pair
fails boot. When HUDU_SEARCH_RESOURCES excludes articles or assets the policy is vendor and no
body tier runs, whatever the variable says: every body tier is corpus-wide.
A search answer also reports which body source produced it: meta.bodySource is index,
typesense or none, and the compact header names it. meta.degraded.reason gains
typesense-unavailable (the configured endpoint refused or timed out; the answer fell back to the
local index or the vendor tier) and typesense-unauthorized (the key was rejected — the key is the
thing to fix), so a body-blind answer always says why.
HUDU_SEARCH_INDEX_TTL (milliseconds, default 600000) is the body index's freshness window. What
the SDK does past it — answer from the stale index while rebuilding in the background — is
UNVERIFIED here: this repo sets the knob (a test asserts it reaches the client) but has not measured
the stale-rebuild behaviour.
HUDU_TYPESENSE_* configure the optional Typesense body tier — the same index Hudu's own UI
searches, so body coverage does not depend on this process's heap:
Variable | Default | Meaning |
| — | Origin only ( |
| — | Search-only key ( |
|
| Articles collection name. |
|
| Assets collection name; must differ from the articles one. |
|
| Per-search deadline; |
|
| Re-read every hit through the Hudu API under the caller's credential (see below). |
HUDU_TYPESENSE_URL and HUDU_TYPESENSE_API_KEY are set together or not at all: one without the
other fails boot, naming both. With both set the server builds its SDK client with this block and
the boot log gains one line, typesense: enabled (collections articles, assets) — the collections
only, never the URL or the key. With both unset nothing changes, boot output included: no Typesense
request is ever made.
Setting the pair makes the typesense tier the default for hudu_search: the call reads the whole
corpus directly from Hudu's index, with no local build. HUDU_SEARCH_TIER_POLICY still chooses the
tier explicitly — index or vendor overrides that default, and naming typesense without the pair
fails boot. The served schema advertises the typesense value only on a deployment that configured
it.
HUDU_TYPESENSE_VERIFY_HITS (default true) is the credential re-read flag. What the SDK does when
it is on — re-read every candidate id through the Hudu API under the credential that made the call
before returning a hit — is the SDK's behaviour: this repo sets the flag (a test asserts it reaches
the client) and has not measured the re-read itself. Hudu's index carries neither folder permissions
nor deletions (it keeps discarded articles), so the re-read is what stops a hit being a record the
caller may not see. false skips it: faster, but a hit can be discarded or permission-hidden. Use
false only on a single-tenant, non-restricted, read-only deployment.
A request cancelled while a call waits on a body-index build answers instead of throwing.
meta.degraded.reason stays body-not-indexed (the SDK's closed union), meta.cancelled records
{ during: "index-build", waited } as a deliberate SERVER extension (the SDK's meta has no
cancellation field; it travels in the open meta object beside the SDK vocabulary, and
meta.policyOverride is the same kind of extension on the http path), and meta.reasons stays empty because no bound bit — the answer
was cancelled, not bounded. Its meta.index.state is cold or refreshing; cold here means no
generation was observed by THIS call, not that the process has no warm index (under stdio a warm
index is possible, and this answer does not claim otherwise). The served hudu_search outputSchema
types meta as an open object on purpose (a closed union costs about 1.1 KB against ~233 B of
surface headroom); the meta vocabulary is enforced by the tests that publish it
(test/tools/search-progress.test.ts), not by the outputSchema.
The body index is bounded by TWO caps derived from the same heap estimate: a text budget and a
document cap. The text budget is a SIXTEENTH of the process's V8 heap ceiling, clamped to 8-64 MiB;
the document cap is one document per KiB of that budget (maxDocs), capped at 50,000. Both
travel the SDK's search options, so neither is an env knob. The divisor moved from 10 to 16 in
round 11 because the tenth left no usable production margin (the arithmetic below).
Where the sixteenth comes from, and what the "4x" is a ratio of. The #51 crash trace is a 512 MiB
container running the OLD behaviour — the SDK-default 64 MiB text budget — which peaked at ~252 MB
of heap and died with Reached heap limit mid-search. It measured the ~90 MB baseline heap and about
4 bytes of index structures per byte of text ACTUALLY HELD, not per byte of budget: that index held
~41 MB of text out of its 64 MiB budget, and the peak reconciles as ~90 + 4 x 41 = ~254 MB. Read
against the budget instead of the held text the same ratio would predict 90 + 4 x 64 = ~346 MB, which
the trace never saw. A 512 MiB container (roughly a 259 MB heap ceiling) now gets a 16 MiB pool,
divided THREE ways for one read-only cache (5.33 MiB per scoped client) or nine ways when writes are
enabled, because read, write and delete each keep their own cache and each counts one eviction of
margin (1.78 MiB per client). The division is an exact byte quotient, so no pool space is lost to
rounding. The share is SIZED so the cache capacity plus one eviction of margin per cache fits the
pool; it is not an enforced peak. A scope evicted by the LRU stays resident while an in-flight
request that resolved it finishes, and because the SDK cannot revoke a live index, concurrent
evictions of distinct credentials can exceed the pool until those requests settle. A standalone walk of a synthetic 25,000-asset / 3,300-article tenant through
the real SDK client under a ~262 MB V8 ceiling (an uncommitted one-off probe; --max-semi-space-size=1
brings V8's reported limit to ~262 MB, while --max-old-space-size=259 alone reports ~451 MB in
Node 24) peaked at ~136 MiB with the ROUND-10 tenth settings; the same walk with the OLD
64 MiB / 50,000 settings peaked at ~147 MiB. Both survived. That probe's baseline is lower than the
server's ~90 MB. A 1 GiB container's ~816 MB V8 ceiling — an ASSUMED ~80% container-to-heap ratio; the MEASURED ratio for the 512 MiB container is ~50.6% (259 MB) — gets a 51 MiB pool and 17 MiB per scoped client (5.67 MiB with writes enabled); a 1024 MB heap ceiling is
what reaches the 64 MiB cap.
The document cap exists because the text budget bounds index TEXT only: a document whose text was
evicted keeps its record and postings, the SDK's own default is 50,000 documents, and a walk can
reach 250 pages x 100 rows x 2 body resources. Pairing the caps bounds that overhead — a 512 MiB
container indexes up to 5,461 documents per scoped client read-only, 1,820 with writes (a stdio
parent carries the whole pool instead: 16,384 documents; writes enabled builds one parent per mode,
so up to three of them carry it), a 1 GiB container's ~816 MB ceiling (assumed ~80%) 17,408 per
client read-only, 5,802 with writes (50,000 in a stdio parent), and only a 1024 MB ceiling 21,845
read-only, 7,281 with writes. The per-document cost is an ESTIMATE (~1 KB of record
plus postings per document, on top of the text ratio), so the pair bounds growth without proving a
peak. HONEST SCOPE: the SDK upserts every walked document and calls its cap only AFTER the walk, so
maxDocs bounds the RETAINED index, not the build-time peak; the peak during a walk is bounded by
the heap ceiling and the text budget's eviction, not by maxDocs. A capped index reports index state
partial, exactly as it already does when the text budget bites.
ARTICLES ARE RESERVED THE WHOLE CAP. Without a reservation the SDK keeps the freshest updated_at
across ALL resources, so on an account with more assets than articles the assets take every slot and
the older articles have no index entry at all. maxDocsByResource (node-hudu 0.20.0) is a hard
reservation inside maxDocs: the server names articles with the whole cap, which makes it
articles-first — each article keeps its slot up to the cap, and assets fill whatever room is left,
in the SDK's updated_at order. That is the resource split the index exists for: article rows carry
the long text, asset rows do not.
A smaller reservation would also be a hard ceiling on articles, so an article-heavy account would
drop articles while its cap sat half empty (measured against the SDK: 300 articles and 5 assets at a
100-document cap retain 30 documents with a quarter reserved and 100 with the whole cap; an
asset-heavy corpus fills the cap either way).
THE ASSET SHARE IS DERIVED, AND CAN BE ZERO: assets keep maxDocs - min(articles, maxDocs) slots.
On the production corpus that is 18,641 of 21,845; where the article corpus meets or exceeds the cap
the assets get none — the 512 MiB write-enabled profile derives a 1,820-document cap against 3,204
articles, so docs.assets.indexed is 0 there and asset hits come from Hudu's own title/custom-field
search, which every call runs. meta.index.docs.<resource>.capped (node-hudu 0.20.0) names the
documents the caps dropped, per resource, so the loss is visible in the answer. The reservation
tracks the cap,
so it deepens with the heap like the rest of the budget: 17,408 article slots at a 1 GiB container's
assumed ~816 MB heap ceiling, 21,845 at a 1024 MB one, 50,000 in a stdio parent. This is what the
Unreleased CHANGELOG entry measures.
MEASURED on the production tenant (19,991 assets + 3,204 articles = 23,195 documents). At the former
division by eight (6,144 documents): 4,666 assets and 1,478 of the 3,204 articles retained, 1,036
article bodies indexed, 317 evicted by the text budget and 3 truncated (the counts do not decompose
the 1,478 — some retained articles carry no body text). Dividing the pool by two instead of eight
was expected (INFERRED at the time) to keep all 3,204 articles at 17 MiB / 17,408 documents; the
deployment measured that expectation FALSE — 15,588 assets and 1,820 articles — because the asset
count fills whatever cap the pool offers. At the 21,845-document ceiling the split was 19,991 assets
and 1,854 articles, i.e. 1,350 articles with no body index, while the asset rows (titles and custom
fields, no long text) held 91% of the cap. Reserving the cap for articles is the fix: the same
21,845-document cap then keeps all 3,204 articles and gives assets the remaining 18,641 slots (the
SDK's reservation arithmetic, reproduced with a synthetic corpus in test/index-reservation.test.ts);
node-hudu #122 has the upstream detail.
BEFORE THE DIVISOR CHANGE. The round-10 end-to-end run measured the real server (dist/main.js) under
a 259 MiB V8 ceiling: peak heapUsed 187.96 MB with a 24.6 MB baseline, so the INDEX STRUCTURES cost
~163 MB. Production's baseline is ~90 MB, so the honest production peak was 163 + 90 = ~253 MB
against 259 MiB — ~98%, about 6 MB of headroom. That leaves no usable margin. The round-10 README
instead added 90 MB to a standalone-walk figure and reported ~233 MB; that understated the risk.
AFTER THE DIVISOR CHANGE (one sixteenth, this round's measurement). The same SHAPE of probe — the
built server under node --max-old-space-size=259 --max-semi-space-size=1 --import <uncommitted heap-sampler.mjs> dist/main.js, stdio against a stub tenant of 25,000 assets (250 pages x 100 rows) and 3,300 articles
with HTML bodies, hudu_search with tier:"index" and refresh:true — peaked at 118.51 MB
heapUsed against a 27.57 MB base, so the structures cost ~90.93 MB. The process survived (exit 0)
and the call took 11.62 s. The index retained 16,384 documents (articles 3,300, assets 13,084) and
reported state partial; bodiesEvicted was 0. PRODUCTION EXTRAPOLATION: 90.93 + 90 = ~180.93 MB,
~70% of the 259 MiB ceiling and ~26 MB below the 207 MB (80%) target. MEASURED PROBE PEAK: 118.51 MB,
~45.8% of the 259 MiB ceiling. The target is met. The fraction shipped is one sixteenth. The
probe scripts were removed from the tree after measuring (the review asked for no ad-hoc probes in
the repo), and the stub was recreated for this round, so the before/after delta also reflects stub
differences and is not a controlled A/B;
the production extrapolation uses this round's measured structures. The stub corpus is synthetic,
its bodies are smaller than production's, and its ~27.6 MB baseline is lower than the ~90 MB
production baseline, so the production figure is an extrapolation. bodiesEvicted 0 means this run
did not exercise eviction. Issue #51's trace remains the only PRODUCTION measurement (~252 MB peak
on the old 64 MiB profile).
The read-through cache (SDK recommended preset), the 429 cooldown gate and the 30 s whole-call deadline are fixed, not env-tunable.
blockPrivateHosts deliberately gets no override: it is the literal-IP SSRF floor for
caller-named origins — a security property, not a deployment preference.
Article bodies are Markdown, with an HTML passthrough (node-hudu 0.14+)
hudu_read and hudu_write pass contentFormat: 'markdown' to the SDK, and hudu_get_article_context
asks for Markdown, so single-article reads (articles.get, and articles.resolve / articles.getContext only with opts.expand: true) are returned as Markdown, and an article write body that does not open with block-level HTML is accepted as Markdown and converted to HTML before the write. A committed article write returns a summary, not the stored record: id, name, slug, updated_at, url, and, for a body
write (articles.create / articles.update with data.content), content_match, sent_bytes and stored_bytes. content_match is
true when the stored body is exactly what the SDK sent — the body verbatim when it opens with block HTML or the caller set
opts.format: 'html', otherwise markdownToHtml of the Markdown at the pinned node-hudu 0.24.0 — so Hudu's rewrite of inline image
src to /public_photo/<slug> is the expected reason to be false; element_deltas (table, pre, a, img) then names the
stored-minus-sent opening-tag counts. articles.patchSection carries a weaker content_match — a presence check that the converted
section sits inside the stored body — because the write response holds only the post-patch record (the SDK fetches, patches and
converts internally and its opts are closed, so a strict comparison would need an extra GET this tool does not make); it has no
sent_bytes and no element_deltas. A field-only articles.update (no data.content) carries neither content_match nor
sent_bytes, only stored_bytes. The full stored body comes back from articles.get with opts.format: 'html', or hudu_fetch_many
for the same. Hudu callouts (> [!INFO]), accordions, task
lists and Mermaid fences round-trip as the dialect documents. Observed live (Hudu 2.48.1), a ```mermaid fence is stored as <pre class="language-mermaid"><code class="language-mermaid">...</code></pre> (entity-escaped, indentation preserved) and reads back as the identical fence, including through articles.patchSection. The read path matches the <pre> by class and takes its text, so it accepts both that form and the bare <pre class="language-mermaid"> form. That is also what the Hudu editor itself stores (checked on 2026-10-25 against a diagram saved in the UI editor): the same <pre class="language-mermaid"><code class="language-mermaid"> element pair, plus a data-id attribute on the pre (and on other blocks such as <p>). A Markdown round trip drops those editor data-id attributes and keeps the diagram text unchanged; it does not use the code-block-mermaid-source class. Rows from hudu_fetch_many, hudu_read articles.list, articles.search and articles.findBySlug still carry stored HTML:
those operations take no format. Rules a caller needs:
A write's
data.contentis parsed as Markdown unless it opens with block-level HTML (<p>,<div>,<h1>-<h6>,<table>, lists,<pre>,<blockquote>,<hr>, Hudu'smce-accordion) or the caller setsopts.format: 'html'explicitly; in either case it is sent as HTML and stored as sent (the API keeps the body verbatim, apart from rewriting inline imagesrcto/public_photo/<slug>). Re-read an article througharticles.getbefore editing it.A Markdown
articles.updateis refused when the stored article holds something the dialect cannot express (for example a<video>); send HTML to keep it. The loss guard runs on the Markdown path ofarticles.updateonly — reached directly or througharticles.patchSection— not onarticles.create(nothing stored to compare) and not on a body that opens with block HTML (stored as sent, unconverted, unguarded). The dry-runexpectedUpdatedAtstaleness check is the same Markdown path only: a dry-run HTML update with a staleexpectedUpdatedAtneither fetches the record nor throws.Prefer Markdown (or editor-native HTML): Hudu reshapes API-written HTML on the next UI save (h5/h6 flattened,
small/abbr/q/section/asideunwrapped,theadremoved, an unknown callout type becomes info).articles.getSection/articles.patchSectionread or edit one section as Markdown, with dry-run.articles.getSectioncarries the samemarkdown_lossy/markdown_lossy_codessignal asarticles.get; it describes the whole article, so the loss may be in another section.A Markdown read can be incomplete: anything the dialect cannot express is dropped or reduced (
<video>and<script>are dropped entirely).hudu_read(articles.get/getSection, andresolve/getContextwithopts.expand: true) andhudu_get_article_contextwithopts.expand: truethen setmarkdown_lossy: true(withmarkdown_lossy_codes, the SDK'sdiffArticleRoundTripcontent-loss codes) and add one sentence to the result text; fetch the stored HTML withhudu_fetch_many({items:[{resource:"articles", id}]}) to see the full body. The read is one fetch: the SDK converts the body and reports the loss on the record (no second request); a caller's ownopts.format: "html"returns the stored HTML untouched with no flag. Callouts, accordions, task lists, Mermaid, inline<svg>and<iframe>round-trip cleanly and are never flagged; a lossless read is unchanged. A body that converts to nothing (only<video>,<input>,<p></p>) reads ascontent: ""withmarkdown_lossy_codescontainingROUNDTRIP_BODY_EMPTY(it used to fail withCONVERSION_EMPTY_OUTPUT). The SDK fails a Markdown read withCONFIG_ERRORwhen the Markdown form exceeds 256 KiB.articles.resolve,articles.getContextandhudu_get_article_contextcarry no lossy signal in the default mode: the result holds an article summary (no body), so there is nothing to convert. Only withopts.expand: trueis the full record returned (for getContext, asarticle) with a Markdown body, and then the samemarkdown_lossy/markdown_lossy_codessignal applies.
SDK behaviour (0.12.0)
Three SDK behaviours changed in the 0.12.0 line and shape a deployment:
Redirects are refused, not followed. Every Hudu request is sent with
redirect: 'manual'; a 3xx answer (any except 304) is aREDIRECT_BLOCKEDerror carrying thelocation, and the SDK never follows it. If a reverse proxy in front of the Hudu origin answers 301/302 (scheme upgrade, trailing slash, host rename), dial the final origin instead — the fix is topology, not config.Reads have a closed input contract.
hudu_readnow refuses unknown input fields at the schema layer with a typedCONFIG_ERRORnaming the accepted fields, before any request;hudu_describe_operationserves the same contract asinput_contract. The batch readoperations.fetchMany(up to 20 single records across mixed resources in one call) is part of thehudu_readsurface; this server also registers the dedicatedhudu_fetch_manytool from the 0.12.0 CORE profile, with the secret-read policy applied to the batch's items on both paths.Error envelopes carry the status in a field. The error
messageis the vendor's short detail (or a fixed default), never the status or the dialed host; those travel in their own fields, and a long upstream body is still replaced by the response shape, not served. The envelope also carries the SDK'scategory,retryable,operation,suggestedAction,fieldErrorsanddetailswhere the error has them,notSenton a locally issued 429 cooldown refusal, andlocationon aREDIRECT_BLOCKEDrefusal; the status field ishttpStatus. A 401/403-class credential refusal collapses to the fixedUNAUTHORIZEDenvelope on every tool — the vendor's own message never crosses the boundary, and the credential-shaped keys are redacted out of the payload the same way as the success path. Two honest corners:detailsmay carry the SDK's own 256-char vendor body snippet, labelled untrusted like the rest of the envelope, and thelocationof aREDIRECT_BLOCKEDrefusal is the vendor's own target, served so the deployment can report or verify it.
Every client the server builds — the parent, the fail-closed placeholders and each (origin, credential) scope — opts into four 0.12.0 behaviours:
profile: 'agent': the conservative agent preset for every option this server leaves unset (1 retry, 240/min rate limit, concurrency 3, bounded resolution scans). The rate-limit bucket is per client, i.e. per scope. The preset stays a deployment-wide constant with no env override; the one deadline it would otherwise govern is pinned explicitly instead (a fixed 30 s, below), and the 429 now arms the cooldown gate instead of retrying blindly.cache: 'recommended'— fixed on: the per-entity read-through TTL preset (1 h structural / 5 min mutable / 1 min base). Caches are per client, so per scope — and a scope's client survives only while it stays in its mode cache, which holds two pairs: a deployment serving more than two credentials therefore re-fetches more from Hudu after an eviction, the same breadth-for-depth trade the body index makes. The write and delete clients have their own caches, so after everyhudu_write/hudu_deleteattempt that is not a dry run (including a failed or timed-out one, which may have applied) the server clears the read client's cache for the same scope: a record read (get, getSection, fetch_many, article context) after a write in the same server process is fresh. A write made by another process or deployment can still be served stale for up to the TTL, because caches are per process. Thehudu_searchbody index is a separate store that a write does not refresh: after a write, passrefresh: trueor wait for the index TTL (SDK default 10 min) before relying on a body search to find new text.blockPrivateHostson the per-scope clients: the literal-IP SSRF floor for CALLER-named origins, fail-closed at client construction. It complements theHUDU_ALLOWED_BASE_HOSTShostname matcher (which alone can express wildcard suffixes) and holds even in bring-your-own-origin mode, where that matcher is wide. The operator's own base — theHUDU_BASE_URLunderstdio, the default origin underhttp— is trusted at the process / env boundary and is never floor-checked, so an on-prem install whose Hudu sits on a literal private IP is unaffected by the upgrade. Deliberately no env override: it is a security property, not a deployment preference.cooldown: { enabled: true }— fixed on: the 429 cooldown gate; a call refused locally by the gate answers withnotSent: truein the error envelope, so a model does not read a local refusal as a vendor rejection of a sent call.
The whole-call deadline is a fixed 30000 ms:
it is always passed to the SDK explicitly, so it stays above the agent profile's 20 s
regardless of preset — the preset's 20 s would shorten a cold tier:'index' body-index walk
that the 0.9.2 deployment tolerated at the SDK's 30 s, and a cold-scope search must not start
timing out because of an upgrade.
These are deployment-wide constants, not per-caller and not env-tunable.
Development
git clone https://github.com/msoukhomlinov/iit-mcp-hudu.git
cd iit-mcp-hudu
pnpm install
pnpm run build
pnpm test
pnpm run devCI (.github/workflows/ci.yml) runs typecheck, lint and test on every push and pull request.
node-hudu is pinned to 0.24.0. The catalog hash test detects metadata drift; upgrades require
review of the client authority, operation enums and redaction regressions. Docker uses pnpm install --frozen-lockfile.
Deployment
Dockerfile builds a non-root runtime image. The healthcheck is liveness-only and deliberately does
not call Hudu — otherwise an upstream outage would cycle the container, turning a partial failure
into a total one.
GET /doctor (same trust model as /healthz: unauthenticated, answered before the MCP handler,
zero Hudu requests) serves the same zero-wire credential-shape report the doctor subcommand
prints, so a deployment can check its loaded credential without a vendor dial.
The deployment definition is docker-compose.yml: its own Compose project, joining an existing
app_net external network (create it with docker network create app_net, or rename it in the
file). The listener is not published to the host, so put a TLS-terminating reverse proxy in front of
it, or share the network with the client that calls it. Copy .env.example to .env and fill it
in, then deploy from a clean git checkout with:
scripts/deploy.shThe script tags the build with the git short SHA of HEAD (iit-mcp-hudu-iit-mcp-hudu:<sha>) —
never :latest — so the running container is always tied to a commit.
Rolling back
A rebuild cannot touch a previous tag, so it stays resolvable in the local image store. Roll back from the same checkout — and only from it:
docker image inspect iit-mcp-hudu-iit-mcp-hudu:<previous-sha> >/dev/null # must exist locally
HUDU_IMAGE_TAG=<previous-sha> docker compose up -d --no-build --pull never--no-build and --pull never are not optional: the service declares both image and build,
so without them Compose would fall back to pulling — or building the current source under the old
tag — if the image is missing, a rollback that deploys the new code instead. The compose file
also pins pull_policy: never as the backstop for any invocation, and the docker image inspect
line makes a pruned or absent image fail loudly before anything is started.
The image that was running before the first tagged deploy was :latest (unpinned); until a newer
pinned tag exists it remains the rollback target via the same two commands with latest.
Release checklist
Server identity (
name/versionreported ininitialize) is hardcoded insrc/tools/server.ts, becauserootDir: "src"forbids importingpackage.jsonfrom across the repo root. Bump it there as well as inpackage.json.BUILT_AGAINST_PLAN_HASHinsrc/tools.tspins the SDK catalogue this server was built against. If its test fails after an SDK upgrade, re-read the catalogue and re-check the 19 registered tools, then move the constant. Never move it to make CI green — that is the one thing it exists to stop.
Available Tools
17 toolshudu_describe_operationDescribe One OperationARead-only
Full input contract, effect, dry-run support, errors and usage for ONE operation, by exact catalog key (never a tool name); an unknown key is CONFIG_ERROR naming the nearest.
| Name | Required | Description | Default |
|---|---|---|---|
| operation | Yes | Canonical operation key exactly as hudu_list_operations prints it, e.g. "companies.update". Never a tool name; there is no fuzzy match. |
Output Schema
| Name | Required | Description |
|---|---|---|
| op | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint=true and openWorldHint=false, so the description only needs to add context, and it does: it discloses that failures are surfaced as CONFIG_ERROR and that the error names the nearest key. It does not mention limits on size or response shape beyond the output schema, but the safety profile is consistent with the annotations (no contradiction).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence with the key constraint and the error behavior front-loaded, and nothing restating the name or title. The enumeration ('input contract, effect, dry-run support, errors and usage') is compact but slightly telegraphic, and the parenthetical repeats the schema's 'never a tool name' warning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read-only introspection tool with an output schema, the definition covers purpose, valid input form, failure mode and what the caller learns, so nothing needed to invoke it correctly is missing. Return-value details are correctly left to the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single required parameter already documented as a canonical operation key copied exactly from hudu_list_operations, so the baseline is 3. The description adds real value on top by specifying the consequence of a malformed value (CONFIG_ERROR naming the nearest) and by ruling out tool names and fuzzy matching, reducing invalid calls.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('describe ... ONE operation') plus the invocation form ('by exact catalog key, never a tool name'), which cleanly separates it from hudu_list_operations (enumerates keys) and hudu_read (executes an operation). An agent can tell what it gets back: input contract, effect, dry-run support, errors, usage.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Makes the precondition of an exact catalog key explicit and warns that tool names are not accepted and there is no fuzzy match, which is the main misuse risk. It stops short of stating the intended flow (call hudu_list_operations first, then hudu_describe_operation, then hudu_read) or any when-not condition.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_fetch_manyFetch Many Records (Batch Read)ARead-only
Fetch up to 20 single records across MIXED resources in ONE call: each item is EXACTLY { resource, id, fields? }. All items are validated first (a CONFIG_ERROR names every problem, zero wire); one failed item never aborts the rest. Article bodies: rows here are stored HTML.
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | 1-20 items, each exactly { resource, id, fields? }. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Items that fetched a record. |
| failed | Yes | Items that failed (a miss or a vendor fault) — the typed error is on the item row. |
| results | Yes | One outcome per input item, in input order: { resource, id, found, value, error? }. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, and the description goes well beyond them: it discloses that all items are validated before any wire call, that a CONFIG_ERROR enumerates every problem with zero requests sent, and that per-item failures do not abort the batch. The note that article body rows are stored HTML is a real output-shape caveat an agent would otherwise get wrong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the capability and cap, then the item shape, then failure semantics and the HTML caveat — a sensible ordering. Slightly over-emphatic all-caps and the one clause that duplicates the schema shape cost it the top mark, but there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and the description covers the cap, validation ordering, partial-failure behavior, and an output encoding caveat. It omits whether results are returned in input order and whether 'fields' projection changes the response shape, which an agent might want for a 20-item batch.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the item shape is fully enumerated in the schema, so the baseline of 3 applies. The description restates the shape ('each item is EXACTLY { resource, id, fields? }') rather than adding meaning, though the emphasis on 'EXACTLY' usefully warns against extra keys given additionalProperties=false.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and scope: 'Fetch up to 20 single records across MIXED resources in ONE call,' which immediately separates it from the single-record sibling hudu_read and from list/search style siblings. The batch semantics (mixed resource types, per-item addressing) are explicit rather than implied.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The batching intent is implied by 'in ONE call' and the 20-item cap, and 'one failed item never aborts the rest' hints at when this beats sequential reads. However, no sibling is named and there is no explicit when-not guidance (e.g. use hudu_read for a single record, hudu_search for criteria-based lookup), so the agent must infer the routing rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_find_asset_passwords_by_slugFind Asset Passwords By SlugARead-only
Find one password record by its URL slug, instead of a full scan. At most one exact match; to enumerate use hudu_search. Credential-shaped fields are masked by default; expand:true returns the full record.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| slug | Yes | Exact slug of the asset password. |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds real behavioral context beyond annotations: credential-shaped fields are masked by default, and expand:true returns the full record. It stops short of covering auth requirements or rate limits, but the masking disclosure is an important non-obvious trait.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences with no waste. The core operation is front-loaded, followed by scoping, the sibling alternative, and the important masking/expand caveat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has two parameters, one nested options object, an output schema, and read-only annotations. The description covers the key behavioral gap (masked fields by default) and the exact-match limitation, so an agent has everything needed to call it correctly without redundant return-value documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%, so the description must carry weight, and it does: it clarifies that slug is a URL slug with exact-match semantics and explains what expand:true changes about the returned record. The schema already describes expand as 'Full records, not compact summaries,' so the description adds complementary masking context rather than restating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: find one password record by its URL slug. It explicitly contrasts this with a full scan and names hudu_search as the enumeration alternative, so an agent can distinguish it from siblings without inspecting schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit guidance: use this tool instead of a full scan for a single exact match, and use hudu_search to enumerate. The alternative is named directly, and the 'at most one exact match' condition sets clear expectations for when this tool applies.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_find_websites_by_slugFind Websites By SlugARead-only
Find one website by its URL slug, not a full scan. At most one exact match; to enumerate use hudu_search or hudu_get_website. expand:true returns the full record.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| slug | Yes | Exact slug of the website. |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered. The description adds useful cardinality behavior ('at most one exact match') and the effect of expand:true, but is silent on what a non-matching slug produces (empty result vs error) — a real question for a lookup tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, each earning its place: purpose, cardinality, alternative routing, and the expand flag. Purpose and the anti-pattern are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists so return shape needn't be explained, annotations cover safety, and the description covers scope, cardinality, alternatives, and the one optional parameter's effect. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%, but both leaf parameters are already documented in the schema (expand: 'Full records, not compact summaries'; slug: 'Exact slug of the website'). The description only restates these plus the word 'URL', so it adds marginal meaning; baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find one website by its URL slug') plus the scope boundary ('not a full scan'). It names the siblings to use instead for enumeration, so an agent can distinguish it from hudu_search and hudu_get_website without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: this tool for one exact-slug lookup, hudu_search or hudu_get_website for enumeration. It also sets the expectation 'At most one exact match', which tells the agent what this call can and cannot return.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_api_infoGet Api InfoARead-only
Return the API information document. Prefer api_info.get. Omit identifier for the tenant-wide document; expand:true returns the full record.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds useful behavior about tenant-wide versus identified documents and expanded records, but does not discuss auth needs, rate limits, or other operational nuances.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the primary action and followed by routing and parameter guidance. Every sentence has a clear purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema and annotations reduce what the description must cover, and it explains the main tenant-wide/expand behavior. However, given the complex `identifier` parameter and 0% schema description coverage, the description leaves a meaningful gap about how to specify a non-tenant document.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the burden for both parameters. It clarifies that omitting `identifier` yields the tenant-wide document and that `expand:true` returns full records, but it never explains what `identifier` represents or which of its many accepted forms (integer, string, or object with id/name/slug/domain/external_id) should be used.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Return the API information document.' This is clearly distinct from the generic sibling tools such as hudu_read, hudu_search, and hudu_list, which do not target API metadata.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit usage guidance: 'Prefer api_info.get' names an alternative, and 'Omit `identifier` for the tenant-wide document; expand:true returns the full record' clarifies key invocation choices. It lacks explicit when-not-to-use guidance but is otherwise clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_article_contextGet Article ContextARead-only
Fetch an article with its company and folder context (ArticleContext). Pass the numeric id, not a slug. Bodies here are Markdown; a refused sub-list answers UNAUTHORIZED.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Numeric article id; articles.resolve finds it. | |
| opts | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| context | Yes | |
| skipped | No | |
| markdown_lossy | No | |
| markdown_lossy_codes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint; the description supplies behavioral detail they don't cover: bodies are returned as Markdown, and an unauthorized sub-list is surfaced as UNAUTHORIZED rather than silently dropped. It doesn't mention pagination or size limits, but the error-shape and content-format disclosures are substantive additions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with what is returned, then how to address it, then format/error behavior. The UNAUTHORIZED clause is terse to the point of being slightly cryptic, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read with an output schema, it covers identity constraints, body format, and an error convention — enough to call it correctly. The remaining gap is expand semantics, which an agent must dig out of the nested schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 50% schema coverage, the description compensates on `id` by reinforcing numeric-not-slug and naming articles.resolve. It says nothing about `opts.expand`, whose only meaning lives in the schema's one-line description, so half the surface is undocumented by the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource plus its scope: fetch an article together with company and folder context (ArticleContext). That distinguishes it from the generic hudu_read and from the sibling context tools (hudu_get_company_context, hudu_get_asset_context) by naming the resolved entity type.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Adds a real selection constraint — 'Pass the numeric `id`, not a slug' — and points at articles.resolve for finding the id via the schema. But it never says when to prefer this over hudu_get_folder, hudu_read, or the other context tools, leaving the choice to inference from naming.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_asset_contextGet Asset ContextBRead-only
Fetch an asset with its context (AssetContext). Pass identifier: id, name, slug or serial. A refused sub-list answers UNAUTHORIZED.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| context | Yes | |
| skipped | No | |
| markdown_lossy | No | |
| markdown_lossy_codes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds one genuinely useful behavioral fact — that a refused sub-list returns UNAUTHORIZED, implying partial results are possible — but says nothing about pagination defaults, the effect of expand, or the size of the context payload.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short, front-loaded sentences with no filler — the core action comes first, then the identifier forms, then the error behavior. Terse jargon like 'AssetContext' and 'UNAUTHORIZED' is used without explanation, but the structure is efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description need not explain return values, and the annotations cover safety. The remaining gap is modest: opts (limit/expand) and the shape/size of the returned context are not addressed, which matters for a nested-object tool with a 100-row cap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description has to compensate. It correctly enumerates the accepted identifier forms (id, name, slug, serial), which maps onto the anyOf/object schema, but it leaves opts.limit and opts.expand entirely unexplained in the description. Note the slight mismatch between 'serial' and the schema's 'primary_serial'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: fetch an asset together with its context (AssetContext). That distinguishes it from generic siblings like hudu_search or hudu_read, though it does not explicitly contrast with the closely named hudu_get_company_context / hudu_get_article_context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It says how to call the tool (pass an identifier) but never says when to prefer it over hudu_search, hudu_read, hudu_resolve_any, or the other *_context siblings. There are no conditions, prerequisites, or exclusions offered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_asset_layoutGet Asset LayoutARead-only
Resolve an asset layout from an id, name or slug; asset_layouts.get is cheaper for a known id. expand:true returns the full AssetLayout, not the summary.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so safety is covered. The description adds genuine behavioral context the annotations lack: a relative cost characteristic versus the direct getter and the output-shape difference between expand:true (full AssetLayout) and the default summary.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences, zero filler, with the resolution capability and the cheaper-alternative caveat front-loaded before the expand semantics. Every clause carries information an agent would otherwise have to guess.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-shape explanation is unnecessary, and annotations cover read-only/open-world semantics. The description covers resolution inputs and the expand trade-off, but does not state failure behavior (unknown identifier, ambiguous name) or the full set of accepted identifier forms.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and `identifier` is a nested anyOf (integer, string, or object with id/name/slug/domain/external_id), so the description carries real weight. It correctly explains that id, name, or slug resolve the layout, but omits the object form, the `domain` and `external_id` keys, and does not clarify bare-integer/string behavior, leaving part of a complex parameter undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Resolve an asset layout') and immediately clarifies the accepted identifier kinds and the cheaper sibling path for known ids. An agent can distinguish this from `asset_layouts.get` or `hudu_resolve_any` without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative (`asset_layouts.get`) and the condition that selects it ('for a known id'), and states when to set expand:true. It stops short of guiding preference against the other resolution-adjacent siblings like `hudu_resolve_any` or `hudu_get_asset_context`, so it is strong but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_company_contextGet Company ContextARead-only
Fetch a company with its bounded context (CompanyContext). Required: numeric id, never a name or slug. Without password_access it 401s unless opts.skip omits asset_passwords; a refused sub-list answers UNAUTHORIZED.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Numeric company id. | |
| opts | No | Read options: limit, expand, skip. |
Output Schema
| Name | Required | Description |
|---|---|---|
| context | Yes | |
| skipped | No | |
| markdown_lossy | No | |
| markdown_lossy_codes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnlyHint/openWorldHint annotations by disclosing the auth requirement (401 without password_access), the workaround (skip asset_passwords), and the per-sub-list failure mode (a refused sub-list returns UNAUTHORIZED). That is exactly the context annotations do not carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact clauses with no filler, front-loading what the tool returns, then the required-parameter constraint, then the failure behavior. Every phrase carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need no explanation, and the nested `opts` details are fully covered by the schema. Combined with the auth and error disclosure, nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it reinforces that `id` is numeric-only and explains how `opts.skip` interacts with the auth model. The `limit` CONFIG_ERROR behavior is documented only in the schema, not the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Fetch a company with its bounded context (CompanyContext)' — and pins the identifier as numeric. It distinguishes the tool from list/search siblings by its bounded-context scope, though it never names an alternative tool explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Required: numeric `id`, never a name or slug' gives a concrete precondition that shapes how the agent must call it, and the password_access/`opts.skip` clause tells the agent how to avoid a 401. There is no explicit when-to-use-versus-siblings guidance, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_folderGet FolderBRead-only
Resolve a folder from an id or name, optionally company-scoped; folders.get is cheaper for a known id. expand:true returns the full Folder.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds that resolution accepts ids or names and that expand:true returns the full Folder rather than a summary, but says nothing about failure behavior when a name doesn't resolve or how company scoping is determined.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense, front-loaded sentence; the verb and resource come first and the cheaper-alternative note follows. No filler, though the semicolon-chained clauses pack several ideas without clear separation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values needn't be explained, and annotations cover the read-only nature. Still, the 'company-scoped' claim is unexplained and unmapped to any parameter, and the alternative identifier fields go unmentioned, leaving an agent to inspect the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% with a nested identifier object, so the description must carry the load. It only mentions 'id or name' while the schema also accepts slug, domain, and external_id, and it references 'company-scoped' even though no company parameter exists in the schema, leaving ambiguity.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Resolve a folder from an id or name, optionally company-scoped.' It distinguishes itself from a cheaper alternative (folders.get) for known ids, which is genuine differentiation, though folders.get is not among the listed siblings so the contrast is somewhat abstract.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing guidance: use folders.get when the id is known because it is cheaper, and this tool resolves from id or name. That is a clear when-to-use condition, though it stops short of stating exclusions or prerequisites for the company-scoped case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_groupGet GroupARead-only
Resolve a user group from an id, name or slug; groups.get is cheaper for a known id. expand:true returns the full Group, members included.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only and open-world behavior. The description adds useful context beyond annotations: expand:true returns the full Group including members, and it notes a cost tradeoff with groups.get. It does not cover auth requirements or rate limits, but those are less relevant for a read operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single compact sentence with purpose front-loaded and zero waste. Every clause adds value: identifier forms, cost alternative, and expand behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema and annotations covering return shape and safety, the description supplies enough detail for correct invocation. The only gap is not mentioning all identifier variants (domain, external_id) supported by the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the burden. It explains that identifier accepts id, name, or slug and clarifies that expand:true returns full records with members. It omits the domain and external_id identifier forms allowed by the schema, so it is helpful but not exhaustive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Resolve a user group') and enumerates accepted identifier forms (id, name, slug). It also contrasts with 'groups.get' for known ids, which aids differentiation, though it does not name a sibling tool from the provided list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit usage condition: use this for id/name/slug resolution, but 'groups.get is cheaper for a known id.' No explicit exclusions or prerequisites are stated, keeping it just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_get_password_folderGet Password FolderARead-only
Resolve a password folder from an id or name, optionally company-scoped; password_folders.get is cheaper for a known id. Credential-shaped fields are masked by default.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| found | Yes | |
| record | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint; the description adds real context beyond them by disclosing that credential-shaped fields are masked by default, which matters for a password-related read. It doesn't say whether masking is toggleable or what the resolve returns for ambiguous names (multiple matches).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact clauses, front-loaded with the primary action and followed by the routing hint and masking note; nothing is redundant. Slightly dense with semicolons but every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema covers return shape, so that isn't needed. However, for a resolver with a five-key nested identifier at 0% schema coverage, the description should clarify the remaining keys and the ambiguous-match behavior; those gaps remain alongside an unexplained 'company-scoped' capability.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the identifier is a nested anyOf accepting id, name, slug, domain, and external_id, but the description only mentions id and name, leaving three accepted keys undocumented. The 'company-scoped' wording hints at a scope option that has no corresponding schema field, which muddies rather than clarifies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Resolve') and resource ('password folder'), plus the accepted key types (id or name). It names a cheaper alternative path (password_folders.get), which differentiates it from a plain id lookup, though it's not a listed sibling so the contrast is only partially useful.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to prefer password_folders.get when the id is already known, giving a clear cost/selection rule. It stops short of saying when this resolve path is the right choice (e.g., name-based lookups where id is unknown) and the 'optionally company-scoped' claim has no matching named parameter.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_listList RecordsARead-only
List ONE bounded page of companies, articles, assets (all companies) or asset_layouts. It takes no operation argument: it cannot become a write and never pages the tenant. Each collection accepts only its own filters; another is refused naming them. asset_layouts takes no page_size (the vendor ignores it): has_more is conservative (true for any non-empty page) — stop when a page comes back empty. Article bodies: compact rows carry none; expand:true returns stored HTML.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Page number to fetch (1-based, default 1). This tool returns that one page and never follows it. | |
| expand | No | Full records, not compact summaries. | |
| filters | No | Resource-specific, closed per resource: companies: name, phone_number, website, city, id_number, state, slug, search, id_in_integration, updated_at; articles: name, company_id, draft, enable_sharing, slug, search, updated_at; assets: company_id, id, name, primary_serial, asset_layout_id, archived, slug, search, updated_at; asset_layouts: name, slug, active, updated_at. A filter outside its resource's set is refused naming the accepted ones. | |
| resource | Yes | Which collection to list. | |
| page_size | No | Rows per page (1-100, default 25); refused for asset_layouts, never clamped. |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| rows | Yes | |
| count | Yes | |
| has_more | Yes | |
| page_size | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond readOnlyHint and openWorldHint, the description discloses important behavior: no operation argument, never pages the tenant, per-resource filter enforcement, asset_layouts page_size handling, conservative has_more semantics, and article body behavior under expand:true. This is exactly the kind of context an agent needs to invoke the tool correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but front-loaded and every sentence carries operational information. Parentheticals are compact, and no sentence is redundant against the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given five parameters, nested filters, an output schema, and rich annotations, the description covers the key behavioral gaps: bounded paging, stop condition, resource-specific filters, asset_layouts exception, and article body expansion. Nothing material is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3. The description adds meaning beyond the schema by explaining refusal behavior for filters, asset_layouts page_size behavior, has_more conservatism, and expand effects on article bodies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb and resource set: list one bounded page of companies, articles, assets, or asset_layouts. It clarifies that this is not a write and does not page the tenant, but it does not explicitly distinguish itself from nearby read/search/fetch siblings such as hudu_search or hudu_fetch_many.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives useful usage constraints: one bounded page, per-resource filter sets with refusals, no page_size for asset_layouts, and a stop condition when a page is empty. It lacks explicit routing guidance naming when to use hudu_search or hudu_fetch_many instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_list_operationsDiscover Operations (Catalog)ARead-only
List SDK operations with the tool exposing each, its effect, dry-run/confirm need and required arguments. This server is a curated SUBSET: hudu_describe_operation gives one operation's contract, hudu_read/hudu_write/hudu_delete run it. Bounded 1-100 rows (default 40), offset pages; the catalog is large, so filter or page.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | 1-100, default 40. | |
| effect | No | ||
| offset | No | Row offset, for paging. | |
| resource | No | Resource, e.g. "assets". | |
| unexposed_only | No | Only rows with NO tool of their own. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| limit | Yes | |
| offset | Yes | |
| matched | Yes | |
| has_more | Yes | |
| total_operations | Yes | |
| reachable_operations | Yes | |
| unexposed_operations | Yes | |
| unreachable_operations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only and closed-world behavior, and the description adds non-obvious context: this is a curated subset, rows are bounded 1-100 with a default of 40, and paging is required for a large catalog. It does not cover error behavior or authentication, but with annotations present this is solid added context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and output fields, then scope/alternatives, then paging constraints. Every sentence contributes useful selection or invocation guidance with no repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema and read-only annotations, the description supplies everything an agent needs: what the catalog returns, how it differs from siblings, and how to avoid an unbounded large result. No material gap remains for this discovery tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80%, so the schema already documents limit, offset, resource, and unexposed_only well. The description reinforces paging and filtering, but does not add significant meaning for effect values or explain unexposed_only beyond what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource (list SDK operations) and names the fields returned for each row: exposing tool, effect, dry-run/confirm need, and required arguments. It also clearly distinguishes this catalog tool from hudu_describe_operation and the hudu_read/hudu_write/hudu_delete execution siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes the agent: use this to browse operations, hudu_describe_operation for one contract, and hudu_read/hudu_write/hudu_delete to run it. It also advises filtering/paging because the catalog is large, which tells the agent how to use the tool correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_readDispatch read operationARead-only
Execute READ operations only, by registry key (hudu_list_operations, hudu_describe_operation). input is closed: unknown fields are CONFIG_ERROR naming valid ones. Results mask credentials, custom fields. Article bodies: single-article reads are Markdown, list rows HTML; lossy reads set markdown_lossy. Text-channel tolerance: an integer, number or boolean field also accepts the exact string literal of its value ("61" for an id, "true" for a flag); any other string is refused.
| Name | Required | Description | Default |
|---|---|---|---|
| input | No | The operation's arguments as one closed object. Never put dry_run or confirm inside it. | |
| operation | Yes | Registry key from hudu_list_operations, not a tool name. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes | |
| operation | Yes | |
| simulated | Yes | |
| markdown_lossy | No | |
| markdown_lossy_codes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile, but the description adds substantial non-obvious behavior: the closed input object with CONFIG_ERROR naming valid fields, credential and custom-field masking in results, Markdown vs HTML article bodies with a markdown_lossy flag, and the string-literal tolerance rule for numeric/boolean fields. These are exactly the traits an agent cannot infer from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and the registry-key mechanism, and every subsequent sentence contributes distinct information (error shape, masking, lossiness, coercion). The compression is dense to the point of terseness in the "Text-channel tolerance" sentence, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape need not be described, and the description covers error behavior, masking, and content-format caveats well. It is light on pagination or result-size behavior for the list/search-style operations (fetchMany, searchAcrossResources), which matters for a read dispatcher.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum of registry keys is fully enumerated, so the schema already carries most of the parameter meaning. The description's closed-object rule and string-literal coercion note add real value, but the constraint about dry_run/confirm duplicates the schema text rather than extending it. Baseline 3 fits when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and scope ("Execute READ operations only") plus the addressing mechanism ("by registry key"), and names the discovery siblings in parentheses. An agent can immediately tell this is the generic dispatcher rather than a targeted helper like hudu_search or hudu_get_company_context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Points explicitly at hudu_list_operations and hudu_describe_operation as the way to obtain a valid registry key, which is the key prerequisite. It does not explain when to prefer this dispatcher over convenience siblings such as hudu_search or hudu_resolve_any, so routing between alternative paths is left partly to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_resolve_anyResolve AnyARead-only
Resolve an identifier across resources when the type is unknown; else use hudu_get_. Unreadable resources are listed in unauthorized; fails only if all are refused. Narrow resources to skip asset_passwords/password_folders.
| Name | Required | Description | Default |
|---|---|---|---|
| opts | No | ||
| identifier | Yes | Id, name/slug, or object |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | |
| total | Yes | |
| scanned | Yes | |
| truncated | Yes | |
| unauthorized | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only and open-world, but the description adds genuinely new behavior: partial failure semantics ('Unreadable resources are listed in `unauthorized`; fails only if all are refused'). That is exactly the kind of non-obvious behavior an agent needs. It stops short of describing cost/fan-out implications of resolving across eight resource types.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short clauses, each doing distinct work: purpose, routing alternative, failure semantics, plus a sensitivity hint. Front-loaded with the primary rule and free of filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists and the description wisely doesn't restate return shape, only the `unauthorized` failure contract. For a multi-resource fan-out tool this covers routing, partial-failure, and sensitive-resource skipping. It could note that resolution may make several upstream calls or how ambiguous matches across types are handled.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%; the `resources` default and HUDU_SEARCH_RESOURCES refusal already live in the schema, and the description only adds the recommendation to narrow it for sensitivity. The `identifier` shapes (id / name / slug / object with domain, external_id) and the `limit` constraint are left entirely to the schema, so the description does not compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource: 'Resolve an identifier across resources when the type is unknown', which is far more precise than the title 'Resolve Any'. It also draws the boundary against the sibling getters, though it names the alternative as a pattern ('hudu_get_<singular>') rather than an actual sibling name, so the agent still has to map the pattern to the real tool names.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states the selection condition (type unknown) and the alternative when the type is known ('else use hudu_get_<singular>'). It also gives a second, non-obvious usage instruction: narrow `resources` to skip asset_passwords/password_folders, which tells the agent how to avoid sensitive fan-out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hudu_searchSearch Hudu (search | help | resources)ARead-only
Search Hudu; help mode="search": ranked fuzzy search over article TITLE and BODY, asset names, fields, identifiers. defaults to tier "index": body-complete. Empty: read meta.degraded; unhonoured field is CONFIG_ERROR
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | search|help|resources | search |
| tier | No | Defaults to "index": builds or awaits the body index. "vendor" is body-blind. | |
| limit | No | 1-25, default 8; larger is CONFIG_ERROR | |
| query | No | REQUIRED for mode="search". Partial words fine; article BODY is searched. Multi-word: topic="query" (mode="help"). | |
| topic | No | help only; "all" adds syntax | |
| refresh | No | rebuilds local body index (stdio only; no Hudu change). | |
| min_score | No | 0-100; floor may empty hits; meta.scanned/meta.returned; company_id/updated_since; uncounted. | |
| resources | No | Default: all searchable; enum lists it | |
| company_id | No | Numeric company id; omit = all companies + global articles. | |
| exact_only | No | No fuzzy match. | |
| snippetChars | No | 0 = no snippet. | |
| updated_since | No | ISO ts; newer only |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly and open-world access, so the bar is lower, yet the description still adds real behavior: the default tier builds/awaits a body index ("body-complete"), empty results should be diagnosed via meta.degraded, and an unhonoured parameter surfaces as CONFIG_ERROR. These are non-obvious failure and side-effect semantics an agent could not infer from annotations alone. It loses a point only because the phrasing is so compressed that the intent is hard to parse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is short with no filler, but the structure is telegraphic: sentence fragments like "Empty: read meta.degraded; unhonoured field is CONFIG_ERROR" and a dangling "help" on the first line read as notes rather than prose. The compression costs comprehension, and the help/resources modes get a word or nothing at all.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Return values need not be explained because an output schema exists, and the search path is adequately covered for a 12-parameter tool. However, a tool whose title advertises three modes documents only one; an agent cannot tell what help mode returns for a given topic or what resources mode is for.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents tier defaults, limit bounds, and query requirements; the baseline of 3 applies. The description largely restates schema facts ("defaults to tier 'index': body-complete") rather than adding new meaning, and it says nothing about the interaction of min_score, resources, and company_id.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and scope for mode="search": "ranked fuzzy search over article TITLE and BODY, asset names, fields, identifiers". That is far more than a restatement of the name. It falls short of 5 because the two other modes in the title (help, resources) are never actually described, and no sibling tool is named for contrast.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: it says what mode="search" does and hints that help mode covers topics, and it gives a diagnostic path for empty results ("read meta.degraded"). There is no explicit statement of when to pick this over hudu_resolve_any, hudu_read, or hudu_list, and no guidance on choosing between search/help/resources beyond the enum names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.11.0- Changed
hudu_read1 field changed- changed
Input schema / properties / operation / enumPrevious value: -[ - "activity_logs.findByResource", - "activity_logs.resolve", - "api_info.get", - "api_info.resolve", - "articles.findBySlug", - "articles.get", - "articles.getContext", - "articles.getSection", - "articles.resolve", - "articles.search", - "asset_layouts.get", - "asset_layouts.resolve", - "asset_passwords.findBySlug", - "asset_passwords.get", - "asset_passwords.getShare", - "asset_passwords.listShares", - "asset_passwords.resolve", - "asset_passwords.search", - "assets.findBySerial", - "assets.get", - "assets.getContext", - "assets.resolve", - "assets.search", - "cards.jump", - "cards.lookup", - "cards.resolve", - "companies.findByDomain", - "companies.findBySlug", - "companies.get", - "companies.getContext", - "companies.jump", - "companies.resolve", - "companies.search", - "expirations.findByResource", - "expirations.resolve", - "flag_types.get", - "flag_types.resolve", - "flags.findByFlagable", - "flags.get", - "flags.resolve", - "folders.get", - "folders.resolve", - "groups.get", - "groups.resolve", - "groups.search", - "ip_addresses.findByAddress", - "ip_addresses.get", - "ip_addresses.resolve", - "label_types.get", - "label_types.resolve", - "labels.findByLabelable", - "labels.get", - "labels.resolve", - "lists.findByName", - "lists.get", - "lists.resolve", - "magic_dash.findByCompany", - "magic_dash.resolve", - "matchers.findBySyncId", - "matchers.resolve", - "networks.findByAddress", - "networks.get", - "networks.resolve", - "operations.fetchMany", - "operations.resolveAny", - "operations.searchAcrossResources", - "operations.searchKnowledge", - "password_folders.get", - "password_folders.resolve", - "password_folders.search", - "procedure_tasks.get", - "procedure_tasks.resolve", - "procedures.get", - "procedures.getWithTasks", - "procedures.resolve", - "rack_storage_items.get", - "rack_storage_items.resolve", - "rack_storages.get", - "rack_storages.resolve", - "relations.findByEndpoints", - "relations.resolve", - "users.findByEmail", - "users.get", - "users.resolve", - "users.search", - "vlan_zones.get", - "vlan_zones.resolve", - "vlans.findByVlanId", - "vlans.get", - "vlans.resolve", - "websites.findBySlug", - "websites.get", - "websites.resolve", - "websites.search" -]New value: +[ + "activity_logs.findByResource", + "activity_logs.resolve", + "api_info.get", + "api_info.resolve", + "articles.findBySlug", + "articles.get", + "articles.getContext", + "articles.getSection", + "articles.resolve", + "articles.search", + "asset_layouts.get", + "asset_layouts.resolve", + "asset_passwords.findBySlug", + "asset_passwords.get", + "asset_passwords.getShare", + "asset_passwords.listShares", + "asset_passwords.resolve", + "asset_passwords.search", + "assets.findBySerial", + "assets.get", + "assets.getContext", + "assets.resolve", + "assets.search", + "cards.jump", + "cards.lookup", + "cards.resolve", + "companies.findByDomain", + "companies.findBySlug", + "companies.get", + "companies.getContext", + "companies.jump", + "companies.resolve", + "companies.search", + "expirations.findByResource", + "expirations.resolve", + "flag_types.get", + "flag_types.resolve", + "flags.findByFlagable", + "flags.get", + "flags.resolve", + "folders.get", + "folders.resolve", + "groups.get", + "groups.resolve", + "groups.search", + "ip_addresses.findByAddress", + "ip_addresses.get", + "ip_addresses.resolve", + "label_types.get", + "label_types.resolve", + "labels.findByLabelable", + "labels.get", + "labels.resolve", + "lists.findByName", + "lists.get", + "lists.resolve", + "magic_dash.findByCompany", + "magic_dash.resolve", + "matchers.findBySyncId", + "matchers.resolve", + "networks.findByAddress", + "networks.get", + "networks.resolve", + "operations.fetchMany", + "operations.probeBodySearch", + "operations.resolveAny", + "operations.searchAcrossResources", + "operations.searchKnowledge", + "password_folders.get", + "password_folders.resolve", + "password_folders.search", + "procedure_tasks.get", + "procedure_tasks.resolve", + "procedures.get", + "procedures.getWithTasks", + "procedures.resolve", + "rack_storage_items.get", + "rack_storage_items.resolve", + "rack_storages.get", + "rack_storages.resolve", + "relations.findByEndpoints", + "relations.resolve", + "users.findByEmail", + "users.get", + "users.resolve", + "users.search", + "vlan_zones.get", + "vlan_zones.resolve", + "vlans.findByVlanId", + "vlans.get", + "vlans.resolve", + "websites.findBySlug", + "websites.get", + "websites.resolve", + "websites.search" +]
- Changed
hudu_search5 fields changed- changed
Input schema / properties / company_id / descriptionPrevious value: -"One company"New value: +"Numeric company id; omit = all companies + global articles." - changed
Input schema / properties / min_score / descriptionPrevious value: -"0-100; 0 hits may be floor/filter; meta.scanned/meta.returned; company_id/updated_since; drops uncounted."New value: +"0-100; floor may empty hits; meta.scanned/meta.returned; company_id/updated_since; uncounted." - changed
Input schema / properties / mode / descriptionPrevious value: -"search|help|resources; help documents fields"New value: +"search|help|resources" - changed
Input schema / properties / query / descriptionPrevious value: -"REQUIRED for mode=\"search\". Partial words fine; article BODY is searched. Multi-word rules: topic=\"query\" (mode=\"help\")."New value: +"REQUIRED for mode=\"search\". Partial words fine; article BODY is searched. Multi-word: topic=\"query\" (mode=\"help\")." - changed
Input schema / properties / updated_since / descriptionPrevious value: -"ISO timestamp; newer only"New value: +"ISO ts; newer only"
21 tool updates
v0.6.0- Changed
hudu_describe_operation2 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_fetch_many4 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / items / items / properties / id / descriptionAdded value: +"Positive integer id (>= 1)." - removed
Input schema / properties / items / items / properties / id / maximumRemoved value: -9007199254740991 - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_find_asset_passwords_by_slug3 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_find_websites_by_slug3 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_get_api_info5 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "domain": { - "description": "Domain, for resources that have one.", - "type": "string" - }, - "external_id": { - "description": "Vendor-side external id.", - "type": "string" - }, - "id": { - "description": "Numeric id - the direct fetch.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "description": "Exact name.", - "type": "string" - }, - "slug": { - "description": "Exact slug.", - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "domain": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - removed
Input schema / properties / identifier / descriptionRemoved value: -"Optional identifier; omit it for the tenant-wide api info." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_get_article_context8 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / id / descriptionPrevious value: -"Numeric article id (hudu_get_article_context takes the id; use hudu_search or hudu_read / hudu_write / hudu_delete with articles.resolve to find it)."New value: +"Numeric article id; articles.resolve finds it." - removed
Input schema / properties / id / maximumRemoved value: -9007199254740991 - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Output schema / properties / markdown_lossyAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / markdown_lossy_codesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / skippedAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
hudu_get_asset_context9 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "companyId": { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "id": { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "type": "string" - }, - "primary_serial": { - "type": "string" - }, - "slug": { - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "companyId": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "primary_serial": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - removed
Input schema / properties / identifier / descriptionRemoved value: -"Id, name, slug, primary serial or { id, companyId }." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - changed
Input schema / properties / opts / properties / limit / descriptionPrevious value: -"Maximum rows returned (1-100, default 25). A larger value throws CONFIG_ERROR - the SDK never silently clamps."New value: +"1-100 rows, default 25; larger is a CONFIG_ERROR." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Output schema / properties / markdown_lossyAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / markdown_lossy_codesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / skippedAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
hudu_get_asset_layout5 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "domain": { - "description": "Domain, for resources that have one.", - "type": "string" - }, - "external_id": { - "description": "Vendor-side external id.", - "type": "string" - }, - "id": { - "description": "Numeric id - the direct fetch.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "description": "Exact name.", - "type": "string" - }, - "slug": { - "description": "Exact slug.", - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "domain": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - removed
Input schema / properties / identifier / descriptionRemoved value: -"Id, exact name/slug, or an identifier object." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_get_company_context10 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - removed
Input schema / properties / id / maximumRemoved value: -9007199254740991 - added
Input schema / properties / opts / descriptionAdded value: +"Read options: limit, expand, skip." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - changed
Input schema / properties / opts / properties / limit / descriptionPrevious value: -"Maximum rows returned (1-100, default 25). A larger value throws CONFIG_ERROR - the SDK never silently clamps."New value: +"1-100 rows, default 25; larger is a CONFIG_ERROR." - added
Input schema / properties / opts / properties / skipAdded value: +{ + "description": "Sub-lists to omit; no request is made for them.", + "items": { + "enum": [ + "assets", + "articles", + "websites", + "asset_passwords" + ], + "type": "string" + }, + "type": "array" +} - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Output schema / properties / markdown_lossyAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / markdown_lossy_codesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / skippedAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
hudu_get_folder5 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "domain": { - "description": "Domain, for resources that have one.", - "type": "string" - }, - "external_id": { - "description": "Vendor-side external id.", - "type": "string" - }, - "id": { - "description": "Numeric id - the direct fetch.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "description": "Exact name.", - "type": "string" - }, - "slug": { - "description": "Exact slug.", - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "domain": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - removed
Input schema / properties / identifier / descriptionRemoved value: -"Id, exact name/slug, or an identifier object." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_get_group5 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "domain": { - "description": "Domain, for resources that have one.", - "type": "string" - }, - "external_id": { - "description": "Vendor-side external id.", - "type": "string" - }, - "id": { - "description": "Numeric id - the direct fetch.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "description": "Exact name.", - "type": "string" - }, - "slug": { - "description": "Exact slug.", - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "domain": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - removed
Input schema / properties / identifier / descriptionRemoved value: -"Id, exact name/slug, or an identifier object." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_get_password_folder5 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "domain": { - "description": "Domain, for resources that have one.", - "type": "string" - }, - "external_id": { - "description": "Vendor-side external id.", - "type": "string" - }, - "id": { - "description": "Numeric id - the direct fetch.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "description": "Exact name.", - "type": "string" - }, - "slug": { - "description": "Exact slug.", - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "domain": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - removed
Input schema / properties / identifier / descriptionRemoved value: -"Id, exact name/slug, or an identifier object." - changed
Input schema / properties / opts / properties / expand / descriptionPrevious value: -"Return the full typed records instead of the compact summaries (default false)."New value: +"Full records, not compact summaries." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Added
hudu_list - Removed
hudu_list_articles - Removed
hudu_list_asset_layouts - Removed
hudu_list_assets - Removed
hudu_list_companies - Changed
hudu_list_operations7 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - removed
Input schema / properties / effect / descriptionRemoved value: -"Only rows with this effect." - changed
Input schema / properties / limit / descriptionPrevious value: -"Rows per page (1-100, default 40); the whole catalog is ~10k tokens, so page it."New value: +"1-100, default 40." - removed
Input schema / properties / offset / maximumRemoved value: -9007199254740991 - changed
Input schema / properties / resource / descriptionPrevious value: -"Only rows of this registry resource (e.g. \"assets\")."New value: +"Resource, e.g. \"assets\"." - changed
Input schema / properties / unexposed_only / descriptionPrevious value: -"Only rows with NO tool of their own — the capabilities reachable only through hudu_read / hudu_write / hudu_delete."New value: +"Only rows with NO tool of their own." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
- Changed
hudu_read7 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / input / descriptionPrevious value: -"The operation arguments as one object; validated against the registry record before any request. The field contract is closed: an unknown top-level field is a typed CONFIG_ERROR naming the accepted fields, which hudu_describe_operation serves as input_contract."New value: +"The operation's arguments as one closed object. Never put dry_run or confirm inside it." - changed
Input schema / properties / operation / descriptionPrevious value: -"Canonical operation key (see hudu_list_operations). Exact registry key, never a tool name."New value: +"Registry key from hudu_list_operations, not a tool name." - changed
Input schema / properties / operation / enumPrevious value: -[ - "activity_logs.findByResource", - "activity_logs.resolve", - "api_info.get", - "api_info.resolve", - "articles.findBySlug", - "articles.get", - "articles.getContext", - "articles.resolve", - "articles.search", - "asset_layouts.get", - "asset_layouts.resolve", - "asset_passwords.findBySlug", - "asset_passwords.get", - "asset_passwords.resolve", - "asset_passwords.search", - "assets.findBySerial", - "assets.get", - "assets.getContext", - "assets.resolve", - "assets.search", - "cards.jump", - "cards.lookup", - "cards.resolve", - "companies.findByDomain", - "companies.findBySlug", - "companies.get", - "companies.getContext", - "companies.jump", - "companies.resolve", - "companies.search", - "expirations.findByResource", - "expirations.resolve", - "flag_types.get", - "flag_types.resolve", - "flags.findByFlagable", - "flags.get", - "flags.resolve", - "folders.get", - "folders.resolve", - "groups.get", - "groups.resolve", - "groups.search", - "ip_addresses.findByAddress", - "ip_addresses.get", - "ip_addresses.resolve", - "label_types.get", - "label_types.resolve", - "labels.findByLabelable", - "labels.get", - "labels.resolve", - "lists.findByName", - "lists.get", - "lists.resolve", - "magic_dash.findByCompany", - "magic_dash.resolve", - "matchers.findBySyncId", - "matchers.resolve", - "networks.findByAddress", - "networks.get", - "networks.resolve", - "operations.fetchMany", - "operations.resolveAny", - "operations.searchAcrossResources", - "operations.searchKnowledge", - "password_folders.get", - "password_folders.resolve", - "password_folders.search", - "procedure_tasks.get", - "procedure_tasks.resolve", - "procedures.get", - "procedures.getWithTasks", - "procedures.resolve", - "rack_storage_items.get", - "rack_storage_items.resolve", - "rack_storages.get", - "rack_storages.resolve", - "relations.findByEndpoints", - "relations.resolve", - "users.findByEmail", - "users.get", - "users.resolve", - "users.search", - "vlan_zones.get", - "vlan_zones.resolve", - "vlans.findByVlanId", - "vlans.get", - "vlans.resolve", - "websites.findBySlug", - "websites.get", - "websites.resolve", - "websites.search" -]New value: +[ + "activity_logs.findByResource", + "activity_logs.resolve", + "api_info.get", + "api_info.resolve", + "articles.findBySlug", + "articles.get", + "articles.getContext", + "articles.getSection", + "articles.resolve", + "articles.search", + "asset_layouts.get", + "asset_layouts.resolve", + "asset_passwords.findBySlug", + "asset_passwords.get", + "asset_passwords.getShare", + "asset_passwords.listShares", + "asset_passwords.resolve", + "asset_passwords.search", + "assets.findBySerial", + "assets.get", + "assets.getContext", + "assets.resolve", + "assets.search", + "cards.jump", + "cards.lookup", + "cards.resolve", + "companies.findByDomain", + "companies.findBySlug", + "companies.get", + "companies.getContext", + "companies.jump", + "companies.resolve", + "companies.search", + "expirations.findByResource", + "expirations.resolve", + "flag_types.get", + "flag_types.resolve", + "flags.findByFlagable", + "flags.get", + "flags.resolve", + "folders.get", + "folders.resolve", + "groups.get", + "groups.resolve", + "groups.search", + "ip_addresses.findByAddress", + "ip_addresses.get", + "ip_addresses.resolve", + "label_types.get", + "label_types.resolve", + "labels.findByLabelable", + "labels.get", + "labels.resolve", + "lists.findByName", + "lists.get", + "lists.resolve", + "magic_dash.findByCompany", + "magic_dash.resolve", + "matchers.findBySyncId", + "matchers.resolve", + "networks.findByAddress", + "networks.get", + "networks.resolve", + "operations.fetchMany", + "operations.resolveAny", + "operations.searchAcrossResources", + "operations.searchKnowledge", + "password_folders.get", + "password_folders.resolve", + "password_folders.search", + "procedure_tasks.get", + "procedure_tasks.resolve", + "procedures.get", + "procedures.getWithTasks", + "procedures.resolve", + "rack_storage_items.get", + "rack_storage_items.resolve", + "rack_storages.get", + "rack_storages.resolve", + "relations.findByEndpoints", + "relations.resolve", + "users.findByEmail", + "users.get", + "users.resolve", + "users.search", + "vlan_zones.get", + "vlan_zones.resolve", + "vlans.findByVlanId", + "vlans.get", + "vlans.resolve", + "websites.findBySlug", + "websites.get", + "websites.resolve", + "websites.search" +] - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Output schema / properties / markdown_lossyAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / markdown_lossy_codesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
hudu_resolve_any7 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / identifier / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - { - "minLength": 1, - "type": "string" - }, - { - "properties": { - "domain": { - "description": "Domain, for resources that have one.", - "type": "string" - }, - "external_id": { - "description": "Vendor-side external id.", - "type": "string" - }, - "id": { - "description": "Numeric id - the direct fetch.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "name": { - "description": "Exact name.", - "type": "string" - }, - "slug": { - "description": "Exact slug.", - "type": "string" - } - }, - "type": "object" - } -]New value: +[ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "minLength": 1, + "type": "string" + }, + { + "properties": { + "domain": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "id": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "type": "object" + } +] - changed
Input schema / properties / identifier / descriptionPrevious value: -"Id, exact name/slug, or an identifier object; resolved against every requested resource."New value: +"Id, name/slug, or object" - changed
Input schema / properties / opts / properties / limit / descriptionPrevious value: -"Maximum rows returned (1-100, default 25). A larger value throws CONFIG_ERROR - the SDK never silently clamps."New value: +"1-100 rows, default 25; larger is a CONFIG_ERROR." - changed
Input schema / properties / opts / properties / resources / descriptionPrevious value: -"Resources to try, in fan-out order; default this deployment's searchable set: articles, asset_passwords, assets, companies, groups, password_folders, users, websites. A resource outside HUDU_SEARCH_RESOURCES is refused before any request."New value: +"Default: articles, asset_passwords, assets, companies, groups, password_folders, users, websites; a resource outside HUDU_SEARCH_RESOURCES is refused before any request." - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Output schema / properties / unauthorizedAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
hudu_search16 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / company_id / descriptionPrevious value: -"mode=\"search\" only. Scope the scan to one company."New value: +"One company" - removed
Input schema / properties / company_id / maximumRemoved value: -9007199254740991 - changed
Input schema / properties / exact_only / descriptionPrevious value: -"mode=\"search\" only. true disables fuzzy matching, still ranked and snippeted. Default false."New value: +"No fuzzy match." - changed
Input schema / properties / limit / descriptionPrevious value: -"mode=\"search\" only. Ranked hits: 1-25, default 8. A larger value is a CONFIG_ERROR, never clamped."New value: +"1-25, default 8; larger is CONFIG_ERROR" - changed
Input schema / properties / min_score / descriptionPrevious value: -"mode=\"search\" only. 0-100 score floor: hits below the floor are left out of the result, and NO count of them is reported - an empty result can be the floor, not absence. meta.scanned (candidates scored before the floor) and meta.returned separate absence from exclusion: scanned 0 = nothing scored at all; scanned > 0 with returned 0 = candidates scored but none returned - below the floor, or excluded by the company_id, updated_since or scope filters (lower or drop min_score, or relax those filters, to see what was left out)."New value: +"0-100; 0 hits may be floor/filter; meta.scanned/meta.returned; company_id/updated_since; drops uncounted." - changed
Input schema / properties / mode / descriptionPrevious value: -"Which mode to run. Default \"search\" (the query runs; nothing else is needed). \"help\" returns how to use this tool. \"resources\" returns what is searchable. A field the selected mode cannot honour is a CONFIG_ERROR naming the fields it does honour - never silently ignored."New value: +"search|help|resources; help documents fields" - changed
Input schema / properties / query / descriptionPrevious value: -"mode=\"search\" only, and REQUIRED there: omitting it is a CONFIG_ERROR naming mode:\"help\" (it never falls back to help). Partial words are tolerated; article BODY content is searched. Hudu's own search differs by resource. Companies, websites, passwords and assets match the words in order with gaps allowed, so reordering words can miss a record (\"acme corp\" finds \"Acme Corp Pty\", \"corp acme\" may not). Users, groups and password folders match the whole query as one exact phrase, so use one word. Some instances answer from a fuzzy index that returns records matching only some of the words. Indexed articles and assets score each word independently (match.coverage). On a multi-word query, when the full phrase finds nothing, the tool retries the individual words and ranks partial matches lower; match.coverage shows how good a match is. For an exact lookup, search one distinctive word."New value: +"REQUIRED for mode=\"search\". Partial words fine; article BODY is searched. Multi-word rules: topic=\"query\" (mode=\"help\")." - changed
Input schema / properties / refresh / descriptionPrevious value: -"mode=\"search\" only. true forces a body-index rebuild before answering."New value: +"rebuilds local body index (stdio only; no Hudu change)." - changed
Input schema / properties / resources / descriptionPrevious value: -"mode=\"search\" only. Which resources to search; default [\"articles\",\"assets\"]. The resources this deployment's credential may read (HUDU_SEARCH_RESOURCES), from the SDK's searchable set: articles, asset_passwords, assets, companies, groups, password_folders, users, websites."New value: +"Default: all searchable; enum lists it" - changed
Input schema / properties / snippetChars / descriptionPrevious value: -"mode=\"search\" only. Snippet characters per hit: 0-400, default 200, 0 = no snippet."New value: +"0 = no snippet." - changed
Input schema / properties / tier / descriptionPrevious value: -"mode=\"search\" only. THIS TOOL DEFAULTS TO \"index\" (build or await the body index, so the answer is complete rather than body-blind). \"auto\" is the SDK default for programmatic callers; \"vendor\" answers from Hudu search alone."New value: +"Defaults to \"index\": builds or awaits the body index. \"vendor\" is body-blind." - changed
Input schema / properties / topic / descriptionPrevious value: -"mode=\"help\" only. Which help section to return (default \"core\"; \"all\" adds query syntax and scoring). Rejected in the other modes."New value: +"help only; \"all\" adds syntax" - changed
Input schema / properties / updated_since / descriptionPrevious value: -"mode=\"search\" only. ISO 8601; only records updated at or after it."New value: +"ISO timestamp; newer only" - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema"
20 tool updates
v0.2.3- First observed
hudu_describe_operation - First observed
hudu_fetch_many - First observed
hudu_find_asset_passwords_by_slug - First observed
hudu_find_websites_by_slug - First observed
hudu_get_api_info - First observed
hudu_get_article_context - First observed
hudu_get_asset_context - First observed
hudu_get_asset_layout - First observed
hudu_get_company_context - First observed
hudu_get_folder - First observed
hudu_get_group - First observed
hudu_get_password_folder - First observed
hudu_list_articles - First observed
hudu_list_asset_layouts - First observed
hudu_list_assets - First observed
hudu_list_companies - First observed
hudu_list_operations - First observed
hudu_read - First observed
hudu_resolve_any - First observed
hudu_search
TDQS
Scored across 17 tools
The generic hudu_read, hudu_list, hudu_search, hudu_resolve_any, and hudu_fetch_many all retrieve overlapping data, and the specialized get_* tools are essentially shortcuts over hudu_read. The descriptions explain when to prefer each shortcut, but the boundaries still require careful reading.
All tools use a consistent hudu_ prefix and snake_case, with predictable verb_noun shapes such as hudu_list_operations, hudu_get_company_context, and hudu_find_websites_by_slug. The verbs vary across list/get/find/resolve/fetch, but that variation is readable and mostly semantic.
17 tools is slightly above the typical 3-15 range, but Hudu is a broad IT-documentation domain and the set is still curated rather than exhaustive. Several specialized getters could be folded into the generic read path, but the count is reasonable for the scope.
The tool descriptions reference hudu_write and hudu_delete as operation runners, but no write, update, or delete tools are actually exposed. The surface is read-heavy and covers many resources through hudu_read, but lifecycle operations and several resource-specific actions are missing.
Maintenance
Related MCP Connectors
Read-only MCP access to a documented IT fleet: state, changes, posture. 15 tools.
Team docs served to AI agents over MCP - search, Markdown reads, version pinning, read audit.
Knowledge base MCP for AI agents on iknow.dev. Search, read, and maintain via OAuth.
Docsy gives AI assistants current, version-specific documentation for libraries, frameworks and SDKs. Resolve library IDs and versions, then search and fetch focused official documentation with original source links using five read-only MCP tools. Hosted Streamable HTTP with OAuth 2.1 sign-in.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with HUDU technical documentation and customer management platform through API integration. Supports retrieving company information, searching knowledge base articles, and managing assets and credentials.8MIT
- FlicenseCqualityDmaintenanceEnables Claude to interact with Hudu IT documentation, including searching, reading, creating, and updating articles, assets, passwords, companies, and more.100-

hudu-mcpofficial
AlicenseAqualityDmaintenanceEnables AI assistants to interact with Hudu IT documentation, providing tools for companies, assets, knowledge base articles, credentials, IPAM, racks, and more, with security gates for passwords and destructive operations.7023 npmMIT- AlicenseAqualityBmaintenanceA Model Context Protocol server for querying and reading articles from a how-to documentation portal, with search and retrieval tools scoped to the token's user permissions.37 npmApache 2.0