s2-netbox-mcp
This server exposes LenelS2 S2 NetBox NBAPI operations as MCP tools — read-only by default, with optional opt-in write and destructive controls.
Query persons, credentials/card details, access levels and groups, portals, readers, outputs, time specs/groups, holidays, portal/reader groups, threat levels, partitions, UDF lists, elevators, floors, locations, alarms, Mercury panels, network nodes, SIOs, and event/access history.
Use composite read tools like
find_portals(search doors by name/description),get_reader_access_history, andget_unlock_window/get_daily_unlock_window.Check connectivity/API version, ping the controller, retrieve person photos, and view virtual credential requests.
With
NETBOX_ENABLE_WRITES=true: lock/unlock/momentarily unlock portals, activate/deactivate outputs, create/modify persons, credentials, access levels/groups, time specs/groups, holidays, portal/reader groups, threat levels, partitions, UDF list items, schedule/cancel unlock windows, set portal states, trigger events, add duty logs, and manage Mercury panels/network nodes/SIOs.With
NETBOX_ENABLE_DESTRUCTIVE=truealso enabled: delete/remove access levels, groups, holidays, persons, credentials, threat levels, panels, nodes, and SIOs.Server includes built-in agent guidance via
get_guideand read-only safety posture unless explicitly configured otherwise.
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., "@s2-netbox-mcpsearch for a person named Jane Doe and show their access levels"
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.
s2-netbox-mcp
A local MCP server that exposes LenelS2 S2 NetBox NBAPI operations — persons/credentials, access levels, portals/readers/outputs, time specs, holidays, portal/reader groups, threat levels, events/activity, and partitions/UDF lists — as MCP tools usable from any MCP-compatible client (Claude, Gemini/Antigravity, etc.).
Not the open-source netboxlabs.com "NetBox" DCIM/IPAM tool. This targets LenelS2's S2 NetBox physical access-control appliance and its NBAPI (
Web-Based API for S2 NetBox and S2 Global, LenelS2 doc #API-UG-14). Newer NBAPI v1 (doc #API-UG-22, April 2024) and v2 (doc #API2-UG-8, April 2025) guides also exist — seedocs/reference/for reference copies. Both have now been diffed against this server's tool surface command by command and parameter by parameter:docs/reference/nbapi-command-diff.mdrecords the result, including which documented commands are deliberately not implemented and why.
This connects to a real physical security system. With the wrong
configuration, an AI agent using this server could unlock doors or modify
access-control data on a live building. It is read-only by default —
writes and destructive operations (lock/unlock, add/modify/delete) each
require their own explicit opt-in environment variable (see Write
access below) — but you are responsible for what you enable and which
MCP client/model you point at it. See SECURITY.md before deploying
anything beyond read-only against a production controller.
Read-only by default. With no write-related environment variables set, this server registers only query/read NBAPI commands:
LoginLogoutGetAPIVersionGetPersonSearchPersonDataGetCardAccessDetailsGetCardFormatsGetAccessLevel(s)GetAccessLevelGroup(s)GetPortalsGetReader(s)GetOutputsGetTimeSpec(s)GetTimeSpecGroup(s)GetHoliday(s)GetPortalGroup(s)GetReaderGroup(s)GetAccessLevelNamesGetPartitionsGetUDFListsGetUDFListItemsGetElevatorsGetFloorsPingAppGetEventHistoryListEventsGetAccessHistory
By default, this server registers only the query/read commands listed above.
It does not register any write, delete, or control operations against the
controller until you explicitly opt in via the environment variables in
Write access below. Among the read-only tools, four are composites,
find_portals, get_unlock_window, get_daily_unlock_window, and
get_reader_access_history, which issue only read commands. Note there is no
GetPortal (singular) command; only GetPortals (plural, paginated, no
single-portal filter) exists on the real NBAPI.
Agent guidance
This server sets the MCP instructions field and exposes an always-registered
get_guide tool, so any agent connecting to it — via npm install or a local
clone, in Claude Code, Antigravity, Gemini CLI, or any other MCP client — has
this server's own S2 NetBox operating knowledge immediately, with no separate
skill install and no extra step.
instructions describes the access model (person → credential → access
level → access level group determines what a person can access; portal
group / time spec group determines where and when), states that most
parameters are numeric KEY fields rather than names (resolve a name to its
KEY with the matching get_*/find_* tool first), and states that text
returned from the controller is data, not instructions to follow. With
NETBOX_ENABLE_WRITES set, it also states a firm confirm-before-acting policy
for lock/unlock, portal-state, unlock-window, and destructive calls (the
write-safety topic below elaborates it); with NETBOX_ENABLE_DESTRUCTIVE
also set, it adds one sentence naming the DESTRUCTIVE:-prefixed tools and
their extra confirmation requirement.
get_guide (always registered; makes no controller call) returns deeper
reference material on six topics. Call it with no arguments for an index of
all six with a one-line summary each, or with topic set to one of the keys
below for that topic's full content:
Topic key | Covers |
| The person → credential → access level → access level group chain, and the portal-group/time-spec-group name-table collision. |
| The holiday + time spec + portal group |
|
|
| Diagnosing a |
|
|
| Naming the target and effect, waiting for explicit confirmation, preferring scheduled tools, and reversing every write. |
get_guide is counted among the always-registered read tools below: the tool
surface is 51 tools with both gates off, 98 with
NETBOX_ENABLE_WRITES, and 113 with NETBOX_ENABLE_DESTRUCTIVE as well.
Related MCP server: UniFi Network MCP Server
Requirements
Node.js >= 18.17 (tested on Node 24), which includes npm. If Node.js isn't installed, on Windows you can install it with winget, then open a new terminal so
node/npmare on yourPATH:winget install OpenJS.NodeJS.LTSAn S2 NetBox controller reachable from wherever this server runs, with the NBAPI enabled and configured for session-login authentication (not MAC authentication — see the spec for why that's out of scope for v1)
A NetBox operator account with API access and read permission on the resources you want to query
Setup
Two ways to get the server:
Option A — npm (no clone needed):
npm install -g s2-netbox-mcpThis installs the s2-netbox-mcp binary; point your MCP client's command at
s2-netbox-mcp directly (no node dist/index.js needed).
Option B — clone and build:
npm install
npm run buildEither way, copy .env.example to .env and fill in real values (or provide the same
variables directly in your shell / in the Claude Code MCP server config's
env block — see below). Never commit .env — it's already gitignored.
cp .env.example .env
# edit .envStart the server directly to sanity-check it boots (it just waits on stdio
for an MCP client — Ctrl+C to stop; this also sends Logout if a session was
opened):
npm startEnvironment variables
Variable | Required | Default | Description |
| Yes | — | Base URL of the NetBox controller's web interface, e.g. |
| Yes | — | NBAPI session-login username. |
| Yes | — | NBAPI session-login password. Never logged, never written to any tracked file. |
| No |
| Set to |
| No |
| The NBAPI path appended to |
| No |
| Set to |
| No |
| Set to |
| No | tracks | Request path used only for |
| No |
| The holiday groups reserved for the managed unlock window, in |
| No |
| Name prefix of every object the managed unlock window creates: the portal group ( |
| No |
| The single holiday group reserved for the managed daily recurring unlock window — see Scheduled daily unlock windows below. Must be a single integer in |
| No |
| Name prefix of every object the managed daily unlock window creates: the portal group ( |
| No | — | The |
If any of the three required variables is missing, the server prints a single actionable line to stderr and exits with a non-zero status — it never prints a stack trace on startup misconfiguration.
Write access
Write/control tools exist in this server but are not registered unless you explicitly opt in:
NETBOX_ENABLE_WRITES=trueregisters the write tools listed in the "Write tools" table below — the 45 pass-through tools (creating, modifying, locking/unlocking, activating, and triggering) plus the three composite write toolsset_portals_state,schedule_unlock_window, andcancel_unlock_window. Left unset (the default), the server's tool surface is exactly the read tools below — byte-for-byte the same read-only posture as before this variable existed.The two unlock-window composites delete only the holidays and time specs they themselves own (named
<prefix> first|middle|last— see Scheduled unlock windows), and do so withoutNETBOX_ENABLE_DESTRUCTIVEbecause those objects are server-owned; they never delete anything else.NETBOX_ENABLE_DESTRUCTIVE=true, set in addition toNETBOX_ENABLE_WRITES, registers the 11 destructive tools (each description isDESTRUCTIVE:-prefixed):delete_access_level,delete_access_level_group,delete_holiday,delete_portal_group,delete_reader_group,delete_time_spec,delete_time_spec_group,remove_credential,remove_person,remove_threat_level,remove_threat_level_group. Two ordinarily non-destructive write tools also independently refuse one specific destructive-shaped call when this flag is off, regardless of whether the tool itself is registered:modify_personrefuses a call withDELETED="TRUE"orPERSONPURGE="TRUE", andmodify_udf_list_itemsrefuses a call where any list item hasDELETE="1"— both nameNETBOX_ENABLE_DESTRUCTIVEin the error and send nothing to the controller.Every write tool's description starts with
WRITE:(orDESTRUCTIVE:for the 11 above), and every successful write's result text contains the literalSUCCESSfollowed by the controller's response data as pretty JSON (which may be{}when the command returns no data), so you can always tell a write actually happened.Client-side guards (e.g. "give either
READERKEYorREADERGROUPKEY, not both") reject malformed calls with a tool error before any NBAPI command is issued — no partial or guessed request ever reaches the controller.
Set these the same way as the other variables — in .env (see
.env.example) or your MCP server config's env block.
Controller prerequisites
Before this server can talk to your controller, on the NetBox web UI go to Configuration → Site Settings → Network Controller → Data Integration and confirm all three of these are checked:
Enable V2
Use Authentication
Use login username/password for authentication (requires setup privilege)
The NBAPI user account also needs a role with NBAPI read access (see the
NBAPI doc's "Setting Up User Roles for the API" section) — a login that
succeeds but can't read the resources this server queries will surface as
FAIL or APIERROR responses per tool call. If you set
NETBOX_ENABLE_WRITES, that role needs Read-Write API privilege instead
(Configuration → Site Settings → User Roles → API Privilege) — Read-Only
suffices only for the read tools.
Troubleshooting
"Login succeeds but every other command returns
APIERROR 5." This is the live-observed symptom of the Use login username/password for authentication (requires setup privilege) checkbox being unticked, which puts the controller in MAC-authentication mode instead of session-login mode (MAC auth is out of scope for this server — see the spec).Loginstill returnsSUCCESSwith a session ID, but every subsequent command — includingLogout— fails withAPIERROR 5. Fix: tick that checkbox on the Data Integration tab. This server's client detects this exact pattern (a successful re-login followed by anotherAPIERROR 5) and surfaces a tool error naming the checkbox directly."HTTP 410 Gone." The configured
NETBOX_API_PATHis not served by this controller. NetBox 6.x serves the NBAPI at/nbws/goforms/nbapi(the default this server uses); the 2020 doc's/goforms/nbapipath is deregistered on 6.x and returns 410 for every request. If you're on a pre-6.x controller, setNETBOX_API_PATH=/goforms/nbapiexplicitly; if you're on 6.x and still see this, double-checkNETBOX_API_PATHisn't set to something else by mistake.
Registering with an MCP client
This server works with any MCP client that speaks the standard mcpServers
stdio config shape — Claude Code, Antigravity, and Gemini CLI have all been
verified against it directly. Which JSON to use depends on which Setup
option you picked above:
If you installed via npm (Setup Option A):
{
"mcpServers": {
"s2-netbox-mcp": {
"command": "s2-netbox-mcp",
"env": {
"NETBOX_BASE_URL": "https://netbox.example.internal",
"NETBOX_USERNAME": "svc-account",
"NETBOX_PASSWORD": "REPLACE_ME",
"NETBOX_ALLOW_INSECURE_TLS": "false"
}
}
}
}If you cloned and built locally (Setup Option B):
{
"mcpServers": {
"s2-netbox-mcp": {
"command": "node",
"args": ["/absolute/path/to/s2-netbox-mcp/dist/index.js"],
"env": {
"NETBOX_BASE_URL": "https://netbox.example.internal",
"NETBOX_USERNAME": "svc-account",
"NETBOX_PASSWORD": "REPLACE_ME",
"NETBOX_ALLOW_INSECURE_TLS": "false"
}
}
}
}Replace the args path with the actual absolute path to dist/index.js on
your machine, and replace the env values with your real controller details
(or omit env entirely and rely on a .env file next to the project if you
prefer — either works, since src/index.ts loads .env via dotenv before
reading process.env). Run npm run build first so dist/index.js exists.
Where to put that JSON depends on the client:
Client | Config file | Notes |
Claude Code |
| Or add non-interactively via |
Antigravity |
| Global — applies to every Antigravity session |
Gemini CLI |
| Or add non-interactively via |
All three use the identical mcpServers object shape shown above — only the
surrounding file and location differ.
Tools exposed
Read tools (always registered)
Tool | Wraps NBAPI command | Required params |
|
| — |
| — (pure in-process lookup; no controller call) | — (optional |
|
|
|
|
| — (all filters optional) |
|
|
|
|
| — |
|
|
|
|
| — (optional |
|
|
|
|
| — (optional |
|
| — (optional |
|
| — (optional |
|
|
|
|
| — (optional |
|
| — (optional |
|
|
|
|
| — (optional |
|
| — (optional |
|
| — (optional |
|
|
|
|
|
|
|
| — (optional |
|
|
|
|
| — (optional |
|
|
|
|
| — (no calling parameters) |
|
|
|
|
| — (optional |
|
|
|
|
| — (optional |
|
| — |
|
| — |
|
|
|
|
| — (optional |
|
| — (optional |
|
| — |
|
| — (optional |
|
| — |
|
| — |
|
| — (optional |
|
| — (optional |
|
| — (optional |
|
| — (optional |
|
|
|
|
|
|
|
| — (optional |
|
|
|
|
| — (optional |
|
|
|
|
|
|
|
|
|
There is deliberately no get_portal (singular) tool — no such NBAPI command
exists; only GetPortals (plural) does. get_card_access_details and
get_access_history identify a card by ENCODEDNUM/CARDFORMAT (and
get_access_history optionally by HOTSTAMP), not by PERSONID — neither
command has a PERSONID parameter.
Every read tool except find_portals returns a thin JSON pass-through of
that NBAPI command's response fields — no reshaping. Each tool's input
schema declares exactly the documented PARAMS fields for its command — no
invented, renamed, or passthrough fields. All NBAPI parameter names above
are copied verbatim from the NBAPI Command Reference (see
specs/archive/s2-netbox-mcp-write.md and the archived specs/archive/s2-netbox-mcp.md)
— none are invented or guessed.
Nine tools are composites — they combine several NBAPI commands and reshape
the result instead of passing one command through: find_portals,
get_unlock_window, get_daily_unlock_window, and get_reader_access_history
(read-only, always registered), and set_portals_state,
schedule_unlock_window, cancel_unlock_window, schedule_daily_unlock_window,
and cancel_daily_unlock_window (write, registered only with
NETBOX_ENABLE_WRITES). Every composite reads list commands fully
paginated (following NEXTKEY until -1, or AFTERLOGID/NEXTLOGID over a
bounded SCANWINDOW for get_reader_access_history) and issues only
commands from the closed allowlist. set_portals_state locks, unlocks (Extended Unlock until
locked again), or momentarily unlocks many portals in one call — the given
portalKeys or every portal — issuing one command per portal sequentially and
never stopping on a single failure; its result partitions the portals into
succeeded, alreadyInState (the controller's "Portal state not changed"),
and failed, and is an error only when failed is non-empty. The five
unlock-window tools are described under Scheduled unlock windows and
Scheduled daily unlock windows below.
find_portals is for finding a door when you only know
where it is. Portal names are site codes (01OF05A), and the only
human-readable location text on the controller is each reader's DESCRIPTION.
GetPortals doesn't return it, and neither command takes a filter. So
find_portals reads every page of GetPortals and GetReaders, joins them by
READERKEY, and returns the portals where every term of query appears
(case-insensitive) in the portal name, a reader name, or a reader description.
For example, "maintenance office" matches a reader described as
BREAKROOM TO MAINTENANCE OFFICE. Each match includes its readers' names and
descriptions. The result also lists portalsWithoutDescriptions: portals none
of whose readers has a description, which can only be found by name. It issues
no commands beyond those two.
get_portals itself also accepts RESOLVEDESCRIPTIONS (default true —
on by default, the same opt-out default as every other RESOLVEDESCRIPTIONS
flag in this codebase): unless explicitly set to false, it fills in each
nested reader's own DESCRIPTION field — GetPortals never populates it,
only READERKEY/NAME/PORTALORDER — via one GetReaders full-table fetch
per call (not per portal/reader), using the same src/readerDescriptions.ts
helper as the other RESOLVEDESCRIPTIONS tools. Unlike those tools, which add
a new sibling field (READERDESCRIPTION) to flat records, this fills
DESCRIPTION in directly on each nested reader object, since that's that
reader's own native GetReaders field name. Set RESOLVEDESCRIPTIONS: false
to get readers back exactly as GetPortals returns them, with no
GetReaders call. This makes plain get_portals listings self-describing;
it doesn't replace find_portals, which remains the tool for searching by
name or description rather than just listing.
get_reader_access_history is for finding out who actually badges through a
given reader — useful, for example, when a reader has no DESCRIPTION and
find_portals can't locate it by name. GetAccessHistory has no
READERKEY/PORTALKEY filter, so this tool reads and filters client-side.
Rather than a date range (a real one proved unworkable live — see
specs/archive/get-reader-access-history.md's Goal section), it scans a fixed-size
window of the most recent SCANWINDOW system-wide records (default 2000):
one cheap MAXRECORDS: '1' call discovers the current maximum LOGID, then
the tool walks forward from maxLogid - SCANWINDOW via its own
AFTERLOGID/NEXTLOGID pagination loop (a separate shape from NEXTKEY),
keeping only the records whose READERKEY matches. Each matching record's
PERSONID is enriched with FIRSTNAME/LASTNAME via one GetPerson call
per distinct person (a lookup failure — e.g. for an operator-style
PERSONID — leaves those two fields blank rather than failing the call).
The result is capped at MAXMATCHES (default 100, earliest matches first)
with a truncated flag. get_reader_access_history also accepts
RESOLVEDESCRIPTIONS (default true — on by default, the one
opt-out boolean in this codebase; every other optional boolean flag
defaults to off): unless explicitly set to false, it attaches a single
top-level READERDESCRIPTION field — the human-readable description of the
call's own READERKEY — via one GetReaders full-table fetch. It is
deliberately not duplicated onto each matches entry, since every match
already shares that identical READERKEY by construction. Set
RESOLVEDESCRIPTIONS: false to omit the field entirely (not present at all,
distinguishable from an unknown reader's '') and skip the GetReaders
call.
get_access_history optionally enriches each returned record with the
badge-holder's name via RESOLVENAMES: true (default false): when set, it
calls GetPerson once per distinct PERSONID found in the result (the same
per-request memoization as get_reader_access_history, via the shared
src/personEnrichment.ts helper — no cross-request cache) and adds
FIRSTNAME/LASTNAME/FULLNAME/NOTES to each record, preserving every
original field. This costs one extra GetPerson call per distinct person in
the result, which is why it's opt-in rather than on by default.
get_access_history has no date-range filter: its previous date-range
parameters were removed entirely, closing
#47 — they didn't match
GetAccessHistory's real NBAPI field names, and a live controlled A/B test
this session found that even the correct field names don't work: the
controller silently ignores them and returns the same records regardless of
the requested range, no error, just no effect. Renaming would have only
traded a loud failure for a silently wrong one, so date-range filtering is
dropped rather than fixed — the same reasoning already documented above for
get_reader_access_history.
get_access_history and get_card_access_details both also accept
RESOLVEDESCRIPTIONS (default true — on by default; the same
opt-out default as get_reader_access_history's own RESOLVEDESCRIPTIONS
above, and unlike RESOLVENAMES, which defaults to off): unless explicitly
set to false, each returned record is enriched with the reader's
human-readable READERDESCRIPTION alongside its existing READER (or
PORTALNAME, for get_card_access_details) code, preserving every other
field. Both tools share the same src/readerDescriptions.ts helper
get_reader_access_history uses. It defaults to on rather than off because,
unlike person-name enrichment, the underlying GetReaders fetch has a fixed
cost — this controller's entire reader table (68 readers) fetches in exactly
2 paginated calls regardless of how many result records are returned, so
there's no scaling cost to make callers opt in to. Set
RESOLVEDESCRIPTIONS: false to skip the GetReaders call and get the plain
(unenriched) response. On both get_access_history and
get_card_access_details, RESOLVENAMES and RESOLVEDESCRIPTIONS are
independent flags — either, both, or neither may be requested in the same
call.
get_card_access_details also accepts its own RESOLVENAMES: true (default
false), enriching the response with the card owner's
FIRSTNAME/LASTNAME/FULLNAME/NOTES via the same shared
src/personEnrichment.ts helper get_access_history uses. Unlike
get_access_history (whose response can carry many distinct PERSONIDs, one
per record), GetCardAccessDetails' response carries exactly one PERSONID
at the top level — a card belongs to one person — so this costs a single
GetPerson call per tool call, not one per distinct person. The four
enrichment fields land on the top level of the response, alongside
PERSONID/DISABLED/EXPDATE, rather than being duplicated onto every
ACCESS record.
get_access_level accepts RESOLVEGROUPNAMES (default true — on by
default, the same opt-out default as the other RESOLVE* flags above,
since GetAccessLevel carries exactly one TIMESPECGROUPKEY and one
READERGROUPKEY per call, so resolving both always costs exactly one
fixed-size GetTimeSpecGroups fetch and one fixed-size GetReaderGroups
fetch, never scaling with anything): unless explicitly set to false, it
resolves the response's bare TIMESPECGROUPKEY/READERGROUPKEY foreign
keys into new sibling TIMESPECGROUPNAME/READERGROUPNAME fields, using
the new src/timeSpecGroupNames.ts/src/readerGroupNames.ts helpers.
TIMESPECGROUPKEY is resolved via the full paginated GetTimeSpecGroups
list, filtering client-side for the matching key — never the singular
GetTimeSpecGroup command, which is verified broken on this controller: it
returns CODE=FAIL/ERRMSG="NOT FOUND" even for a genuinely existing group
(the same finding already documented for src/unlockWindow/managed.ts).
READERGROUPKEY is resolved the same way, via the full paginated
GetReaderGroups list, for consistency. An empty/absent key on either axis
independently skips that axis's fetch and yields '' for just that axis's
name, without affecting the other. THREATLEVELGROUPKEY is never
resolved and is left exactly as-is — no NBAPI read command for threat level
groups exists in this server's command surface at all. If the underlying
GetTimeSpecGroups/GetReaderGroups fetch itself fails, that axis's name
resolves to '' and the call still succeeds with the primary
GetAccessLevel data intact — an enrichment failure never loses the primary
data. Set RESOLVEGROUPNAMES: false to skip both fetches and get the
response back exactly as GetAccessLevel provides it.
get_portal_group accepts the same RESOLVEGROUPNAMES flag (default
true, same opt-out default and identical kind of lookup as
get_access_level's own RESOLVEGROUPNAMES above): unless explicitly set to
false, it resolves the response's bare UNLOCKTIMESPECGROUPKEY foreign key
into a new sibling UNLOCKTIMESPECGROUPNAME field, reusing the same
src/timeSpecGroupNames.ts helper (and so the same full-paginated-list
resolution, never the broken singular GetTimeSpecGroup command). A single
GetPortalGroup response carries exactly one UNLOCKTIMESPECGROUPKEY, so
this always costs exactly one fixed-size GetTimeSpecGroups fetch, never
scaling with anything. The already-human-readable PORTALS sub-list
({PORTALKEY, NAME} per portal) is left completely unchanged.
THREATLEVELGROUPKEY is never resolved — no NBAPI read command for
threat level groups exists in this server's command surface. An
empty/absent UNLOCKTIMESPECGROUPKEY skips the fetch entirely and yields
'' for the name; if the underlying GetTimeSpecGroups fetch itself fails,
UNLOCKTIMESPECGROUPNAME resolves to '' and the call still succeeds with
the primary GetPortalGroup data (including PORTALS) intact. Set
RESOLVEGROUPNAMES: false to skip the fetch and get the response back
exactly as GetPortalGroup provides it.
get_portal_groups accepts the same RESOLVEGROUPNAMES flag (default
true, same opt-out default and field name as the singular
get_portal_group above — this is its explicitly-planned follow-on):
unless explicitly set to false, it resolves every returned group's bare
UNLOCKTIMESPECGROUPKEY foreign key into a new sibling
UNLOCKTIMESPECGROUPNAME field, reusing the same
src/timeSpecGroupNames.ts helper. Unlike the singular tool (whose response
carries exactly one UNLOCKTIMESPECGROUPKEY, so it does at most one
conditional fetch), this plural tool builds the
fetchTimeSpecGroupNames map once per call — only if at least one group
on the page carries a non-empty UNLOCKTIMESPECGROUPKEY (zero
GetTimeSpecGroups calls if every group's key on the page is empty) — then
looks every group up against that same shared map, the same one-fetch-per-
page cost shape as get_time_spec_groups's own RESOLVEMEMBERNAMES above,
never one fetch per group. Unlike GetPortalGroup (singular), GetPortalGroups'
response is already flat per item — DETAILS.PORTALGROUPS.PORTALGROUP[], no
per-item PORTALGROUP wrapper — so no per-item unwrap is applied; that
wrapper quirk belongs only to the singular command's own response envelope.
The already-human-readable PORTALS sub-list ({PORTALKEY, NAME} per
portal) is left completely unchanged on every group.
THREATLEVELGROUPKEY is never resolved — no NBAPI read command for
threat level groups exists in this server's command surface. A group with an
empty/absent UNLOCKTIMESPECGROUPKEY gets UNLOCKTIMESPECGROUPNAME: ''
without needing a match; a group whose key has no match in the fetched map
also gets ''. If the underlying GetTimeSpecGroups fetch itself fails,
every group's UNLOCKTIMESPECGROUPNAME resolves to '' and the call still
succeeds with every group's other fields (including PORTALS) intact — an
enrichment failure never loses the primary data. Set RESOLVEGROUPNAMES: false to skip the fetch and get groups back exactly as GetPortalGroups
provides them.
list_events accepts RESOLVEPARTITIONNAMES (default true — on by
default, the same opt-out default as the other RESOLVE* flags above):
unless explicitly set to false, each returned event's bare PARTITIONID
is resolved into a new sibling PARTITIONNAME field via one GetPartitions
fetch per call — not per event, since GetPartitions takes no
STARTFROMKEY at all and always answers every partition in a single
response, so the cost never scales with how many events come back. This is
backed by the new src/partitionNames.ts helper, mirroring
src/readerDescriptions.ts's shape exactly (a Map-returning fetch
function that never throws). An event whose PARTITIONID has no match in
the fetched map resolves to PARTITIONNAME: '', and if the underlying
GetPartitions fetch itself fails, every event's PARTITIONNAME resolves
to '' and the call still succeeds with every other field (including
ACTIONS) intact — an enrichment failure never loses the primary data. Set
RESOLVEPARTITIONNAMES: false to skip the GetPartitions call and get the
response back exactly as ListEvents provides it.
get_time_spec_groups accepts RESOLVEMEMBERNAMES (default true — on
by default, the same opt-out default as the other RESOLVE* flags above,
since resolving every group's members on a page always costs exactly one
fixed-size GetTimeSpecs fetch, never scaling with how many groups/members
are on the page): unless explicitly set to false, each group's
TIMESPECKEYS.TIMESPECKEY field — which GetTimeSpecGroups returns as bare
TIMESPECKEY string(s) — is replaced with a list of {TIMESPECKEY, NAME}
objects, matching this codebase's own convention for other group-membership
sub-lists that NBAPI already returns as objects natively (get_access_level_group's
ACCESSLEVELS, get_reader_group's READERS). A member key with no match
in the fetched GetTimeSpecs table (an unknown/deleted time spec) resolves
to NAME: '' rather than being omitted. Every other field
(TIMESPECGROUPKEY, the group's own NAME, DESCRIPTION) is unchanged. The
name lookup uses the new src/timeSpecNames.ts helper — one full paginated
GetTimeSpecs fetch per call, regardless of how many groups/members are on
the page — and reuses keyList (src/paging.ts, relocated from
src/unlockWindow/managed.ts) to normalize the bare-key collection. If the
underlying GetTimeSpecs fetch itself fails, every member's NAME resolves
to '' and the call still succeeds with every group's own fields intact —
an enrichment failure never loses the primary data. RESOLVEMEMBERNAMES
applies only to this plural tool, not the singular get_time_spec_group,
which is verified broken (CODE=FAIL/ERRMSG="NOT FOUND") on this
controller even for a genuinely existing group, independent of this change.
Set RESOLVEMEMBERNAMES: false to skip the fetch and get TIMESPECKEYS
back exactly as GetTimeSpecGroups provides it (bare string or array of
strings).
Write tools and Destructive tools
write (needs only NETBOX_ENABLE_WRITES) and destructive (needs
NETBOX_ENABLE_WRITES and NETBOX_ENABLE_DESTRUCTIVE). Every write
tool's input schema declares exactly the documented PARAMS fields for its
command, matching required/optional as documented — see the "Write access"
section above for the gating rules and the shared SUCCESS/WRITE:/
DESTRUCTIVE: conventions.
Tool | Wraps NBAPI command | Required params | Tier |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
|
| write |
|
| — | write |
|
|
| write |
|
| — | write |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
|
|
| write |
|
|
| write |
|
|
| destructive |
The twelve hardware tools (*_mercury_panel, *_network_node, *_sio —
six writes and three destructive deletes, plus the six reads in the table
above) are not live-verified: the reference controller this project is
developed against has no Mercury panels and no SIOs, so their read tools
SKIP-pass in npm run test:live and their write tools have never been issued
against real hardware. They are built to the April-2025 NBAPI v2 guide alone
— see the header comment in src/tools/hardware.ts and
docs/reference/nbapi-command-diff.md.
modify_portal_group and modify_reader_group always replace the group's
membership with the PORTALKEYS/READERKEYS you send — on this controller
(6.2.0, verified live) an omitted or unparsed list empties the group instead
of leaving it unchanged, so both tools require the complete membership.
trigger_event is unverified live on 6.x; NETBOX_EVENT_API_PATH is
available to override the request path if your controller serves the Event
API separately from the main NBAPI path (see the environment variable table
above).
switch_partition changes the partition for every later call made by this
server process, not just the caller's own next request — the NBAPI session
is cached and reused, and SwitchPartition has no per-call scope.
Person / credential tools and Active Directory
If this NetBox instance syncs person/access-level data from Active
Directory, any write this server makes to a synced field is silently
overwritten on the next AD sync — add_person and modify_person both
carry this caution in their tool descriptions. Separately, modify_person's
ACCESSLEVELS has two syntaxes: a plain list of access-level name strings
replaces the person's entire set of access levels, while a list of
{ ACCESSLEVELNAME, DELETE?, ACTDATE?, EXPDATE?, AUTOREMOVE? } blocks is
additive (adds/removes individual levels without touching the rest). Mixing
the two syntaxes in one call is rejected client-side before any command is
sent.
Scheduled unlock windows
"Unlock these doors from start to end" is one call —
schedule_unlock_window — and the controller itself enforces the
schedule: no process has to stay alive to relock the doors, so the MCP host
can go away the moment the call returns.
How it works (the same objects an operator creates by hand). The window is
realised as a Holiday covering the dates, a Time Spec with no weekdays
and only one holiday group ticked, and a Portal Group whose Unlock Time
Spec is that time spec's group. A time spec with no weekdays and holiday
group G ticked is active only on dates covered by a holiday in group G, so
the portals unlock exactly on the window's dates and clock range. A window
that spans midnight is split into up to three segments — first (start time →
23:59 on the start date), middle (00:00 → 23:59 on every date strictly
between, if any) and last (00:00 → end time on the end date) — each with its
own holiday + time spec pair.
Managed objects and the single-window model. Everything the tool creates
is named with NETBOX_UNLOCK_NAME_PREFIX (default MCP Unlock Window): the
portal group is named exactly <prefix>, the time spec group <prefix> time specs (never <prefix> — group names are unique across group types on this
controller, so a portal group and a time spec group cannot share a name), and
the per-segment holidays and time specs <prefix> first, <prefix> middle,
<prefix> last. Names are the identity. There is one managed window at a
time: scheduling a new one rewrites those same objects (modifying what
exists, adding what is missing, deleting leftover segments from the previous
window), and calling it twice with the same arguments is idempotent (only
Modify/Get commands, same keys). The composite tools never modify or delete
any object whose name is not exactly one of those; a user-created object
that happens to carry one of those names is treated as managed. The apply
order is fixed — resolve portals, managed time spec group, per-segment
holiday + time spec, group membership, delete leftovers, managed portal group
— and every step is read back and compared to the plan before the tool
reports verified: true; any mismatch is a tool error describing the field.
If any apply step fails, the tool rolls back by deleting every managed
holiday and time spec written so far (mirroring cancel_unlock_window's
cleanup) before returning the error, so no partial window is left active;
the error text names the failed step, the controller's message, and what the
rollback removed.
Reserved holiday groups. NetBox has exactly eight holiday groups (1–8),
shared by every time spec on the controller. NETBOX_UNLOCK_HOLIDAY_GROUPS
(default 8,7,6) reserves one group per segment kind (first, middle,
last, in that order). Reserve groups nothing else on the controller uses.
With fewer than three groups configured, only windows needing that many
segments can be scheduled (one group = same-day windows only); the tool never
doubles up a group, because two segments sharing one would each unlock on the
other's dates.
The side-effect check and acknowledgeSideEffects. A holiday in group
G suppresses, on its dates, every time spec that does not tick G — an
access level whose time spec ticks only groups 1–3, say, would lose access
during a window that uses group 8. Before writing anything,
schedule_unlock_window reads every time spec and holiday and reports
suppressedTimeSpecs (time specs other than Never and its own that lack a
group the plan uses) and overlappingHolidays (non-managed holidays whose
dates intersect the window — reported, never touched). If any time spec would
be suppressed, the call is refused with nothing written unless
acknowledgeSideEffects=true. dryRun=true returns the plan and the report
without writing anything, whether or not you acknowledged. The built-in
Always time spec ticks all eight groups and is never affected.
Cancelling. cancel_unlock_window first, if the managed portal group
exists, points it at the built-in Never time spec group (re-sending its
current portals); then, regardless of whether that portal group exists,
deletes the managed holidays and empties the managed time spec group and
deletes the managed time specs. The last two are best-effort: if the
controller refuses them, the tool still succeeds and lists what was left
under leftBehind, because once the portal group (if any) is on Never and
no managed holiday exists, nothing can unlock. The managed portal group and
time spec group are kept (pointing at Never / empty) and reused by the next
window. The tool reports there was nothing to cancel only when no managed
object of any kind — portal group, time spec group, holiday, or time spec —
exists.
get_unlock_window (always registered, read-only) shows the current managed
state — the portal group and whether it points at the managed time spec
group, that group's members (read from GetTimeSpecGroups, because
GetTimeSpecGroup returns FAIL/NOT FOUND on the verified 6.2.0
controller), the managed time specs and holidays — plus the window derived
from them and activeNow on the host clock.
Limits and caveats.
A window must end in the future and be at most 31 days long. Holidays are capped at 30 per partition, so a window whose segments would push past that is refused.
portalKeysare keys only (useget_portalsorfind_portalsto map names); an unknown key is refused before anything is written.End of day on the NBAPI is
23:59(the built-inAlwaysuses it), andENDTIMEis inclusive through the end of that minute, so there is no midnight gap between segments of a multi-day window. A window's door relocks up to 59 seconds after the statedendminute (observed live: a window ending08:27relocked at08:27:59controller time). Anendof00:00means "up to 23:59 of the previous day".Times are the controller's local time. The MCP host is assumed to share the controller's timezone; the host clock is used only to reject windows that have already elapsed and to compute
activeNow.The physical unlock is not observable through the NBAPI: no read command exposes portal state, and
GetEventHistorycarries no Unlock/Relock activity. The tool verifies its work by reading the configuration objects back and comparing them to the plan; confirm the door itself on Monitor → Portal Status or in person.set_portals_stateis the immediate alternative: itsUNLOCKis an Extended Unlock that lasts untilLOCK, with nothing scheduling the relock.
Session handling, retry-on-expired-session, and error mapping are all automatic and match the NBAPI documentation:
The first tool call triggers
Login; the session ID is cached and reused for every later call in the same server run.If a call fails with
APIERROR 5(auth failure / expired session), the client transparently re-logs-in once and retries the original command.An
<APIERROR>response surfaces as a tool error like"5: There was an authentication failure."A
<CODE>FAIL</CODE>response surfaces as a tool error including the controller'sERRMSGtext verbatim.A
<CODE>NOT FOUND</CODE>response (e.g. an unknownPERSONID) is returned as a normal, non-error result stating "not found" — it is not thrown as an exception.SIGINT/SIGTERMtriggerLogoutfor any active session before the process exits.
Scheduled daily unlock windows
The companion to Scheduled unlock windows above: "unlock these doors from
dailyStartTime to dailyEndTime, every day from startDate through
endDate" — a single partial-day window that recurs daily across a date
range, which schedule_unlock_window cannot express (it models one
continuous span, so a multi-day request there keeps doors unlocked overnight
on the days strictly between the first and last). schedule_daily_unlock_window
relocks the doors every night outside the daily window.
How it works (reusing the same mechanism). This reuses the exact
holiday + time spec + portal group mechanism described above: a Holiday
spanning the whole date range, a Time Spec with no weekdays and
only the one reserved daily holiday group ticked, and a Portal Group whose
Unlock Time Spec is that time spec's group. Because a time spec with no
weekdays and holiday group G ticked is active during its
STARTTIME-ENDTIME on every date covered by a holiday in group G,
one holiday (covering every date in the range) paired with one partial-day
time spec already expresses "the same time-of-day window, every day in the
range" — no first/middle/last segment-splitting is ever needed, unlike
the continuous feature, whose planner has to split a multi-day span into up
to three segments precisely because a middle day needs a full 00:00-23:59
grant. This feature's plan is always exactly one segment.
Environment variables and collision safety. NETBOX_DAILY_UNLOCK_HOLIDAY_GROUP
(default 5) is the single holiday group this feature reserves, and
NETBOX_DAILY_UNLOCK_NAME_PREFIX (default MCP Daily Unlock Window) names
its managed objects — see the environment variable table above for both.
NETBOX_DAILY_UNLOCK_HOLIDAY_GROUP is validated at startup to never be a
member of NETBOX_UNLOCK_HOLIDAY_GROUPS, so this feature and the
continuous one always use disjoint holiday groups, and — because each
feature's managed portal group and time spec group are named after its own
prefix — disjoint managed-object names as well. Consequently the two
features can be active at the same time: a daily window and a continuous
window may both be scheduled and unlocking doors concurrently, with no
shared NBAPI object between them. This also means schedule_daily_unlock_window's
side-effect check does not special-case a currently-active continuous
window — if its dates happen to overlap the daily plan's dates, it is
reported like any other foreign time spec/holiday, which is the correct,
general behaviour rather than a special case.
Managed objects and the single-daily-window model. Everything this
tool creates is named with NETBOX_DAILY_UNLOCK_NAME_PREFIX: the portal
group is named exactly <prefix>, the time spec group <prefix> time specs
(never <prefix> — group names are unique across group types on this
controller, same as the continuous feature), and the one holiday and one
time spec <prefix> schedule (same name, different object tables — no
collision). Names are the identity. There is one managed daily window at a
time: scheduling a new one rewrites those same objects (modifying what
exists, adding what is missing), and calling it twice with the same
arguments is idempotent (only Modify/Get commands, same keys). The tools
never modify or delete any object whose name is not exactly one of those; a
user-created object that happens to carry one of those names is treated as
managed. The apply order is fixed — resolve portals, managed time spec
group, managed holiday, managed time spec, group membership, managed portal
group — and every step is read back and compared to the plan before the
tool reports verified: true; any mismatch is a tool error describing the
field. If any apply step fails, the tool rolls back by deleting the managed
holiday and time spec written so far (mirroring cancel_daily_unlock_window's
cleanup) before returning the error, so no partial window is left active;
the error text names the failed step, the controller's message, and what
the rollback removed.
The side-effect check and acknowledgeSideEffects. Exactly as for the
continuous feature: a holiday in the reserved daily group suppresses, on its
dates, every time spec that does not tick that group. Before writing
anything, schedule_daily_unlock_window reads every time spec and holiday
and reports suppressedTimeSpecs (time specs other than Never and its own
that lack the reserved daily group) and overlappingHolidays (non-managed
holidays whose dates intersect the window — reported, never touched). If any
time spec would be suppressed, the call is refused with nothing written
unless acknowledgeSideEffects=true. dryRun=true returns the plan and the
report without writing anything, whether or not you acknowledged.
Cancelling. cancel_daily_unlock_window first, if the managed
portal group exists, points it at the built-in Never time spec group
(re-sending its current portals); then, regardless of whether that portal
group exists, deletes the managed holiday if it exists, and empties the
managed time spec group and deletes the managed time spec if either exists.
The last two are best-effort: if the controller refuses them, the tool still
succeeds and lists what was left under leftBehind, because once the portal
group (if any) is on Never and no managed holiday exists, nothing can
unlock. The managed portal group and time spec group are kept (pointing at
Never / empty) and reused by the next daily window. The tool reports there
was nothing to cancel only when no managed object of any kind — portal
group, time spec group, holiday, or time spec — exists.
get_daily_unlock_window (always registered, read-only) shows the current
managed state — the portal group and whether it points at the managed time
spec group, that group's members, the one managed time spec and holiday
(singular, not arrays — this feature never has more than one of each) —
plus the window derived from them and activeNow on the host clock.
Limits and caveats.
A window must end in the future and span at most 31 days (
endDate - startDate). Holidays are capped at 30 per partition, so a window whose one holiday would push past that is refused.portalKeysare keys only (useget_portals/find_portalsto map names); an unknown key is refused before anything is written.An overnight-crossing daily window is not supported:
dailyEndTimemust be strictly later thandailyStartTime(same-day time-of-day only). A request like "10 PM to 5 AM, every night" is rejected — a future extension could express this as two segments, but it is out of scope here.There is no per-weekday selectivity: the whole
[startDate, endDate]range unlocks every day at the given time-of-day (no "weekdays only" filtering).Dates are
YYYY-MM-DDand times areHH:MM, both controller-local; the same midnight-relock and host/controller-clock-assumption caveats as the continuous feature above apply here too.The physical unlock is not observable through the NBAPI — confirm the door on Monitor → Portal Status or in person, exactly as above.
Testing
npm testRuns the full unit test suite against a hand-rolled, in-memory HTTP stub — no network access and no live controller are required or contacted.
Live smoke test (optional)
npm run test:liveThis calls all read tools except get_unlock_window/get_daily_unlock_window
(35 of the 37 — see Tools exposed below) against a real, configured controller and
prints a PASS/FAIL line per tool plus a summary, exiting non-zero if
anything failed. It only runs if NETBOX_BASE_URL, NETBOX_USERNAME, and
NETBOX_PASSWORD are all set (loaded from .env if present); otherwise it
prints one line saying live testing was skipped and exits 0. It never prints
the value of NETBOX_PASSWORD, under any circumstance, and it never issues
a write/control command regardless of NETBOX_ENABLE_WRITES. npm test
never runs this script and never requires .env to exist.
Live write smoke test (optional, opt-in twice)
npm run test:live:write # CRUD round-trips only
npm run test:live:write -- --go # ... plus the real 2-minute unlock window
npm run test:live:write -- --go --start 14:30 # pin the unlock time (1-60 min ahead)PowerShell: on at least one PowerShell/npm combination this silently drops flags passed after
--(npm printsnpm warn Unknown cli config "--go"and the flag never reaches the script — observed live, 2026-09-15). If--godoesn't trigger phase (c), call the script directly instead:npx tsx scripts/live-check-write.ts --go.
This skips with one line and exit 0 — making no network call — unless
NETBOX_BASE_URL, NETBOX_USERNAME, NETBOX_PASSWORD,
NETBOX_ENABLE_WRITES=true and NETBOX_LIVE_TEST_PORTALKEY are all set.
Most of the round-trips below issue deletes/removes directly against the
controller (independent of the MCP server's own NETBOX_ENABLE_DESTRUCTIVE
gating, which this script bypasses by calling the NBAPI client directly), so
set NETBOX_ENABLE_DESTRUCTIVE=true before running it.
Otherwise it round-trips add → get → modify → get → delete for a time spec, a
time spec group, a holiday, a reader group, and a portal group under the
distinct prefix MCP livecheck (the portal group's unlock time spec group is
Never and the holiday is in 2099, so nothing can unlock), asserting each
read-back. It then round-trips a person (AddPerson → GetPerson →
ModifyPerson → GetPerson) plus a credential on that person (AddCredential
→ GetPerson with WANTCREDENTIALID → ModifyCredential with DISABLED=1
→ read-back → RemoveCredential → read-back) → RemovePerson, accepting
either NOT FOUND or DELETED=TRUE on the final GetPerson (never sends
PERSONPURGE); an access level (AddAccessLevel with TIMESPECGROUPKEY
Never → GetAccessLevel → ModifyAccessLevel → read-back →
DeleteAccessLevel → read-back gone) plus an access level group built from a
second temporary access level (AddAccessLevelGroup → GetAccessLevelGroup
→ ModifyAccessLevelGroup → read-back → DeleteAccessLevelGroup, tolerating
the same FAIL/ERRMSG="NOT FOUND" quirk documented for GetTimeSpecGroup
against an empty collection); a threat level plus a threat level group
(AddThreatLevel → AddThreatLevelGroup → ModifyThreatLevel →
ModifyThreatLevelGroup → RemoveThreatLevelGroup → RemoveThreatLevel,
proven gone by a second RemoveThreatLevel failing — GetThreatLevels is
not used for round-trip verification here, and SetThreatLevel is never
called); InsertActivity
with a timestamped USERACTIVITY record; a UDF list item round-trip via
ModifyUDFListItems (or a recorded SKIPPED pass if no UDF list is
configured); and GetPartitions → SwitchPartition back to the session's
own partition (AddPartition is never called). It cleans up any
MCP livecheck leftovers — including persons, access levels/groups, and
threat levels/groups — from an aborted run, both before and after the round
trips.
It then estimates the controller's clock from the newest GetAccessHistory
record and refuses to run the door phase — regardless of --go — when that
estimate disagrees with the host clock by more than 2 minutes; window times
passed to schedule_unlock_window are always controller-local, not host-local.
With --go — pass it only after notifying the user (push notification
plus a chat message giving the exact unlock and relock clock times) and
receiving a go-ahead, because they observe the door — it prints a
HEADS-UP line, schedules a real 2-minute unlock of the designated portal
through the real schedule_unlock_window executor (unlock at now + 2 min and
relock at now + 4 min, or at --start HH:MM), prints OBSERVE: portal ... should unlock at HH:MM and relock at HH:MM — confirm on Monitor → Portal Status, polls get_unlock_window every 30 s until one minute after relock,
then calls cancel_unlock_window and asserts the managed portal group is on
Never with no managed holiday, time spec, or time spec group member left
(leftBehind is tolerated but reported). It refuses that phase if a managed
window already exists (so it never replaces a real one); apart from the
supervised single actions below, it never touches outputs, TriggerEvent, or
portal lock/unlock actions, never prints the password, exits non-zero on any
failed assertion (still cancelling the window first), and npm test never
runs it.
Supervised single actions
npm run test:live:write -- --action unlock_portal
npm run test:live:write -- --action set_threat_level --value High--action <name> [--value <v>] runs exactly one write against the
designated portal (or its strike output) instead of the full flow above —
skipping phases (b), (b2), and (c) entirely. It still requires
NETBOX_ENABLE_WRITES=true and the credential variables (same skip line as
above), but not NETBOX_ENABLE_DESTRUCTIVE, since no deletes happen. It
refuses to run — exit 2, no network call — if --action is combined with
--go, if the action name is unknown, or if set_threat_level's required
--value is missing. It prints the exact command and params sent (never
credentials), the controller's CODE/DETAILS or ERRMSG, and an
OBSERVE: ... line describing what to check at the door or on Monitor; a
FAIL with ERRMSG "Portal state not changed" is reported as
PASS-with-note rather than a failure. Exits 0 on success or already-in-state,
1 otherwise, and unknown/invalid arguments exit 2.
Every action is reversible:
Action | Effect | Reverse |
|
|
|
|
| — |
|
| — |
|
|
|
|
|
|
|
| — |
| the real |
|
|
| — |
|
| — |
|
|
|
|
|
|
|
| — |
activate_output/deactivate_output resolve the strike output by finding
the GetOutputs entry whose NAME starts with the designated portal's
NAME (e.g. portal "02OF01A" → output "02OF01A EL"), failing clearly if
none is found. AddPartition is never reachable through --action, same as
the rest of this script.
trigger_event_activate/trigger_event_deactivate require --value <EVENTNAME> — the name of a NetBox event that must already exist (events
cannot be created via the NBAPI; create it first in the NetBox UI). This is
the only live verification path for trigger_event — the full CRUD
flow above never calls TriggerEvent. Both actions go through the same
NetboxClient.call as every other command, so NETBOX_EVENT_API_PATH
routing still applies; the script prints which URL path it used.
Live write smoke test — daily unlock window (optional, opt-in twice)
npm run test:live:write:daily # CRUD round-trips only
npm run test:live:write:daily -- --go # ... plus the real 2-minute daily unlock windowPowerShell: see the same-named caveat under "Live write smoke test" above — if
--gois silently dropped, usenpx tsx scripts/live-check-write-daily.ts --goinstead (verified live, 2026-09-15, on portal02OF01A: unlock/relock confirmed in person, 16/16 steps PASS).
A sibling script to npm run test:live:write above, covering
schedule_daily_unlock_window/cancel_daily_unlock_window/
get_daily_unlock_window (kept as a separate npm script rather than
chained onto test:live:write so -- --go keeps reaching the script it is
meant for). It skips with one line and exit 0 — making no network call —
under the same gating as npm run test:live:write. Otherwise it round-trips
add → get → modify → get → delete for a time spec, a time spec group, a
holiday, and a portal group under the distinct prefix MCP livecheck daily
(the portal group's unlock time spec group is Never and the holiday is in
2099, so nothing can unlock), asserting each read-back, then runs the same
controller-clock-skew guard as test:live:write (refusing the door phase
above a 2-minute skew regardless of --go).
With --go — pass it only after notifying the user and receiving a
go-ahead, same as above — it prints a HEADS-UP line, schedules a real
2-minute daily unlock covering only today's date (dailyStartTime = now
2 min,
dailyEndTime= now + 4 min) on the designated portal through the realschedule_daily_unlock_windowexecutor, prints anOBSERVE: ...line, pollsget_daily_unlock_windowevery 30 s until one minute after relock, then callscancel_daily_unlock_windowand asserts the managed portal group is onNeverwith no managed holiday or time spec left (leftBehindis tolerated but reported). It refuses that phase if a managed daily window already exists, never touches persons, credentials, access levels, threat levels, outputs, events, partitions, or UDF lists, never prints the password, exits non-zero on any failed assertion (still cancelling the window first), andnpm testnever runs it.
Out of scope
Photo upload — the multipart POST to
/nbws/goforms/upload, which is not an NBAPI XML command at all. Reading a person's photo is supported:get_picturewrapsGetPictureand returns the Base64 JPEG unmodified.Elevator and floor writes — not a choice: neither the v1 nor the v2 guide documents any
Add/Modify/Deletecommand for elevators or floors, soget_elevators/get_floorsare read-only because the API is. See the diff report.Data Operations (bulk person import/export) — the LenelS2 Data Operations guide describes a web-UI and NAS-polling feature with no API of its own, so there is nothing to wrap. No import-file builder, export parser or NAS automation is planned.
Alarm-queue workflow commands (
AckAlarm,AckEvent,AlarmClearActions,AlarmSetOwner,EventClearActions) —get_alarmsreads alarms, but this server does not drive an operator alarm queue.StreamEvents/ the persistent/appdevent/nbapi/eventpush feedMAC-based authentication (session-login only)
The S2 Global API variant
The deprecated NBAPI commands (
EditPerson,EditThreatLevel,EditThreatLevelGroup,GetAccessDataLog,GetAccessCardDetails,LoginUserName,LoginUserPassword)Multiple concurrent managed unlock windows, per-window naming, or any persistence on the MCP host (there is one managed window; names are its identity)
Resolving portals by name in the composite tools (keys only —
get_portals/find_portalsmap names)Automatically deleting the managed portal group or time spec group on cancel (they stay, pointing at
Never/ empty, and are reused)Any scheduler on the host (Task Scheduler, in-process timers) — the controller is the only scheduler
Confirmation prompts inside the server — the MCP host's permission model and the environment gates are the controls
Any GUI/dashboard beyond the MCP tool surface
See specs/archive/s2-netbox-mcp-write.md for the full requirements the write-tool
surface was built against, and specs/archive/s2-netbox-mcp.md for the
original read-only v1 spec (archived — all its acceptance criteria passed,
including live verification).
Contributing
Bug reports, feature requests, and PRs are welcome — see CONTRIBUTING.md for the
workflow (issue first, branch naming, PR conventions) and the physical-safety note that
applies to any change touching write/destructive tools.
Security
Found a vulnerability? See SECURITY.md for how to report it privately and what the
physical-safety blast radius looks like at each configuration level.
Available Tools
51 toolscheck_connectionA
Confirms the server can authenticate to the configured S2 NetBox controller and returns the NBAPI version string (wraps GetAPIVersion). No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of disclosing behavior. It clearly states that the tool performs an authentication check and returns a version string, and it names the wrapped API. This is transparent for a simple read/check operation, though it does not describe failure behavior.
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 sentences, no filler, with the core purpose front-loaded. Every phrase adds useful information: what it confirms, what it returns, and the wrapped API method.
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 zero-parameter connectivity check with no output schema, the description is complete: it states the action, the target system, the return value, and the underlying API. Nothing essential 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?
The tool has zero parameters, so there is little to document. The description explicitly states 'No parameters required,' which reinforces the empty schema and removes any ambiguity for the agent.
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 ('confirms'), a specific resource ('server can authenticate to the configured S2 NetBox controller'), and a concrete outcome ('returns the NBAPI version string'). It also names the wrapped API method (GetAPIVersion), which clearly distinguishes it from the many get/list sibling tools.
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 description gives clear context for when to use the tool: to verify server authentication/connectivity to the S2 NetBox controller. It does not explicitly name alternatives or exclusions, but among siblings it is the only connection-checking tool, so the usage context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_portalsA
Finds portals (doors) by location or name. Portal names are site codes (e.g. 01OF05A), so this also searches each portal's reader names and reader descriptions (e.g. 'WORKSHOP TO MAINTENANCE OFFICE'). Case-insensitive; a portal matches when every whitespace-separated term appears in its name, a reader name, or a reader description. Reads every page of GetPortals and GetReaders and joins them by READERKEY. The result also lists portals with no reader description, which can only match by name.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Required. Search terms, e.g. "maintenance office", "electrical closet", or "01OF05". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it does so thoroughly. It discloses case-insensitive matching, the all-terms-must-match rule, the fields searched, the full-page reads of GetPortals and GetReaders, the join key READERKEY, and the special case of portals with no reader description. This is far beyond what the minimal schema or annotations would provide.
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?
Every sentence is functional: the first states what it finds, the second clarifies name semantics and examples, the third defines exact matching behavior, and the fourth explains the no-reader-description edge case. There is no filler or repetition of the schema.
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 the tool's low complexity (one required parameter), no annotations, and no output schema, the description provides enough detail for an agent to invoke it correctly and understand matching behavior. It does not describe the output shape or error cases, but those are less critical for a search-style tool when the matching and result inclusion behavior are already clearly specified.
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?
Although the schema already describes the single query parameter with examples, the description adds substantial meaning beyond it: whitespace-separated terms are treated as separate search terms, matching is case-insensitive, and every term must appear for a match. These semantic rules materially affect how an agent should construct a query, so the description earns credit above the baseline.
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 uses the specific verb 'Finds' with a clearly defined resource, 'portals (doors)' and explains exactly what is searched: name, reader names, and reader descriptions. It effectively distinguishes this tool from sibling raw-listing tools like get_portals and get_readers by describing a joined, term-based search rather than a raw retrieval. The example site codes and human-readable names make the intent unmistakable.
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 description implies the main use case: searching for portals by location or name rather than listing all portals. However, it never explicitly says when to use this tool versus siblings like get_portals or get_readers, and it gives no exclusion criteria. The agent must infer the alternative from the sibling list and context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_access_historyA
Returns historical access (grant/deny) records for optional filters (wraps NBAPI GetAccessHistory). Identifies a person by ENCODEDNUM/HOTSTAMP, not PERSONID — GetAccessHistory has no PERSONID parameter. Set RESOLVENAMES: true to enrich each returned record with the badge-holder's FIRSTNAME/LASTNAME/FULLNAME/NOTES (default false — off); enabling it costs one extra GetPerson call per distinct person found in the result, which is why it is opt-in rather than on by default. RESOLVEDESCRIPTIONS defaults to true — the only default-on optional boolean in this codebase (an inverted, opt-out default, unlike RESOLVENAMES/dryRun-style flags elsewhere): each returned record is enriched with the reader's human-readable READERDESCRIPTION via one GetReaders full-table fetch per call (not per record, since the reader table is small and fixed-size); set RESOLVEDESCRIPTIONS: false to skip it.
| Name | Required | Description | Default |
|---|---|---|---|
| ORDER | No | Optional. Sort order for returned records. | |
| HOTSTAMP | No | Optional. Restrict results to this hot-stamp number. | |
| AFTERLOGID | No | Optional. Return records strictly after this LOGID. | |
| CARDFORMAT | No | Optional. Card format of ENCODEDNUM/HOTSTAMP. | |
| ENCODEDNUM | No | Optional. Restrict results to this encoded card number. | |
| MAXRECORDS | No | Optional. Maximum number of records to return. | |
| STARTLOGID | No | Optional. Begin returning records at this LOGID. | |
| RESOLVENAMES | No | Optional (default false). Enrich each returned record with the badge-holder's FIRSTNAME/LASTNAME/FULLNAME/NOTES via one extra GetPerson call per distinct person found in the result. | |
| RESOLVEDESCRIPTIONS | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Enrich each returned record with the reader's human-readable READERDESCRIPTION via one GetReaders full-table fetch per call (not per record). Set to false to skip it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the burden. It discloses the default-off vs default-on asymmetry for RESOLVENAMES/RESOLVEDESCRIPTIONS, the cost model (per-person GetPerson calls, per-call GetReaders fetch), and the fact that identification is only by ENCODEDNUM/HOTSTAMP. This is exactly the kind of non-obvious behavior an agent needs.
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 first sentence is efficient, but the long second half repeats the schema descriptions for RESOLVENAMES and RESOLVEDESCRIPTIONS almost verbatim. Useful rationale remains, but the description could be trimmed to distinguish only what the schema doesn't say.
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 no output schema, it explains the record shape (grant/deny, enriched fields) and all non-obvious defaults. It doesn't list every possible returned field or describe errors, but for an optional-filter lookup with a fully described schema, the essential context is present.
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?
The schema already covers all 9 parameters at 100%, including default values and cost notes. The prose adds a few contextual clues (PERSONID absence, codebase-wide inverted default, rationale for opt-in), but much of it duplicates the schema, so it hovers just above the baseline.
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: 'Returns historical access (grant/deny) records' and immediately clarifies the identifying keys (ENCODEDNUM/HOTSTAMP, not PERSONID), which separates it from person-oriented history tools. It could have explicitly named a sibling like get_reader_access_history, so it is not a 5.
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 describes what filters exist and how to toggle enrichment, so a caller knows the operational knobs, but it never states when to prefer this tool over get_reader_access_history or get_event_history. The usage is implied by 'historical access (grant/deny) records' rather than explicitly contrasted.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_access_levelA
Returns the details of a single access level for a given ACCESSLEVELKEY (wraps NBAPI GetAccessLevel). RESOLVEGROUPNAMES defaults to true — an inverted, opt-out default (unlike most optional booleans in this codebase): the raw response carries only bare TIMESPECGROUPKEY/READERGROUPKEY/THREATLEVELGROUPKEY foreign keys, so this resolves TIMESPECGROUPKEY and READERGROUPKEY into new sibling TIMESPECGROUPNAME/READERGROUPNAME fields via one full-table GetTimeSpecGroups fetch and one full-table GetReaderGroups fetch per call — a fixed cost regardless of anything else, since a single access level carries exactly one of each key. THREATLEVELGROUPKEY is never resolved (no NBAPI read command exists for threat level groups). Set RESOLVEGROUPNAMES: false to skip both fetches and return the response exactly as GetAccessLevel provides it.
| Name | Required | Description | Default |
|---|---|---|---|
| ACCESSLEVELKEY | Yes | Required. The unique ACCESSLEVELKEY of the access level to retrieve. | |
| RESOLVEGROUPNAMES | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Resolves TIMESPECGROUPKEY/READERGROUPKEY into new TIMESPECGROUPNAME/READERGROUPNAME sibling fields via one full-table GetTimeSpecGroups fetch and one full-table GetReaderGroups fetch per call (each made only when that axis's key is non-empty; an empty/absent key on one axis yields '' for that axis's name without affecting the other). THREATLEVELGROUPKEY is never resolved — no NBAPI read command exists for threat level groups. Set to false to skip both fetches and return the response exactly as GetAccessLevel provides it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does so thoroughly. It discloses the inverted default, two full-table fetches per call, fixed cost, the fact that THREATLEVELGROUPKEY is never resolved, and the exact effect of setting RESOLVEGROUPNAMES to false.
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 long but dense and front-loaded with the core purpose. Some content repeats the schema's parameter descriptions, but the added behavioral and performance context makes the length justifiable.
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 no output schema and no annotations, the description is remarkably complete. It explains the return behavior for both default and opt-out paths, the resolution semantics, the unresolved key, and the performance cost, leaving no ambiguity about how the tool behaves.
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?
Input schema coverage is 100%, so the schema already documents both parameters in detail. The description adds value beyond the schema by noting the raw response contains only bare foreign keys, that the resolution cost is fixed because each access level has exactly one of each key, and that it wraps NBAPI GetAccessLevel.
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 opens with a specific verb and resource: 'Returns the details of a single access level for a given ACCESSLEVELKEY.' This clearly distinguishes it from plural/list siblings like get_access_levels and from group-focused tools like get_access_level_group.
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 description clearly establishes the tool's scope—single access level lookup by key—so an agent can infer when to use it. It does not explicitly name alternative tools or state when not to use it, but the singular/plural contrast and key requirement provide clear contextual guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_access_level_groupA
Returns the details of a single access level group for a given ACCESSLEVELGROUPKEY (wraps NBAPI GetAccessLevelGroup).
| Name | Required | Description | Default |
|---|---|---|---|
| ACCESSLEVELGROUPKEY | Yes | Required. The unique ACCESSLEVELGROUPKEY of the access level group to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral burden. 'Returns' and 'wraps NBAPI GetAccessLevelGroup' indicate a read operation and external API coupling, but there is no explicit statement about side effects, errors, permissions, or rate limits. Adequate for a simple read, but not comprehensive.
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?
One sentence with no wasted words: function first, then key, then underlying API wrapper in a parenthetical. It is well-structured and easy to scan.
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 single-key lookup, the description and schema cover the basics. However, with no output schema and no annotation, the agent still doesn't know what 'details' includes or what happens for missing/invalid keys, leaving some uncertainty.
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 schema already documents ACCESSLEVELGROUPKEY as the required unique key. The description only restates that the tool retrieves the group for that key, adding no extra format, constraints, or usage details beyond 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?
States a specific action ('Returns the details') on a specific resource ('a single access level group') keyed by ACCESSLEVELGROUPKEY. The word 'single' helps differentiate it from plural siblings like get_access_level_groups, though it doesn't enumerate which details are returned.
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 description implies this tool is for retrieving one group when you have a specific key, but it never names alternatives such as get_access_level_groups or states when not to use it. Usage context is present only by implication.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_access_level_groupsB
Lists access level groups configured on the NetBox system (wraps NBAPI GetAccessLevelGroups).
| Name | Required | Description | Default |
|---|---|---|---|
| PARTITIONKEY | No | Optional. Per NBAPI GetAccessLevelGroups — only "0" is documented as allowed. | |
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions wrapping NBAPI GetAccessLevelGroups but does not describe pagination behavior, response format, partitioning implications, or any operational expectations such as whether this is a read-only 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, front-loaded sentence with no filler. It communicates the essential action and resource immediately, and the parenthetical NBAPI reference provides useful implementation context without bloat.
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 simple listing tool, the description is minimally adequate, but with no annotations and no output schema, it leaves gaps around pagination and how to continue listings using STARTFROMKEY. The schema documents the parameters, so this is not severely incomplete, but an agent would benefit from more invocation context.
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%, with both PARTITIONKEY and STARTFROMKEY already described in the input schema. The tool description adds no additional parameter-level meaning beyond the NBAPI wrapper reference, so the baseline score of 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 clear verb ('Lists') and a clear resource ('access level groups configured on the NetBox system'), and the plural 'groups' distinguishes it from the singular sibling get_access_level_group. It is specific enough, though it does not explicitly name or differentiate itself from related tools.
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 description gives no guidance on when to use this tool versus the singular get_access_level_group or get_access_levels. It only states what the tool does, so an agent must infer usage context from the name and siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_access_level_namesB
Lists access level names configured on the NetBox system (wraps NBAPI GetAccessLevelNames).
| Name | Required | Description | Default |
|---|---|---|---|
| PARTITIONKEY | No | Optional. Per NBAPI GetAccessLevelNames — only "0" is documented as allowed. | |
| STARTFROMNAME | No | Optional. Pagination cursor (name) to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden. 'Lists' and 'wraps NBAPI GetAccessLevelNames' convey a read-only enumeration and tie the tool to a known API, but the description does not disclose return shape, pagination behavior, or any constraints beyond what the schema already documents.
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, front-loaded sentence that states the action, resource, and backend wrapper with no filler. It is appropriately sized for a simple list tool.
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 combination of the description and the fully documented schema is sufficient to invoke the tool, but the definition is thin for tool selection: it does not explain how the returned names relate to the sibling access-level tools or what the output looks like. Given no output schema and no annotations, a bit more context would make it complete.
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 both optional parameters (PARTITIONKEY, STARTFROMNAME) have meaningful descriptions, so the description does not need to repeat them. The tool description adds no additional parameter-level context, so the schema baseline of 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 clearly identifies the operation ('Lists access level names') and the resource scope ('configured on the NetBox system'), and the wrapper reference adds endpoint precision. It does not explicitly differentiate itself from siblings like get_access_levels or get_access_level, though the word 'names' implies the distinction.
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?
There is no guidance about when to call this tool versus get_access_levels, get_access_level, or get_access_level_group(s). The description only states what it does; it never provides conditions, exclusions, or alternative suggestions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_access_levelsB
Lists access levels configured on the NetBox system (wraps NBAPI GetAccessLevels).
| Name | Required | Description | Default |
|---|---|---|---|
| WANTKEY | No | Optional. Per NBAPI GetAccessLevels. | |
| PARTITIONKEY | No | Optional. Per NBAPI GetAccessLevels — only "0" is documented as allowed. | |
| STARTFROMKEY | No | Optional. Pagination cursor (key) to continue listing from a previous call. | |
| STARTFROMNAME | No | Optional. Pagination cursor (name) to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It states the tool 'lists' access levels, implying a read-only operation, but it does not describe pagination behavior (despite STARTFROMKEY/STARTFROMNAME parameters), response format, or any side effects or requirements. An agent cannot infer how the pagination cursors work or what the output looks like.
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, front-loaded sentence with no redundant phrasing. It is concise, though it sacrifices useful behavioral and usage context. For conciseness alone it earns a 4.
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?
There is no output schema, so the description must explain what the tool returns and how the pagination parameters work. It merely says 'lists access levels' without detailing the return structure or how to use STARTFROMKEY/STARTFROMNAME to iterate through pages. For a listing tool with multiple optional parameters, this is insufficient for an agent to call it correctly beyond trivial cases.
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?
The input schema has 100% coverage, with each parameter carrying a description (e.g., 'Pagination cursor (key) to continue listing from a previous call'). The tool description itself adds no additional parameter meaning beyond what the schema provides, so it meets the baseline 3 for high schema coverage.
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 uses a specific verb 'Lists' and identifies the resource 'access levels configured on the NetBox system', clearly distinguishing it from sibling tools like get_access_level (singular) or get_access_level_name. The mention of wrapping NBAPI GetAccessLevels further anchors its function.
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 purpose implies this tool is for enumerating access levels, but it does not explicitly state when to use it versus alternatives such as get_access_level for a single record or get_access_level_names for just names. No exclusions or direct comparisons are provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_alarmsA
Returns alarm information from the NetBox system, optionally filtered by partition, alarm ID, event ID, activity ID or owner (wraps NBAPI GetAlarms). Responds with an ALARMS/ALARM collection; a system with no active alarms answers with an empty collection or a documented NOT FOUND.
| Name | Required | Description | Default |
|---|---|---|---|
| ID | No | Optional. Return the alarm with this alarm ID. | |
| EVENTID | No | Optional. Return alarms raised by this event ID. | |
| OWNERID | No | Optional. Return alarms owned by this operator/person ID. | |
| ACTIVITYID | No | Optional. Return alarms for this activity ID. | |
| PARTITIONKEY | No | Optional. Return alarms for this partition key only. | |
| ALLPARTITIONS | No | Optional. "TRUE" to return alarms across all partitions. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses the response format (ALARMS/ALARM collection) and the behavior for no active alarms (empty collection or NOT FOUND), which is valuable. However, it doesn't mention authentication, permissions, or potential side effects, though as a read-only get operation this is less critical. The disclosure is sufficient for a basic understanding.
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 concise, with two sentences that front-load the primary purpose and then add response behavior. No wasted words; every sentence contributes useful 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?
The tool has 6 optional parameters and no output schema. The description gives a high-level response type and handles the empty case, which is helpful. However, it doesn't describe the alarm object structure or any pagination, but for a read-only retrieval with optional filters, this is adequate. It's complete enough for an agent to understand what it returns and when to call it.
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 each parameter already has a description. The tool description adds a natural-language summary of the filter options (partition, alarm ID, event ID, activity ID or owner), but does not provide additional semantics beyond the schema. It meets the baseline but adds little extra value.
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 clearly states the tool returns alarm information from the NetBox system, with a specific set of optional filters (partition, alarm ID, event ID, activity ID or owner). It distinguishes itself from other get_* tools by naming 'alarm information' and the wrapped NBAPI GetAlarms, making its purpose unambiguous.
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 description does not explicitly state when to use this tool versus alternatives or when not to use it. While it's implied that this is the tool for retrieving alarms, there is no guidance on choosing it over related tools like get_event_history or list_events, nor any exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_card_access_detailsA
Returns card/credential access details for a given card (wraps NBAPI GetCardAccessDetails). Identifies the card by ENCODEDNUM + CARDFORMAT, not PERSONID — GetCardAccessDetails has no PERSONID parameter. RESOLVEDESCRIPTIONS defaults to true — an inverted, opt-out default (unlike most optional booleans in this codebase): each returned ACCESS record is enriched with the reader's human-readable READERDESCRIPTION via one GetReaders full-table fetch per call (not per record); set RESOLVEDESCRIPTIONS: false to skip it. Set RESOLVENAMES: true to enrich the response with the card owner's FIRSTNAME/LASTNAME/FULLNAME/NOTES (default false — off) via a single GetPerson lookup for the card's one PERSONID — cheaper than get_access_history's RESOLVENAMES, which pays one GetPerson call per distinct person across many records, since a card has exactly one owner. The four fields land on the top level of the response, alongside PERSONID/DISABLED/EXPDATE, not on each ACCESS record.
| Name | Required | Description | Default |
|---|---|---|---|
| CARDFORMAT | Yes | Required. The card format of ENCODEDNUM. | |
| ENCODEDNUM | Yes | Required. The encoded card number whose access details should be retrieved. | |
| MAXRECORDS | No | Optional. Maximum number of access records to return. | |
| OLDESTDTTM | No | Optional. Oldest date/time to include in the returned access records. | |
| RESOLVENAMES | No | Optional (default false). Enrich the top level of the response with the card owner's FIRSTNAME/LASTNAME/FULLNAME/NOTES via a single GetPerson lookup for the response's top-level PERSONID (one lookup per call, not one per ACCESS record — a card has exactly one owner). | |
| RESOLVEDESCRIPTIONS | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Enrich each returned ACCESS record with the reader's human-readable READERDESCRIPTION via one GetReaders full-table fetch per call (not per record). Set to false to skip it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses several non-obvious behaviors: the inverted default of RESOLVEDESCRIPTIONS (true instead of false), the performance cost of a GetReaders full-table fetch per call, and the single GetPerson lookup for RESOLVENAMES. It also explains where enriched fields land (top-level vs per-record). This is a highly transparent description.
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 every sentence earns its place. It is front-loaded with the core purpose, then logically proceeds through identification, defaults, cost trade-offs, and response placement. Despite its length, there is no filler—every clause conveys essential information about behavior, defaults, or alternatives.
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 a complex tool with 6 parameters, no output schema, and no annotations, the description covers all critical aspects: purpose, identification method, parameter defaults, performance cost, response structure, and comparison with a sibling. 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 100%, so the schema already describes each parameter. However, the description adds significant extra meaning: for RESOLVEDESCRIPTIONS it explains the inverted default and the exact performance impact; for RESOLVENAMES it clarifies the single-lookup efficiency and differentiates it from get_access_history. These details directly enrich the agent's understanding beyond 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 clearly states the tool's purpose: 'Returns card/credential access details for a given card'. It specifies the identifying parameters (ENCODEDNUM + CARDFORMAT) and explicitly distinguishes from siblings by noting that it does not use PERSONID. This leaves no ambiguity about what the tool does and how it differs from similar tools like get_access_history.
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 description explicitly states when to use it (when you have ENCODEDNUM and CARDFORMAT) and when not (it has no PERSONID parameter). It also compares with get_access_history on RESOLVENAMES cost, providing a clear basis for choosing between the two. This is explicit usage guidance with alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_card_formatsB
Returns the card formats configured on the NetBox system (wraps NBAPI GetCardFormats). No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It states 'Returns' which implies a read operation, but it does not explicitly declare it as read-only, safe, or free of side effects. It also does not disclose authentication requirements, error behavior, or any limitations. The mention of wrapping NBAPI GetCardFormats adds implementation detail but not behavioral transparency.
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 two short sentences, front-loaded with the purpose and immediately noting the absence of parameters. Every word earns its place; there is no fluff or redundancy. The structure is efficient and easy to scan.
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 tool with no annotations, no output schema, and no parameters, the description is the only source of context. It fails to describe the return format, data shape, or potential failure modes. An agent calling this tool would know it returns card formats but not what the response looks like or how to interpret it. This is a significant gap for a tool that could return a list or object with various fields.
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?
The tool has zero parameters, and the schema confirms this with an empty properties object. The description explicitly says 'No parameters required', which is clear and sufficient. The baseline for 0 parameters is 4, and the description meets that baseline by reinforcing the absence of inputs.
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 clearly states the verb 'Returns' and the resource 'card formats configured on the NetBox system', which is specific and unambiguous. It also mentions the underlying NBAPI call, adding implementation context. However, it does not explicitly differentiate from the many sibling get_* tools, though the resource name itself is distinctive.
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 description provides no guidance on when to use this tool versus the many sibling getters. It only states that no parameters are required, which is about invocation, not selection. There is no mention of alternatives, prerequisites, or scenarios where this tool is preferred or discouraged.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_daily_unlock_windowA
Reports the managed daily recurring unlock window, if any: the managed portal group "MCP Daily Unlock Window" (key, portals, its unlock time spec group and whether that is the managed time spec group "MCP Daily Unlock Window time specs"), that group and its members (read from paginated GetTimeSpecGroups), the one managed time spec and holiday, the window derived from them, and whether it is active right now on the host clock. Read-only: issues only Get commands. No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It explicitly discloses that the tool issues only Get commands (read-only), reads from paginated GetTimeSpecGroups, derives a window, and checks whether it is active on the host clock. This goes beyond a simple 'reports the window' statement and gives useful behavioral 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 a single, information-dense sentence that front-loads the main purpose and then lists the included details. It is somewhat long but every clause adds meaningful detail about scope, source, and read-only behavior, so it earns its place.
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 zero-parameter read-only reporting tool, the description is quite complete: it names the managed group, the source commands, the derived window, and the active-now check. It does not describe the output format, but with no output schema and a complex report, a bit more detail on the return shape would help; still, the core calling context is fully covered.
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?
There are zero parameters, so the schema already fully defines the input surface. The description adds value by explaining what the tool reports and how it derives the result, which is more than the empty schema provides. Baseline 4 for zero params 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 ('Reports') and a precise resource ('managed daily recurring unlock window'), and enumerates exactly what is included. It distinguishes itself from the sibling get_unlock_window by specifying the managed 'MCP Daily Unlock Window' group and the derived window, so an agent can tell them apart.
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 description clearly implies this is a read-only reporting tool for the managed daily unlock window and notes it reads from paginated GetTimeSpecGroups. It does not explicitly name alternatives or when-not-to-use, but the detailed scope and read-only note give clear context for when to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_elevatorsA
Lists elevators configured on the NetBox system (wraps NBAPI GetElevators).
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It clearly signals a read-only listing operation ('Lists') and names the wrapped API, but it does not describe pagination behavior, output shape, or failure modes. This is adequate for a simple non-destructive list operation, but not detailed.
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, front-loaded sentence that states the action and resource without filler. The parenthetical endpoint context is useful and non-redundant.
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 simple list tool with one optional pagination parameter and no output schema, the description plus schema are sufficient to call it correctly. It names the resource, the wrapped endpoint, and the only parameter; enumerating return fields would be helpful but is not necessary for 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?
The schema provides 100% coverage for the only parameter, STARTFROMKEY, with a clear description ('Optional. Pagination cursor to continue listing from a previous call.'). The tool description adds no parameter-specific meaning, so the baseline 3 applies.
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 uses a specific verb ('Lists') and a specific resource ('elevators configured on the NetBox system'), and it identifies the wrapped endpoint ('NBAPI GetElevators'). This clearly distinguishes it from sibling get_* tools.
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 description implies usage by naming the resource and action, so an agent can infer it is the tool for listing elevators. However, it gives no explicit guidance on when to use this tool versus alternatives such as get_readers or get_floors.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_event_historyB
Returns historical NetBox events for an optional event name/date range (wraps NBAPI GetEventHistory).
| Name | Required | Description | Default |
|---|---|---|---|
| ENDDTTM | No | Optional. End of the date/time range to query (NBAPI-documented format). | |
| NEXTKEY | No | Optional. Pagination continuation cursor from a previous call. | |
| EVENTNAME | No | Optional. Restrict results to this event name. | |
| STARTDTTM | No | Optional. Start of the date/time range to query (NBAPI-documented format). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It only states that it 'wraps NBAPI GetEventHistory' and returns historical events, but does not mention that it is a read-only operation, how pagination works (NEXTKEY), or what the response format is. This is minimal transparency for a tool that could have side effects or require specific handling.
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, focused sentence that immediately states the tool's purpose and wraps the underlying API. It is efficient with no filler, front-loading the core action and optional filters. This is exemplary conciseness.
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 that there is no output schema and no annotations, the description should explain the return format, pagination behavior, and any limitations. It only vaguely states that it returns historical events, without describing the structure of the results, how NEXTKEY is used, or whether the operation is read-only. For a tool with four optional parameters and no other structured documentation, this is incomplete and leaves the agent guessing about expected output.
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% (all four parameters have descriptions in the schema). The description adds little beyond what the schema already provides, merely restating that event name and date range are optional. The NEXTKEY pagination parameter is not mentioned in the description, but the schema documents it. Thus, the description provides marginal additional value, warranting a baseline 3.
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 clearly states that the tool returns historical NetBox events and mentions the optional event name/date range filters. It also identifies the underlying API it wraps. While it distinguishes itself by focusing on historical events, it does not explicitly contrast with sibling tools like list_events, which might offer a different scope.
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 description implies usage for retrieving historical event data by stating 'Returns historical NetBox events' and mentions optional filters. However, it does not provide explicit guidance on when to use this tool over alternatives (e.g., list_events) or any exclusions, leaving the agent to infer the appropriate context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_floorsA
Lists floors configured on the NetBox system (wraps NBAPI GetFloors).
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. 'Lists' clearly signals a read-only operation, and 'wraps NBAPI GetFloors' gives implementation context. However, it does not mention pagination behavior, return format, or potential performance concerns, though the STARTFROMKEY parameter in the schema partially addresses pagination.
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 focused sentence that front-loads the core action and resource. It contains no filler or redundant explanation, making it easy for an agent to parse quickly.
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 low-complexity list tool with one optional parameter and no output schema, the description plus the parameter schema is largely sufficient. It could mention pagination explicitly in the prose, but the schema already documents that behavior, so the main gap is minor.
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?
The schema fully describes the only parameter, STARTFROMKEY, as an optional pagination cursor, so the schema carries the semantic weight. The description adds no parameter-level detail, but with 100% schema coverage, the baseline of 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 uses a specific verb ('Lists') with a clear resource ('floors') and scope ('configured on the NetBox system'), so an agent can understand the tool's function immediately. It doesn't explicitly distinguish from sibling tools, but no sibling tool appears to target floors, so the differentiation need is low.
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 description implies the tool should be used when the agent needs to list floors, but it gives no explicit guidance about when to prefer this tool over alternatives or when not to use it. There are no exclusions or alternative tool references, but the purpose is straightforward enough that the usage is reasonably inferable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_guideA
Returns static reference guidance on this server’s S2 NetBox domain knowledge. Makes no controller call. Call with no topic for an index of all six topics; call with topic set to one of access-model, unlock-windows, group-and-name-gotchas, credentials-and-card-formats, api-quirks, write-safety for that topic’s full content.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | Optional. One of: access-model, unlock-windows, group-and-name-gotchas, credentials-and-card-formats, api-quirks, write-safety. Omitted or unrecognized returns the index instead. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility for behavioral disclosure. It clearly states the tool is static, makes no controller call, and returns an index for unrecognized topics. This goes beyond a simple 'returns guidance' claim and covers the key behavioral traits an agent needs to know.
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 sentences deliver purpose, behavioral transparency, and usage instructions with zero wasted words. The no-controller-call trait is front-loaded, and the invocation instructions are compact and complete. Every clause earns its place.
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 single-optional-parameter static reference tool with no output schema, the description covers all invocation paths (with and without topic, unrecognized topic) and the static behavior. Minor gaps remain around the structure of the returned content, but the tool is self-describing reference material, so this is acceptable.
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 fully documents the topic parameter and its allowed values. The description adds a small amount of value beyond the schema by explaining the behavior for omitted/unrecognized topics ('returns the index instead'), which mirrors the schema but in context. This meets the baseline for covered parameters.
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 opens with a specific verb and resource: 'Returns static reference guidance on this server's S2 NetBox domain knowledge.' It also states 'Makes no controller call,' which clearly distinguishes it from sibling get_* tools that fetch live data. Enumerating the six topics makes the tool's scope immediately obvious.
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 usage guidance is provided: call with no topic for an index, or with a specific topic for full content. It also states that an unrecognized topic returns the index instead. It does not name a sibling alternative or an explicit when-not-to-use condition, but the domain-guidance purpose is distinct enough that this is a minor gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_holidayA
Returns the details of a single holiday for a given HOLIDAYKEY (wraps NBAPI GetHoliday).
| Name | Required | Description | Default |
|---|---|---|---|
| HOLIDAYKEY | Yes | Required. The unique HOLIDAYKEY of the holiday to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure. It states the operation is a retrieval ('returns'), which implies a read, and notes it wraps NBAPI GetHoliday. However, it does not describe behavior for an invalid or missing HOLIDAYKEY or the response structure.
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 front-loaded sentence communicates the tool's purpose, scope, and key parameter without wasted words. The parenthetical wrapper note is short and adds context.
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 single-parameter getter, the description is sufficient for an agent to select and invoke it correctly. The only gaps are the lack of an explicit not-found behavior and return format, but neither blocks correct use for the common case.
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 both the schema and description convey that HOLIDAYKEY is required and unique. The description adds no additional format, source, or usage detail beyond the schema, so the baseline of 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, resource, and selection criterion: 'returns the details of a single holiday' for a HOLIDAYKEY. This clearly distinguishes it from the sibling get_holidays, which presumably returns multiple holidays.
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 clear this is the tool to use when you have a specific HOLIDAYKEY and need one holiday. It does not explicitly name get_holidays as the alternative for listing many holidays, so some inference is required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_holidaysA
Lists holiday keys configured on the NetBox system (wraps NBAPI GetHolidays). Returns a comma-separated key string, not a list of records — use get_holiday per key for details. GetHolidays has no documented calling parameters (no pagination).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses that the tool returns a comma-separated key string rather than a list of records, that it wraps NBAPI GetHolidays, and that there is no pagination. These are specific behavioral traits that go beyond the schema and significantly aid the agent in predicting output and limitations.
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 two sentences with no filler. It front-loads the purpose, then provides the return format, the alternative tool, and the parameter note. Every clause earns its place, making it highly 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?
Given the simplicity (no parameters, no output schema), the description covers everything an agent needs: what it returns, how it differs from get_holiday, and its calling constraints. Nothing critical 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?
The tool has zero parameters and the schema coverage is 100% (empty schema). The baseline for zero-parameter tools is 4, and the description adds a note that there are no documented calling parameters, which reinforces the schema. No further parameter explanation is needed.
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 clearly states the verb 'Lists' and the resource 'holiday keys configured on the NetBox system', and immediately distinguishes itself from the sibling get_holiday by noting the return format and that get_holiday is for details. This makes its purpose unmistakable.
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 directs the agent to use get_holiday per key for details, which is a clear alternative for a different use case. It also notes the lack of parameters and pagination, which informs when to call this tool. However, it does not explicitly state 'use this when you need all keys' or list exclusions, so it falls short of a perfect 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_locationsA
Lists the locations configured on the NetBox system (wraps NBAPI GetLocations). Each record carries LOCATIONKEY, PARTITIONKEY and a nested PARENTLOCATION { PARENTKEY, NAME } block, so the result describes a location tree. LOCATIONKEY values feed get_portal_statuses and set_threat_level.
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. (Not in the guide's Calling Parameters list, but its documented FAIL messages include "Invalid STARTFROMKEY".) | |
| ALLPARTITIONS | No | Optional. "TRUE" to list locations across all partitions. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full burden. It adds useful behavioral context by detailing the nested PARENTLOCATION structure and downstream usage, but it does not disclose permissions, rate limits, pagination behavior, or explicitly state read-only status (though 'Lists' implies it).
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 sentences, each earning its place: the first states purpose, the second describes output shape, and the third explains downstream utility. No redundancy or 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?
For a read-only listing tool with two optional parameters and no output schema, the description sufficiently explains output structure and workflow fit. It could briefly mention pagination or ALLPARTITIONS behavior, but the schema already provides parameter semantics, so the description is adequately complete.
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 baseline is 3. The description adds no parameter-specific meaning; STARTFROMKEY and ALLPARTITIONS are already fully documented in the schema, and the description does not clarify their effects or usage context.
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: 'Lists the locations configured on the NetBox system.' It also describes the output record structure and names downstream consumers of LOCATIONKEY, making it distinct from the many sibling get_* tools by both name and behavior.
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?
Provides clear context that the result is a location tree and that LOCATIONKEY values are used by get_portal_statuses and set_threat_level, implying when an agent would need this tool. However, it does not explicitly exclude alternatives or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_mercury_panelA
Returns the details of a single Mercury panel for a given MERCURYKEY (wraps NBAPI GetMercuryPanel).
| Name | Required | Description | Default |
|---|---|---|---|
| MERCURYKEY | Yes | Required. The key of the Mercury panel to retrieve. Use get_mercury_panels to discover keys. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden. 'Returns' implies a read-only operation, but the description does not disclose error behavior, authentication needs, or what happens for an invalid key. This is acceptable for a simple getter but not richly transparent.
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 one compact sentence that states the action, the resource, the input, and the underlying API wrapper. There is no filler or redundancy, and the most important information is 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?
For a single-parameter, no-output-schema, no-annotation tool, the definition provides the essential information: what it returns, what input is needed, and how to find that input via a sibling tool. It does not describe the response shape or edge-case behavior, but the tool is simple enough that this is a minor gap.
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 MERCURYKEY parameter is already well documented in the schema as required and as the key to retrieve. The main description only restates 'for a given MERCURYKEY' and adds no additional parameter meaning beyond the schema, 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 uses a specific verb ('Returns') and specific resource ('details of a single Mercury panel'), and the word 'single' clearly distinguishes it from the plural sibling get_mercury_panels. Mentioning that it wraps NBAPI GetMercuryPanel adds useful provenance.
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 description makes it clear this tool is for retrieving one panel by MERCURYKEY, and the parameter description explicitly tells the agent to use get_mercury_panels to discover keys. It lacks an explicit when-not-to-use statement or comparison to other single-resource getters, but the intended usage is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_mercury_panelsA
Lists the Mercury panels configured on the NetBox system, optionally filtered (wraps NBAPI GetMercuryPanels). Returns an empty collection (or a FAIL/"NOT FOUND") on a system with no Mercury hardware.
| Name | Required | Description | Default |
|---|---|---|---|
| NAME | No | Optional. Restrict the listing to Mercury panels with this name. | |
| MERCURYKEY | No | Optional. Restrict the listing to this Mercury panel key. | |
| PARTITIONKEY | No | Optional. Restrict the listing to this partition key. | |
| ALLPARTITIONS | No | Optional. "TRUE" to list Mercury panels across all partitions. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral disclosure burden. It compensates by stating the listing behavior, optional filtering, and the failure mode ('empty collection' or FAIL/NOT FOUND) when no Mercury hardware exists. It does not discuss permissions or pagination, but those are less critical for a simple read-oriented list 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?
The description is two sentences with no filler, front-loads the core operation, and includes the useful NBAPI wrapper context. Every clause contributes to the agent's understanding.
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 description is largely complete for a simple filtered list tool: it names the resource, supports the schema's optional filters, and explains the no-hardware outcome. Since there is no output schema, a success-response shape is not described, but the absence of required parameters reduces invocation risk.
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 all four optional parameters already have meaningful descriptions. The prose adds only the collective 'optionally filtered' framing, which is helpful but does not deepen the meaning of any individual parameter beyond 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 clearly identifies a collection-listing operation with an explicit resource ('Mercury panels'), a system context ('configured on the NetBox system'), and optional filtering. It does not explicitly name or contrast a sibling such as get_mercury_panel, though the plural 'panels' helps distinguish it from the singular sibling.
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 description states what the tool does but provides no guidance on when to choose it over alternatives like get_mercury_panel, nor any exclusions or prerequisites. The reader must infer its appropriate use from the purpose alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_network_nodeA
Returns the details of a single network node for a given NODEKEY (wraps NBAPI GetNetworkNode).
| Name | Required | Description | Default |
|---|---|---|---|
| NODEKEY | Yes | Required. The key of the network node to retrieve. Use get_network_nodes to discover keys. | |
| PARTITIONKEY | No | Optional. Key of the partition the node belongs to. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility for disclosing behavior. It conveys that this is a read-style operation ('Returns'), scopes the result to a single node, and notes that it wraps NBAPI GetNetworkNode. It does not disclose error behavior, auth requirements, or what happens for invalid keys, which keeps this at a minimum viable level rather than higher.
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 front-loaded sentence with no filler. It states the action, the resource, the required input, and the wrapped API in a compact and scannable form.
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 simple single-node getter, the description plus schema provides enough to select and invoke the tool: required key, optional partition, and a discovery route to get_network_nodes. There is no output schema, so a bit more detail about the returned fields could help, but it is not necessary 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 description coverage is 100%, so the baseline is 3. Both NODEKEY and PARTITIONKEY already have meaningful schema descriptions, including the pointer to get_network_nodes for discovering keys. The main description adds little beyond naming NODEKEY, but the schema already carries the parameter meaning.
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 uses a specific verb ('Returns') with a concrete resource ('details of a single network node') and the identifying input ('given NODEKEY'). It clearly distinguishes itself from the sibling get_network_nodes by emphasizing 'single' node retrieval rather than listing.
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 description makes the retrieval context clear: use this when you have a NODEKEY and want one node's details. The NODEKEY parameter description additionally points to get_network_nodes for discovering keys, which is a useful routing hint. It does not explicitly state when not to use the tool, but the singular/plural contrast with the sibling is reasonably clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_network_nodesC
Lists the network nodes configured on the NetBox system, optionally filtered (wraps NBAPI GetNetworkNodes).
| Name | Required | Description | Default |
|---|---|---|---|
| NAME | No | Optional. Restrict the listing to network nodes with this name. | |
| NODEKEY | No | Optional. Restrict the listing to this network node key. | |
| PARTITIONKEY | No | Optional. Restrict the listing to this partition key. | |
| ALLPARTITIONS | No | Optional. "TRUE" to list network nodes across all partitions. | |
| UNIQUEIDENTIFIER | No | Optional. Restrict the listing to the node with this 16-hex-digit unique identifier. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It only states that the tool lists and optionally filters network nodes, and that it wraps NBAPI GetNetworkNodes. There is no mention of output shape, pagination, filter matching semantics, interaction between filters, or potential error conditions.
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 short and front-loaded with the purposeful verb and resource. The parenthetical "wraps NBAPI GetNetworkNodes" is somewhat redundant and not especially helpful to an agent, but it does not seriously hurt clarity.
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 read-only listing tool with no required parameters and full schema coverage, the description plus schema is minimally viable for invoking the tool. However, no output schema exists and the description does not clarify whether filters are combined, how ALLPARTITIONS relates to PARTITIONKEY, or what the returned list contains, leaving moderate context gaps.
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 all five parameters are already documented in the input schema. The description's phrase "optionally filtered" adds general framing but no concrete meaning beyond the schema, so it receives the baseline score.
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 uses a specific verb and resource: "Lists the network nodes configured on the NetBox system," which clearly identifies the tool's purpose. It does not, however, explicitly distinguish this list operation from the sibling get_network_node, though the plural naming reduces ambiguity.
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 description provides no guidance about when to use this tool versus alternatives such as get_network_node. It mentions optional filtering but does not explain when filters should be applied or when a caller would want the list versus a single node lookup.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_outputsB
Lists auxiliary outputs configured on the NetBox system (wraps NBAPI GetOutputs).
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are absent, so the description carries full responsibility for behavioral disclosure. It only says 'lists auxiliary outputs', which is a read operation, but does not mention safety implications (e.g., read-only nature, no side effects), pagination behavior beyond the parameter, or any potential errors. For a list operation, this is a minor gap, but given the lack of annotations, the description should have at least indicated it is non-destructive and returns a list. The reference to 'wraps NBAPI GetOutputs' adds some context but does not fully disclose behavior.
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, concise sentence that states the purpose and references the underlying API. It is appropriately sized and front-loaded with the core action. No wasted words.
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 the tool's simplicity (one optional parameter, no output schema, no nested objects), the description plus the schema is nearly complete. However, the lack of any behavioral transparency (e.g., that the operation is read-only and returns a list of outputs) and absence of usage guidance leave minor gaps. With annotations absent, a bit more context would be expected, but for a simple list tool, the current state is adequate.
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?
The schema description coverage is 100%, with the single parameter STARTFROMKEY fully described as a pagination cursor. The description does not add further meaning beyond that, so the baseline of 3 applies because the schema already provides sufficient semantics. The description's reference to 'auxiliary outputs' does not directly explain the parameter, but schema coverage meets the burden.
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 clearly states the tool lists auxiliary outputs on the NetBox system, a specific verb-resource combination. It also references the wrapped NBAPI endpoint, which helps identify the underlying operation. However, it does not explicitly differentiate from siblings beyond indicating 'auxiliary outputs' as a distinct resource, which is sufficient given the naming.
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 description does not explicitly state when to use this tool versus alternatives, but the resource name 'auxiliary outputs' is specific enough that an agent can infer it is for that resource only. It does not mention any context like pagination handling, but the single parameter's schema covers that. No exclusions or alternatives are given, so it's adequate but not explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_partitionsA
Lists partitions configured on the NetBox system (wraps NBAPI GetPartitions). No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of behavioral disclosure. 'Lists' implies a read-only operation, and the wrapper reference adds context, but the description does not mention return format, pagination, error behavior, or access requirements. It is adequate but not rich.
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 concise sentence that leads with the core action and resource, then adds the wrapper context and parameter note. Every word earns its place.
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 no-argument list tool, the description is nearly complete: it states what is listed and that no parameters are needed. It does not describe the output shape, but the absence of an output schema and the low complexity make this a minor gap.
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?
There are zero parameters, so the baseline is 4. The description explicitly states 'No parameters required,' which reinforces the empty input schema even though the schema already communicates this.
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 uses a specific verb ('Lists') and a clear resource ('partitions configured on the NetBox system'), and it also names the wrapped API ('NBAPI GetPartitions'). This makes the tool's purpose unambiguous and distinct from the sibling get_* tools.
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 description implies usage through its clear purpose, but it does not explicitly state when to use this tool versus alternatives or when not to use it. Mentioning that no parameters are required helps, but there is no explicit routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_personA
Returns the full person record for a given PERSONID (wraps NBAPI GetPerson).
| Name | Required | Description | Default |
|---|---|---|---|
| PERSONID | Yes | Required. The unique PERSONID of the person record to retrieve. | |
| ALLPARTITIONS | No | Optional. Per NBAPI GetPerson. | |
| WANTCREDENTIALID | No | Optional. Include CREDENTIALID values on returned access cards. | |
| ACCESSLEVELDETAILS | No | Optional. Include full access level details in the response. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
'Returns' makes the read-only nature reasonably clear, and 'wraps NBAPI GetPerson' adds a useful reference. However, with no annotations provided, the description carries the full behavioral burden and does not mention error behavior, authentication needs, or response format.
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?
One sentence with no filler; the core behavior and API wrapper are stated upfront. It earns its place entirely.
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 get-by-ID tool with a complete parameter schema, this is adequate but minimal. It omits usage alternatives, return-shape expectations, and any edge cases, which matters more since there is no output schema and no annotations.
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 all four parameters are already documented in the schema. The description adds little beyond naming PERSONID and calling the result 'full,' which is acceptable but not additive.
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 uses a specific verb ('Returns') and resource ('full person record') keyed by PERSONID, so an agent can tell this is a direct lookup tool. It does not explicitly differentiate it from sibling search_person_data, though the PERSONID-based lookup strongly implies it.
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 usage context is only implied: an agent would call this when it has a PERSONID and needs the full person record. It provides no explicit when-not guidance or comparison to alternatives like search_person_data.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_pictureA
Returns a person's photo ID image (wraps NBAPI GetPicture). The response's PICTURE field is a Base64-encoded JPEG and may be very large — a full-size badge photo runs to hundreds of kilobytes of Base64 text, so prefer PICTUREURL when you only need to identify the file. The response also carries PERSONID, PICTUREURL, LASTNAME, FIRSTNAME and LASTMOD. The payload is passed through unmodified: this tool neither decodes, resizes nor truncates the Base64 string. A person with no photo answers FAIL with "No picture URL for this person ID" or "Picture file does not exist"; an oversized image answers "Person picture image file size exceeds maximum to be returned/exported".
| Name | Required | Description | Default |
|---|---|---|---|
| PERSONID | Yes | Required. The PERSONID of the person whose picture should be retrieved. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it excels: it discloses that the payload is passed through unmodified (no decode/resize/truncate), warns about the potentially very large Base64 string, and enumerates the exact error messages for missing or oversized images. It also lists all response fields, giving the agent a complete behavioral picture.
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?
Though longer than average, every sentence earns its place: the core purpose is front-loaded, followed by a critical size caveat, response fields, pass-through behavior, and error cases. The structure is logical and skimmable, with no redundancy or 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?
For a single-parameter tool with no output schema and no annotations, this description is exhaustive. It covers the return format, the size concern, the unmodified pass-through, and the exact error conditions. An agent has everything needed to call the tool correctly and handle the response, with no gaps.
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% — the only parameter, PERSONID, is already documented as required and described. The description adds no new parameter-specific meaning beyond what the schema provides; it mentions PERSONID in the response fields but not in a way that clarifies the input. Baseline 3 is appropriate because the schema already handles parameter semantics fully.
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 opens with a specific verb and resource: 'Returns a person's photo ID image', and names the underlying API it wraps (NBAPI GetPicture). It clearly distinguishes this from sibling tools like get_person, which returns person data rather than the image. The additional detail on response fields and error cases leaves no ambiguity about the tool's purpose.
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 advises preferring PICTUREURL when only file identification is needed, citing the large Base64 size, which is a concrete when-to-use alternative. Though no sibling tool serves the same function, the guidance about which response field to use is a clear usage directive. It also implies when to expect errors, adding context for the agent's decision-making.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_portal_groupA
Returns the details of a single portal group for a given PORTALGROUPKEY (wraps NBAPI GetPortalGroup). RESOLVEGROUPNAMES defaults to true — an inverted, opt-out default (unlike most optional booleans in this codebase): the raw response carries only a bare UNLOCKTIMESPECGROUPKEY foreign key, so this resolves it into a new sibling UNLOCKTIMESPECGROUPNAME field via one full-table GetTimeSpecGroups fetch per call — a fixed cost regardless of anything else, since a single portal group carries exactly one UNLOCKTIMESPECGROUPKEY. THREATLEVELGROUPKEY is never resolved (no NBAPI read command exists for threat level groups). Set RESOLVEGROUPNAMES: false to skip the fetch and return the response exactly as GetPortalGroup provides it.
| Name | Required | Description | Default |
|---|---|---|---|
| PORTALGROUPKEY | Yes | Required. The unique PORTALGROUPKEY of the portal group to retrieve. | |
| RESOLVEGROUPNAMES | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Resolves UNLOCKTIMESPECGROUPKEY into a new UNLOCKTIMESPECGROUPNAME sibling field via one full-table GetTimeSpecGroups fetch per call (skipped when UNLOCKTIMESPECGROUPKEY is empty/absent, yielding '' for the name). THREATLEVELGROUPKEY is never resolved — no NBAPI read command exists for threat level groups. Set to false to skip the fetch and return the response exactly as GetPortalGroup provides it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and discharges it exceptionally: it discloses the inverted opt-out default, the per-call full-table GetTimeSpecGroups fetch cost and why it is fixed, the empty-key behavior (yielding ''), that THREATLEVELGROUPKEY is never resolved and why, and the exact effect of setting RESOLVEGROUPNAMES to false. This is model-level transparency about side effects and costs.
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 and longer than typical, but every clause carries information: the inverted default is surprising, the cost implication is decision-relevant, and the threat-level caveat prevents a natural false assumption. Purpose is front-loaded. It could be lightly trimmed, but there is no fluff or redundancy.
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 2-parameter, single-record getter with no output schema and no annotations, the description covers everything an agent needs: what is returned, the resolved vs raw response shapes, the cost condition, the non-resolvable field, and the escape hatch. Nothing needed for correct invocation 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% and the schema's own parameter descriptions are unusually rich, so the baseline is 3. The description adds genuinely new meaning beyond the schema: the 'inverted, opt-out default unlike most optional booleans in this codebase' framing and the 'fixed cost regardless of anything else' rationale. These explain why the default matters in practice rather than restating the mechanic.
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 opens with a specific verb-resource pair — 'Returns the details of a single portal group for a given PORTALGROUPKEY' — and the word 'single' cleanly distinguishes it from the plural sibling get_portal_groups. The NBAPI GetPortalGroup wrapper reference adds grounding. An agent can tell exactly what this does without opening the 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?
The description implies when to use it (retrieve one portal group by key, versus the plural sibling) and gives explicit parameter-level guidance: set RESOLVEGROUPNAMES to false when you want the raw GetPortalGroup response and want to skip the full-table fetch. It does not name alternative siblings or state explicit when-not-to-use conditions, but the context is clear enough that exclusions are inferable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_portal_groupsA
Lists portal groups configured on the NetBox system (wraps NBAPI GetPortalGroups). RESOLVEGROUPNAMES defaults to true — an inverted, opt-out default (unlike most optional booleans in this codebase): each returned group's UNLOCKTIMESPECGROUPKEY is a bare foreign key, so this resolves it into a new sibling UNLOCKTIMESPECGROUPNAME field on every group. Costs at most one full-table GetTimeSpecGroups fetch per call — not per group — built once and skipped entirely when every group on the page has an empty/absent UNLOCKTIMESPECGROUPKEY. THREATLEVELGROUPKEY is never resolved (no NBAPI read command exists for threat level groups). Set RESOLVEGROUPNAMES: false to skip the fetch and return groups exactly as GetPortalGroups provides them.
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. | |
| RESOLVEGROUPNAMES | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Resolves each group's UNLOCKTIMESPECGROUPKEY bare foreign key into a new sibling UNLOCKTIMESPECGROUPNAME field, via at most one full-table GetTimeSpecGroups fetch per call (not per group) — built once and skipped entirely when every group on the page has an empty/absent UNLOCKTIMESPECGROUPKEY, yielding '' for the name. THREATLEVELGROUPKEY is never resolved — no NBAPI read command exists for threat level groups. Set to false to skip the fetch and return groups exactly as GetPortalGroups provides them. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full behavioral burden, and it delivers: it reveals the inverted opt-out default for RESOLVEGROUPNAMES, the exact resolution behavior (new sibling UNLOCKTIMESPECGROUPNAME), the at-most-one-fetch cost, the skip condition, and the permanent non-resolution of THREATLEVELGROUPKEY. This goes far beyond a generic 'list' statement.
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?
Every sentence carries specific operational detail; the first sentence front-loads the purpose, then the description tightens around the non-obvious default and its consequences. No filler or redundant restatement.
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?
Despite lacking an output schema, the description explains the key output difference (added UNLOCKTIMESPECGROUPNAME) and the behavior when the flag is false, while STARTFROMKEY pagination is documented in the schema. Combined with the safety and performance notes, an agent has what it needs to call correctly.
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% (both parameters documented), so baseline is 3. The description adds crucial semantics for RESOLVEGROUPNAMES — inverted default, foreign-key resolution, cost model, and opt-out behavior — which materially helps an agent choose the right value.
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 the exact operation (lists portal groups) and resource (NetBox portal groups), plus the underlying NBAPI wrapper. This clearly separates it from siblings like get_portal_group (singular) and get_portals.
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 description does not explicitly compare against sibling tools such as get_portal_group or get_portals. Its use case is implied through 'Lists portal groups configured on the NetBox system,' but there is no when/when-not or alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_portalsA
Lists portals (doors) configured on the NetBox system, each with its nested readers (wraps NBAPI GetPortals, paginated via STARTFROMKEY/NEXTKEY — there is no single-portal filter). RESOLVEDESCRIPTIONS defaults to true — an inverted, opt-out default (unlike most optional booleans in this codebase): GetPortals never populates a nested reader's own DESCRIPTION field (only READERKEY/NAME/PORTALORDER), so this fills it in directly on each nested reader via one GetReaders full-table fetch per call (not per portal/reader). Set RESOLVEDESCRIPTIONS: false to return readers exactly as GetPortals provides them, with no DESCRIPTION field and no GetReaders call.
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor — the NEXTKEY from a previous call, to continue listing. | |
| RESOLVEDESCRIPTIONS | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Fills in each nested reader's own DESCRIPTION field (GetPortals leaves it unpopulated) via one GetReaders full-table fetch per call (not per portal/reader). Set to false to skip it and return readers exactly as GetPortals provides them. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure, and it does so excellently. It exposes pagination via STARTFROMKEY/NEXTKEY, the surprising inverted default of RESOLVEDESCRIPTIONS, the fact that GetPortals leaves DESCRIPTION unpopulated, and the extra GetReaders full-table fetch cost per call.
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 information-rich, front-loading the core purpose and pagination behavior. The second sentence packs several important caveats into one long clause; although everything earns its place, it could be structured slightly more cleanly.
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 no output schema and no annotations, the description covers the critical behaviors well: pagination, the single-portal limitation, the default behavior, and the extra fetch cost. It is slightly incomplete on the exact return shape for portal objects, though nested reader fields are partially described.
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 schema already documents both parameters, including the RESOLVEDESCRIPTIONS default and behavior. The tool description mostly restates this same information, so it adds little semantic value beyond what the schema provides.
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: 'Lists portals (doors) configured on the NetBox system, each with its nested readers.' It also distinguishes this list-style call from single-portal access by stating 'there is no single-portal filter,' which differentiates it from sibling tools like get_portal.
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 clear context for when to use the tool: for listing all portals with nested readers, with no single-portal filter available. It also gives explicit guidance on when to set RESOLVEDESCRIPTIONS to false. However, it does not explicitly name alternatives or state when to prefer find_portals or get_portal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_portal_statesA
Lists the portal states the NetBox system defines (wraps NBAPI GetPortalStates). Returns STATEKEY/STATENAME pairs — the vocabulary get_portal_statuses reports against, and the values its STATEKEY filter accepts.
| Name | Required | Description | Default |
|---|---|---|---|
| PORTALSTATES | No | Optional. "TRUE" returns portal states across all partitions; omitted, the controller returns states for the default or currently switched partition. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral transparency burden. It says the tool 'lists' states and wraps NBAPI GetPortalStates, and specifies the return shape. It does not mention authentication, side effects, or pagination, but for a simple list operation this is a reasonable but not fully comprehensive disclosure.
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 two sentences, front-loaded with the core purpose, and adds only relevant context about the wrapper and the relationship to get_portal_statuses. No filler or redundant 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?
For a tool with one optional parameter, no output schema, and no annotations, the description covers the main purpose, return value shape, and how it relates to a sibling tool. It is slightly light on behavioral details such as auth requirements, but the simplicity of the operation and the strong schema coverage keep it nearly complete.
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 single optional PORTALSTATES parameter has a clear enum description. The tool description does not add parameter-level detail, but it does explain the relationship between the returned STATEKEY values and get_portal_statuses, which is useful context beyond 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 clearly states the tool lists portal states, identifies the underlying API wrapper, and describes the output as STATEKEY/STATENAME pairs. It also distinguishes itself from the sibling get_portal_statuses by explaining that this tool provides the vocabulary used by that tool's filter.
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 description implies when to use this tool: when you need the set of valid portal states or the acceptable STATEKEY values for get_portal_statuses. It does not explicitly state when not to use it, but the connection to get_portal_statuses gives clear contextual guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_portal_statusesA
Returns the live status of portals (doors) — the current state of each door, not its configuration (wraps NBAPI GetPortalStatuses). Each PORTALSTATUS block carries PORTALKEY, PORTALNAME, STATEKEY, STATENAME, THREATLEVELNAME, LOCATIONKEY, LOCATIONNAME, TYPEKEY and PARTITIONKEY. Filter by portal, state, partition or location; use get_portal_states for the STATEKEY vocabulary and get_locations for LOCATIONKEY values.
| Name | Required | Description | Default |
|---|---|---|---|
| STATEKEY | No | Optional. Report only portals currently in this state (see get_portal_states). | |
| PORTALKEY | No | Optional. Report status for this portal only. | |
| LOCATIONKEY | No | Optional. Report status for portals at this location only (see get_locations). | |
| PARTITIONKEY | No | Optional. Report status for portals in this partition only. | |
| ALLPARTITIONS | No | Optional. "TRUE" to report portal status across all partitions. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It states this is a read operation ('Returns the live status'), which implies no mutation, and it discloses the underlying API wrapper (NBAPI GetPortalStatuses). It does not explicitly say 'read-only' or describe side effects, but the nature of a status query makes the safety profile clear. A score of 4 reflects strong implicit disclosure, with room for an explicit read-only note.
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 sentences, no filler. The primary purpose is front-loaded, the return fields are enumerated compactly, and the filtering guidance is efficient. Every clause earns its place.
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 no output schema, the description compensates by listing all fields in each PORTALSTATUS block. It covers the five optional parameters, explains filtering, and points to the relevant vocabulary tools. For a read-only status query with no required parameters, this is complete; 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 100% with each parameter already described. The description adds value by grouping the filters (portal, state, partition, location) and cross-referencing the vocabulary tools, which helps an agent understand how the parameters relate. It does not repeat parameter details but enhances their meaning by linking to context.
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 opens with a specific verb and resource: 'Returns the live status of portals (doors)', and immediately clarifies it is the current state, not configuration. It also lists the exact fields returned, making the output shape unambiguous. This distinguishes it from siblings like get_portals (configuration) and get_portal_states (vocabulary) without needing to inspect 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?
Explicitly states the filtering dimensions (portal, state, partition, location) and directs the agent to get_portal_states for STATEKEY vocabulary and get_locations for LOCATIONKEY values. This is clear 'when to use this tool vs alternatives' guidance, naming the exact sibling tools and the condition that selects them.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_readerA
Returns the details of a single reader for a given READERKEY (wraps NBAPI GetReader).
| Name | Required | Description | Default |
|---|---|---|---|
| READERKEY | Yes | Required. The unique READERKEY of the reader to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description carries the full behavioral disclosure burden. It does state the operation ('Returns the details') and identifies the underlying backend call ('wraps NBAPI GetReader'), which adds useful context, but it does not mention read-only behavior, error conditions, or output format.
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 well-structured sentence that front-loads the core action, target, and required input. There is no superfluous text or repetition of schema details.
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?
This is a simple one-parameter getter, and the description combined with the schema provides enough information to invoke the tool correctly. However, since there is no output schema, the description could be slightly more explicit about what fields or object shape are contained in the returned 'details'.
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 parameter READERKEY is already fully documented in the schema. The description repeats the parameter name but does not add any semantic detail beyond what the schema provides, keeping this at the baseline score of 3.
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 the specific action ('Returns the details') and the target resource ('a single reader') with the required identifier READERKEY. The singular 'single reader' clearly distinguishes this from the sibling get_readers tool without needing to inspect 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?
The description implies the usage context: you call this when you have a READERKEY and need one reader's details. However, it does not explicitly state when to prefer it over get_readers or any other sibling, nor does it mention alternative tools for reader-related queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reader_access_historyA
Returns a single reader's access (grant/deny) history for a given READERKEY, with each match's PERSONID enriched to a name (composite: wraps NBAPI GetAccessHistory + GetPerson). GetAccessHistory has no server-side reader filter, so this reads and filters client-side. Rather than a date range, it scans the most recent SCANWINDOW system-wide records (default 2000). RESOLVEDESCRIPTIONS defaults to true — an inverted, opt-out default like get_access_history's own RESOLVEDESCRIPTIONS: it attaches a single top-level READERDESCRIPTION field for the given READERKEY (not one per match — every match already shares this identical READERKEY by construction) via one GetReaders full-table fetch; set to false to omit it entirely.
| Name | Required | Description | Default |
|---|---|---|---|
| READERKEY | Yes | Required. Only access records for this reader are returned. | |
| MAXMATCHES | No | Optional. Maximum number of matches to include, in chronological order. Defaults to 100. | |
| SCANWINDOW | No | Optional. Number of most-recent system-wide access records to scan. Defaults to 2000. | |
| RESOLVEDESCRIPTIONS | No | Optional (default true — on by default). Attaches a single top-level READERDESCRIPTION field (the description for this call's own READERKEY, not one per match) via one GetReaders full-table fetch. Set to false to omit the field entirely. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden. It discloses the composite nature, the absence of a server-side reader filter, client-side filtering, SCANWINDOW semantics, the opt-out RESOLVEDESCRIPTIONS default, and the single top-level READERDESCRIPTION field via one GetReaders full-table fetch. This exceeds typical transparency.
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 information-dense but long and run-on, especially the RESOLVEDESCRIPTIONS sentence, which is grammatically tangled and hard to parse. Every clause adds value, but the structure could be split into shorter sentences or bullets to make the key points easier for an agent to absorb.
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?
Despite having no output schema and no annotations, the description covers purpose, composite call behavior, filtering semantics, defaults, and return-field behavior (PERSONID enrichment and READERDESCRIPTION). An agent has enough context to invoke the tool correctly and predict important behavioral quirks.
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. The description adds real value beyond the schema by explaining that RESOLVEDESCRIPTIONS is an inverted opt-out default, that SCANWINDOW scans system-wide records rather than a date range, and that READERDESCRIPTION is attached once per call rather than per match. This is useful additional semantic context.
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 opens with a specific verb and resource: 'Returns a single reader's access (grant/deny) history for a given READERKEY', and immediately clarifies it is a composite of GetAccessHistory + GetPerson. It distinguishes itself from the sibling get_access_history by noting the lack of a server-side reader filter and the client-side filtering approach.
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 description gives clear context for when this tool is appropriate: per-reader history, client-side filtering, and SCANWINDOW-based scanning rather than date ranges. It contrasts the inverted RESOLVEDESCRIPTIONS default with get_access_history's behavior, but it could more explicitly state when to prefer this vs get_access_history or another sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reader_groupB
Returns the details of a single reader group for a given READERGROUPKEY (wraps NBAPI GetReaderGroup).
| Name | Required | Description | Default |
|---|---|---|---|
| READERGROUPKEY | Yes | Required. The unique READERGROUPKEY of the reader group to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It only says 'returns the details' without specifying the nature of the operation (likely read-only), potential error conditions, or what 'details' encompass. It does mention wrapping NBAPI GetReaderGroup, but that adds no behavioral clarity. The description is too thin to disclose meaningful behavior.
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, focused sentence with no extraneous content. The key fact (returns a single reader group by key) is front-loaded, and the parenthetical implementation detail is supplementary without adding bulk.
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 getter tool with no output schema and no annotations, the description leaves significant gaps: it does not define what 'details' are returned, does not indicate any error behavior, and does not clarify that the operation is read-only. Given the simple single-parameter interface, some of this is inferred, but the absence of return format and usage guidance makes it incomplete for an agent to call correctly.
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% for the single parameter READERGROUPKEY, so the schema fully documents the parameter. The description merely restates 'given READERGROUPKEY' without adding format, constraints, or examples beyond what the schema provides, meeting the baseline.
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 clearly states it returns details of a single reader group for a given READERGROUPKEY, and explicitly uses 'single' to distinguish from the plural sibling get_reader_groups. The verb 'returns' and resource 'reader group' are specific and unambiguous.
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 implied by 'single' and 'given READERGROUPKEY', but the description does not explicitly state when to use this tool over alternatives like get_reader_groups or get_reader. No exclusions or conditions are provided, leaving selection to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reader_groupsB
Lists reader groups configured on the NetBox system (wraps NBAPI GetReaderGroups).
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the behavioral disclosure burden. It only says 'Lists...' which implies read-only, but does not explicitly state safety, pagination behavior, permissions, or side effects. The pagination cursor is mentioned only in the schema, not the description, leaving the agent without important behavioral 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 a single sentence that front-loads the main action. The parenthetical about wrapping NBAPI adds some implementation detail that may not be essential for an agent, but it does not bloat the description. Overall concise and well-structured.
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 no output schema, no annotations, and only one optional parameter, the description still leaves gaps: it does not indicate what the response contains, whether pagination is required for large lists, or how this tool relates to the singular get_reader_group. The description is too minimal to be fully complete for an agent selecting and invoking the 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 description coverage is 100%: the sole parameter STARTFROMKEY has a clear description ('Optional. Pagination cursor to continue listing from a previous call.'). The tool description adds no additional semantic value beyond the schema, so the baseline score of 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 clearly states the action ('Lists'), the resource ('reader groups'), and the system ('NetBox'). It distinguishes itself from sibling tools like get_reader_group (singular) by the plural form, and the parenthetical identifies the underlying API. This is a specific and unambiguous purpose.
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 description implies usage through the plural 'reader groups' (listing all) but does not explicitly state when to use this instead of get_reader_group or related tools. No alternatives or exclusion criteria are mentioned, so the guidance is only implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_readersA
Lists readers configured on the NetBox system (wraps NBAPI GetReaders). There is no portal-id filter — use get_portals to see each reader nested under its portal.
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral burden. 'Lists' and the NBAPI wrapper make the read-only nature reasonably clear, and the 'no portal-id filter' constraint is useful. However, it does not describe pagination behavior or response format beyond what the schema already provides, so behavioral disclosure is adequate but not rich.
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 concise sentences with no wasted words. The core purpose is front-loaded, followed by the key limitation and the sibling alternative, making it highly scannable for an agent.
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 simple one-parameter, no-output-schema tool, the description covers what the tool does, its limitation, and the relevant alternative. The missing explicit statement about what the returned reader list contains is a minor gap, but the description is otherwise sufficient 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 100% for the only parameter, STARTFROMKEY, whose description already explains it as a pagination cursor. The tool description adds no parameter-level meaning beyond that, so the baseline of 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 uses a specific verb ('Lists') and resource ('readers configured on the NetBox system'), making the purpose immediately clear. It also distinguishes itself from get_portals by explicitly noting the absence of a portal-id filter.
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 description directly tells the agent when to use this tool vs. an alternative: use get_readers for a flat reader list, and use get_portals to see readers nested under a portal. This is explicit routing with no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sioA
Returns the details of a single SIO for a given SIOKEY (wraps NBAPI GetSio). Note: the v2 guide's worked example for GetSio sends MERCURYKEY, but its Calling Parameters list — which the guide makes authoritative — specifies SIOKEY, so SIOKEY is what this tool sends.
| Name | Required | Description | Default |
|---|---|---|---|
| SIOKEY | Yes | Required. The key of the SIO to retrieve. Use get_sios to discover keys. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It correctly indicates this is a read operation (returns details) and identifies a potential source of confusion (parameter key type). However, it does not disclose return format, error behavior, or authentication needs, which would be helpful for a simple read 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?
The description is concise with two sentences. The first sentence states the purpose and the wrapper; the second is a focused caveat about the parameter key. No wasted words, and the key information is 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?
For a single-parameter read tool with a clear schema, the description is nearly complete. It addresses a potential ambiguity that could cause errors (the parameter key mismatch) and references a discovery mechanism. The only minor gap is a lack of output format description, but that is not critical since no output schema exists and the purpose is straightforward.
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 explains the SIOKEY parameter and even recommends using get_sios to discover keys. The description adds no additional parameter semantics beyond clarifying that SIOKEY (not MERCURYKEY) is used, which is a useful nuance but not essential given the schema's clarity.
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 clearly states the tool returns details for a single SIO given its SIOKEY, wrapping NBAPI GetSio. It is distinct from sibling tools like get_sios (which lists keys) and other get_* tools that target different resources.
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 description explicitly notes that SIOKEY is used rather than MERCURYKEY, despite a discrepancy in the v2 guide's worked example, and cites the authoritative Calling Parameters list. This directly guides correct usage and prevents misuse, indicating when to trust the description over the guide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_siosA
Lists the SIOs (serial I/O boards) attached to one Mercury panel (wraps NBAPI GetSios). There is no unfiltered "all SIOs" listing — MERCURYKEY is required; the response carries a NEXTKEY pagination cursor.
| Name | Required | Description | Default |
|---|---|---|---|
| MERCURYKEY | Yes | Required. The Mercury panel key whose SIOs should be listed. Use get_mercury_panels to discover keys. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden. It discloses that the request requires MERCURYKEY, that there is no unfiltered listing, and that the response includes a NEXTKEY pagination cursor. This goes beyond a bare 'list SIOs' statement, though it does not detail auth beyond the key or the full response shape.
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 two tight sentences. It front-loads the core purpose, then adds the two most important operational facts: the required key and pagination. Every clause earns its place; 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?
For a single-parameter listing tool with full schema coverage, the description covers the essential context: scope, mandatory key, the absence of an unfiltered variant, and the pagination mechanism. No output schema exists, but the description's mention of the NEXTKEY cursor gives the agent the key behavioral detail needed to call it and continue paging.
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 schema already documents MERCURYKEY. The description adds meaning by clarifying that this key is required and scopes the listing to one panel, and the schema description adds the discovery path via get_mercury_panels. This is useful context beyond the raw 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 ('Lists'), a specific resource ('SIOs attached to one Mercury panel'), and a scope constraint ('one Mercury panel'). It also notes it wraps NBAPI GetSios and explicitly says there is no unfiltered all-SIOs listing, which distinguishes it from any broader listing sibling.
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 description makes the usage context clear: this is for listing SIOs for a single Mercury panel, and MERCURYKEY is required. The schema description adds 'Use get_mercury_panels to discover keys,' which provides a concrete prerequisite. It does not explicitly name alternatives like get_sio, but the scope limitation and key requirement are enough guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_threat_levelsA
Lists threat levels configured on the NetBox system, optionally filtered (wraps NBAPI GetThreatLevels).
| Name | Required | Description | Default |
|---|---|---|---|
| ALLPARTITIONS | No | Optional. Per NBAPI GetThreatLevels filter. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. 'Lists' indicates a read-only operation, and 'wraps NBAPI GetThreatLevels' identifies the backend call, giving some behavioral context. However, it does not mention authentication, return shape, pagination, error behavior, or side effects.
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 sentence with the primary action and resource front-loaded, followed by filtering and wrapper context. Every part earns its place and there is no filler or redundancy.
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 simple one-optional-parameter read tool, the description plus schema is mostly sufficient. However, with no output schema and no annotations, an agent is left without detail on the return format or the semantics of the ALLPARTITIONS filter. It covers the core call but not enough surrounding context.
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 baseline is 3. The description only adds the generic phrase 'optionally filtered' and does not clarify what ALLPARTITIONS expects or how the filter behaves. It adds no real parameter meaning beyond 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 uses the specific verb 'Lists' and names the exact resource 'threat levels configured on the NetBox system', making the purpose clear. It also notes optional filtering, which adds precision. It does not explicitly distinguish itself from sibling get_* tools, though no sibling targets threat levels directly.
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 by the purpose: an agent would infer this is the tool for listing threat levels, with optional filtering. There is no explicit when-to-use guidance, no alternatives, and no exclusions; this is adequate but not proactive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_time_specA
Returns the details of a single time spec for a given TIMESPECKEY (wraps NBAPI GetTimeSpec).
| Name | Required | Description | Default |
|---|---|---|---|
| TIMESPECKEY | Yes | Required. The unique TIMESPECKEY of the time spec to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It states the tool 'returns' details, implying a read operation, but does not disclose error behavior, return format, or any side effects. For a simple get operation, this is minimal but not misleading; it lacks depth but does not contradict anything.
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, focused sentence that front-loads the purpose and input. It contains zero fluff and efficiently conveys the tool's function and the required key. This is a model of conciseness.
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 single-parameter retrieval tool with no output schema and no annotations, the description is adequate. It tells what it does and the input. It does not describe the returned fields or error cases, but given the simplicity and the sibling context, this is sufficient for an agent to use it correctly. The only gap is the vague 'details' without specifics.
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 parameter TIMESPECKEY is already documented in the schema. The tool description adds no extra semantics, format, or examples beyond what the schema provides. This meets the baseline of 3 for schema-covered parameters.
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 clearly states the verb 'Returns', the resource 'details of a single time spec', and the input key TIMESPECKEY. It distinguishes itself from siblings like get_time_specs (plural) and get_time_spec_group(s) by indicating it operates on a single time spec. The wrapper reference adds credibility without confusion.
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 usage is implied: call when you have a TIMESPECKEY to retrieve its details. However, the description does not explicitly mention when not to use it or point to alternatives like get_time_specs for listing. It provides no exclusions or routing guidance, falling into the 'implied usage' category.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_time_spec_groupB
Returns the details of a single time spec group for a given TIMESPECGROUPKEY (wraps NBAPI GetTimeSpecGroup).
| Name | Required | Description | Default |
|---|---|---|---|
| TIMESPECGROUPKEY | Yes | Required. The unique TIMESPECGROUPKEY of the time spec group to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description bears full responsibility for behavioral disclosure. It only says 'returns the details' and wraps NBAPI GetTimeSpecGroup, but does not indicate whether the operation is read-only, what happens if the key is invalid, or the format of the returned details. This is a significant gap for a tool in a system with many similar retrieval tools.
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, front-loaded sentence with no extraneous words. It states the action and the key input immediately, and the NBAPI reference adds context without bloating the text. This is ideal conciseness for a simple retrieval tool.
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 the simplicity (one parameter, no output schema) the description covers the basic call, but it lacks differentiation from the many similar sibling tools (e.g., get_time_spec_groups, get_time_specs) and does not mention error behavior or the contents of the returned details. It is adequate but not fully complete for an agent navigating a large toolset.
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 the TIMESPECGROUPKEY parameter fully. The description merely restates that it uses the key without adding any new meaning, such as accepted formats or constraints. Baseline 3 is appropriate since the schema carries the semantic load.
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 clearly states it returns the details of a single time spec group for a given key, using a specific verb and resource. It does not explicitly differentiate from sibling tools like get_time_spec_groups (plural) or get_time_spec, but the word 'single' and the key parameter imply a distinct retrieval purpose.
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 description gives no guidance on when to use this tool versus alternatives. It only states the input requirement (TIMESPECGROUPKEY) but does not mention when to choose this over get_time_spec_groups for listing, or how it differs from get_time_spec. An agent must infer usage from the name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_time_spec_groupsA
Lists time spec groups configured on the NetBox system (wraps NBAPI GetTimeSpecGroups). RESOLVEMEMBERNAMES defaults to true — an inverted, opt-out default (unlike most optional booleans in this codebase): GetTimeSpecGroups' TIMESPECKEYS.TIMESPECKEY member field carries only bare TIMESPECKEY strings, so this replaces each group member with a {TIMESPECKEY, NAME} object via one GetTimeSpecs full-table fetch per call (not per group/member). An unmatched (unknown/deleted) member key resolves to NAME: ''. Applies only to this plural tool, not the singular get_time_spec_group (confirmed broken/NOT FOUND on this controller — out of scope). Set RESOLVEMEMBERNAMES: false to skip the fetch and return TIMESPECKEYS exactly as GetTimeSpecGroups provides it (bare string or array of strings).
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. | |
| RESOLVEMEMBERNAMES | No | Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Replaces each group's TIMESPECKEYS.TIMESPECKEY bare member key(s) with {TIMESPECKEY, NAME} objects via one GetTimeSpecs full-table fetch per call (not per group/member); an unmatched key resolves to NAME: ''. Set to false to skip the fetch and return TIMESPECKEYS exactly as GetTimeSpecGroups provides it (bare string or array of strings). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and does so thoroughly. It reveals the inverted default, the one full-table fetch per call, the unmatched-key behavior (NAME: ''), and the exact raw return shape when RESOLVEMEMBERNAMES is false. This is exemplary disclosure 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 front-loaded with the core purpose, and every sentence carries substantive operational detail. It is dense and somewhat long, but the complexity of the behavior justifies most of the length. Slight redundancy with the schema's parameter description prevents a 5.
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?
Despite no output schema and no annotations, the description supplies the essential invocation context: default behavior, performance implication, edge-case handling, the broken singular sibling, and the raw-mode alternative. An agent has enough to call this tool correctly without external 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?
Input schema coverage is 100%, so the baseline is 3. The description largely mirrors the schema's already-rich parameter documentation for RESOLVEMEMBERNAMES and adds little beyond it; the added 'unlike most optional booleans' context is useful but already implied by the schema's 'inverse of this codebase's usual' wording. It does not significantly improve on 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 opens with a specific verb and resource: 'Lists time spec groups configured on the NetBox system.' It clearly distinguishes itself from the singular get_time_spec_group by saying the tool applies only to the plural form and that the singular variant is broken/out of scope. An agent can confidently identify what this tool does.
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 description gives explicit when-not guidance by naming the singular get_time_spec_group as confirmed broken and out of scope. It also explains when to set RESOLVEMEMBERNAMES to false versus leaving the default true, which is a practical usage decision. This is more than enough for tool selection among the sibling get_* tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_time_specsA
Lists time specs configured on the NetBox system (wraps NBAPI GetTimeSpecs).
| Name | Required | Description | Default |
|---|---|---|---|
| STARTFROMKEY | No | Optional. Pagination cursor to continue listing from a previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description carries the behavioral burden. 'Lists' and 'wraps NBAPI GetTimeSpecs' imply a read-only retrieval operation, but the description does not explicitly state safety, permissions, pagination behavior, or what the response contains. It provides useful context but leaves notable behavioral details unspecified.
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 sentence communicates the resource, action, and underlying API wrapper with no filler. The essential information is front-loaded and every phrase earns its place.
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?
This is a simple list operation with one optional, well-documented parameter, so it is not severely underserved. However, with no output schema and no annotation coverage, the description leaves return-value structure and the full pagination flow unspecified, making it adequate but not complete.
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%: the only parameter, STARTFROMKEY, is already documented as an optional pagination cursor. The description does not add any parameter-level meaning beyond this, so the baseline score of 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 uses a specific verb ('Lists') and a clear resource ('time specs configured on the NetBox system'), and the plural 'time specs' plus the mention of the system scope distinguishes it from singular/group sibling tools like get_time_spec and get_time_spec_group. It also anchors the behavior to a concrete NBAPI operation.
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 description implies the tool is for listing all time specs configured in NetBox, so an agent can infer when to call it. However, it does not explicitly contrast it with sibling tools such as get_time_spec or get_time_spec_groups, nor does it state when to prefer one over the other.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_udf_list_itemsA
Returns the items of a single UDF list for a given UDFLISTKEY (wraps NBAPI GetUDFListItems).
| Name | Required | Description | Default |
|---|---|---|---|
| UDFLISTKEY | Yes | Required. The unique UDFLISTKEY of the UDF list to retrieve. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden. It does disclose that the call is a retrieval ('Returns') and that it wraps NBAPI GetUDFListItems, but it says nothing about error behavior, response shape, or whether any side effects exist.
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 sentence with no filler, front-loading the core action and resource before the NBAPI wrapper reference. Every clause earns its place.
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 tool with a fully documented schema, the description is mostly sufficient to invoke it. However, with no output schema, it leaves the shape of the returned items unspecified, and it lacks usage or error context.
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 schema already documents UDFLISTKEY as required and unique. The description merely restates the parameter without adding format, source, or additional semantic detail, so it stays at the baseline.
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?
Description uses a specific verb ('Returns'), resource ('items of a single UDF list'), and required key (UDFLISTKEY). The word 'single' and 'items' clearly differentiate it from the sibling get_udf_lists, which retrieves the lists themselves.
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 implied: an agent can infer it should call this tool when it has a UDFLISTKEY and wants that list's items. However, it never explicitly states when to prefer this over get_udf_lists or any alternative, and it gives no exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_udf_listsB
Lists user-defined field (UDF) lists configured on the NetBox system (wraps NBAPI GetUDFLists). No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It only states the action and that no parameters are required, but doesn't disclose return format, pagination, performance implications, or whether it's a read-only operation. For a list operation, an agent might need to know if the list is large or requires iteration, but the description offers no such 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 one sentence, front-loaded with the action and resource, and includes a useful parenthetical about the underlying NBAPI wrapper. The extra note 'No parameters required' is redundant given the empty schema, but harmless. It's concise and well-structured.
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 the tool has zero parameters and no output schema, the description covers the essential input side. However, it doesn't describe what the return value looks like (e.g., a list of UDF list objects with IDs and names), which an agent might need to know for downstream processing. The sibling 'get_udf_list_items' hints at a hierarchy, but the description doesn't clarify the relationship or what fields are returned. Still, for a simple no-param list operation, it's mostly complete.
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?
The tool has zero parameters, and the schema has no properties, so there's no parameter documentation needed. The description explicitly states 'No parameters required,' which is clear and accurate. With 0 parameters, the baseline is 4 as per the rubric, and the description meets it.
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 clearly states it lists user-defined field (UDF) lists configured on the NetBox system, with a specific verb ('Lists') and resource ('UDF lists'). It distinguishes itself from the sibling 'get_udf_list_items' which likely lists the items within a UDF list, and other get_* tools. However, it doesn't explicitly differentiate itself from all siblings or mention the NBAPI wrapper context beyond a parenthetical.
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 description provides no explicit guidance on when to use this tool versus alternatives. The context implies it's for retrieving UDF lists, and the sibling 'get_udf_list_items' suggests a related tool, but no direct comparison or conditions are stated. With many sibling tools, a brief 'use this to fetch all UDF lists; for individual items use get_udf_list_items' would be helpful.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_unlock_windowA
Reports the managed unlock window, if any: the managed portal group "MCP Unlock Window" (key, portals, its unlock time spec group and whether that is the managed time spec group "MCP Unlock Window time specs"), that group and its members (read from paginated GetTimeSpecGroups), the managed time specs and holidays, the window derived from them, and whether it is active right now on the host clock. Read-only: issues only Get commands. No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and explicitly states 'Read-only: issues only Get commands' and 'No parameters required.' It also discloses that data is read from paginated GetTimeSpecGroups, making the safety and data-source behavior clear.
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 core purpose is front-loaded in the first clause, and there is no outright waste. However, the remainder is a long, nested enumeration with multiple parentheticals that is dense and somewhat difficult to scan; splitting it into clearer clauses would improve readability.
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 parameterless report tool with no output schema, the description is complete: it names the managed groups, the time specs and holidays, the derived window, the active status, and the read-only nature. An agent has enough information to invoke it correctly without further context.
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?
The input schema is empty and there are zero parameters, so the baseline is 4. The description reinforces this with 'No parameters required,' confirming to the agent that no arguments are needed.
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 uses a specific verb ('Reports') and a specific resource ('managed unlock window'), then precisely scopes it to the 'MCP Unlock Window' portal group, its time spec group, derived window, and active status. This is clearly differentiated from generic get_time_spec* siblings, even though get_daily_unlock_window is not explicitly named.
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 description gives clear context for when to use the tool: to report the managed unlock window, its constituent groups/specs, and whether it is currently active. It does not explicitly name alternatives or state when not to use it, 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.
get_virtual_credential_requestA
Retrieves a mobile (virtual) credential and its assignment state for a person and card format (wraps NBAPI GetVirtualCredentialRequest). Returns CARDFORMAT, PERSONID and STATUS. A card format the controller does not know answers FAIL with "CARDFORMAT NOT FOUND"; use get_card_formats for valid names.
| Name | Required | Description | Default |
|---|---|---|---|
| PERSONID | Yes | Required. The PERSONID the credential is assigned to. | |
| CARDFORMAT | Yes | Required. Name of the card format used to decode the credential (see get_card_formats). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It discloses an important behavioral trait: an unknown card format returns FAIL with 'CARDFORMAT NOT FOUND'. It also states the exact return fields (CARDFORMAT, PERSONID, STATUS). It does not mention permissions or side effects, but as a read operation, the error disclosure is valuable.
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 sentences, each serving a distinct purpose: purpose, return fields, and error handling with routing. No wasted words; the most critical information is 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?
For a two-parameter read operation with no output schema, the description is quite complete: it states what it returns, how it can fail, and where to find valid parameter values. Minor omission: it doesn't explain the meaning of STATUS values, but that's not essential for invoking the tool correctly.
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%; both parameters already have descriptive text in the schema. The description adds marginal semantic value by linking CARDFORMAT to get_card_formats and mentioning the failure condition, but the schema already defines the parameters adequately.
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 ('Retrieves'), the resource ('mobile (virtual) credential and its assignment state'), and the scope ('for a person and card format'). It also distinguishes itself from siblings by returning CARDFORMAT, PERSONID, and STATUS, and explicitly references get_card_formats for valid 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?
The description clearly implies when to use this tool: to fetch a credential's assignment state for a given person and card format. It explicitly names get_card_formats as an alternative when the card format is unknown, providing a when-not condition and routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_eventsA
Lists the event types/definitions known to the NetBox system (wraps NBAPI ListEvents). RESOLVEPARTITIONNAMES defaults to true — on by default, the same inverted opt-out default used by this codebase's other fixed-cost enrichments: each returned event is enriched with a PARTITIONNAME field resolved from its PARTITIONID via one GetPartitions fetch per call, not per event (GetPartitions costs the same whether it resolves one event or a thousand). Set RESOLVEPARTITIONNAMES: false to skip it.
| Name | Required | Description | Default |
|---|---|---|---|
| RESOLVEPARTITIONNAMES | No | Optional (default true — on by default; an opt-out, not opt-in, default). Enriches each returned event with PARTITIONNAME resolved from its PARTITIONID via one GetPartitions fetch per call (not per event). Set to false to skip it and get the plain ListEvents response. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses the enrichment default, the per-call (not per-event) cost behavior, and the opt-out path – valuable transparency. It does not explicitly state the shape of the plain response or side effects, but for a list operation this is largely adequate.
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 efficient; the purpose is front-loaded and the parameter explanation earns its place. It could be trimmed slightly, but no sentence is wasted and the structure is logical.
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 single-optional-parameter list tool with no output schema, the description covers purpose, parameter behavior, and cost. It lacks an explicit description of the plain response structure and does not give guidance on alternatives, but given the simplicity, it is largely complete.
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 substantial meaning: it explains why the default is true, the cost implications (one GetPartitions fetch per call, not per event), and the effect of setting false. This goes well beyond the schema description and gives an agent a real decision basis.
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 – 'Lists the event types/definitions known to the NetBox system' – and explicitly names the wrapped API (NBAPI ListEvents). It is clearly distinguishable from siblings like get_event_history, and no other sibling appears to list definitions, so an agent can tell it apart without opening 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?
The description implies the tool is for retrieving event definitions, but it does not explicitly contrast with alternatives like get_event_history or state when not to use it. It gives clear guidance on when to set RESOLVEPARTITIONNAMES to false, but not on tool selection relative to siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ping_appB
Pings the NetBox NBAPI application to confirm it is responsive (wraps NBAPI PingApp). No parameters required.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It states that it pings to confirm responsiveness, but does not disclose potential side effects, failure behavior, or what the response indicates. For a tool that likely only checks reachability, this is minimal but not contradictory.
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, efficient sentence with no wasted words. It states the action, target, purpose, and parameter status clearly and front-loads the core functionality.
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 simple, parameter-less health-check tool with no output schema, the description is sufficiently complete. It explains what the tool does and that no inputs are required. The only minor gap is not clarifying the return format or how to interpret the result, but this is not critical for a ping.
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?
The tool has zero parameters, and the description explicitly states 'No parameters required.' The schema coverage is 100% (since there are none), so the baseline for 0 parameters is 4, and the description appropriately confirms this.
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 clearly states the action ('Pings') and the target ('NetBox NBAPI application') with a purpose ('to confirm it is responsive'). It also references the wrapped NBAPI PingApp. However, it does not explicitly differentiate from the sibling 'check_connection', which likely serves a similar health-check role.
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?
No guidance is provided on when to use this tool versus alternatives such as 'check_connection'. The description does not mention any conditions, prerequisites, or exclusions, leaving the agent to infer appropriate usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_person_dataA
Searches for person records matching the given criteria (wraps NBAPI SearchPersonData). Every documented SearchPersonData filter field is modeled explicitly; omit all filters to return every record.
| Name | Required | Description | Default |
|---|---|---|---|
| UDF1 | No | Optional. Search filter on user-defined field UDF1. | |
| UDF2 | No | Optional. Search filter on user-defined field UDF2. | |
| UDF3 | No | Optional. Search filter on user-defined field UDF3. | |
| UDF4 | No | Optional. Search filter on user-defined field UDF4. | |
| UDF5 | No | Optional. Search filter on user-defined field UDF5. | |
| UDF6 | No | Optional. Search filter on user-defined field UDF6. | |
| UDF7 | No | Optional. Search filter on user-defined field UDF7. | |
| UDF8 | No | Optional. Search filter on user-defined field UDF8. | |
| UDF9 | No | Optional. Search filter on user-defined field UDF9. | |
| NOTES | No | Optional. Match on the person record's notes text. | |
| UDF10 | No | Optional. Search filter on user-defined field UDF10. | |
| UDF11 | No | Optional. Search filter on user-defined field UDF11. | |
| UDF12 | No | Optional. Search filter on user-defined field UDF12. | |
| UDF13 | No | Optional. Search filter on user-defined field UDF13. | |
| UDF14 | No | Optional. Search filter on user-defined field UDF14. | |
| UDF15 | No | Optional. Search filter on user-defined field UDF15. | |
| UDF16 | No | Optional. Search filter on user-defined field UDF16. | |
| UDF17 | No | Optional. Search filter on user-defined field UDF17. | |
| UDF18 | No | Optional. Search filter on user-defined field UDF18. | |
| UDF19 | No | Optional. Search filter on user-defined field UDF19. | |
| UDF20 | No | Optional. Search filter on user-defined field UDF20. | |
| DELETED | No | Optional. Include/exclude deleted person records. | |
| HOTSTAMP | No | Optional. Match on a card hot-stamp number. | |
| LASTNAME | No | Optional. Match on the person's last name. | |
| PERSONID | No | Optional. Match on a specific PERSONID. | |
| FIRSTNAME | No | Optional. Match on the person's first name. | |
| CARDFORMAT | No | Optional. Match persons holding a card in this card format. | |
| CARDSTATUS | No | Optional. Match persons holding a card with this card status name. | |
| MIDDLENAME | No | Optional. Match on the person's middle name. | |
| MSUENABLED | No | Optional. Match on whether MSU mobile credentials are enabled ("TRUE"/"FALSE"). | |
| ACCESSLEVEL | No | Optional. Match persons assigned this access level. | |
| MOBILEPHONE | No | Optional. Match on the person's mobile phone number. | |
| CONTACTEMAIL | No | Optional. Match on the person's office email address. | |
| ALLPARTITIONS | No | Optional. Search across all partitions. | |
| NEWESTLASTMOD | No | Optional. Only include records last modified on/before this date/time. | |
| OLDESTLASTMOD | No | Optional. Only include records last modified on/after this date/time. | |
| RAWCARDNUMBER | No | Optional. Match on a raw (unformatted) card number. | |
| VEHICLELICNUM | No | Optional. Match on a vehicle license plate number. | |
| VEHICLETAGNUM | No | Optional. Match on a vehicle tag number. | |
| WILDCARDSEARCH | No | Optional. Treat text filters as wildcard patterns. | |
| CASEINSENSITIVE | No | Optional. Perform a case-insensitive match. | |
| WANTCREDENTIALID | No | Optional. Include CREDENTIALID values on returned access cards. | |
| ACCESSLEVELDETAILS | No | Optional. Include full access level details in the response. | |
| BLUEDIAMONDENABLED | No | Optional. Match on whether BlueDiamond mobile credentials are enabled ("TRUE"/"FALSE"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It only mentions wrapping NBAPI and the ability to omit filters; it does not describe pagination, response format, authentication needs, rate limits, or whether filters combine as AND/OR. For a search tool with 44 parameters, this is a significant gap in transparency about what happens when the tool is invoked.
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 two sentences with zero wasted words. It front-loads the primary purpose and immediately provides a key usage tip (omitting filters returns all records). This is efficient and scannable, earning a high score.
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 tool with 44 optional parameters and no output schema or annotations, the description is notably thin. It does not explain what the response contains, how results are ordered, whether there is pagination, or how filters combine. The absence of any return-format or behavioral detail makes it incomplete for an agent to anticipate the tool's output, especially given the complexity.
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 each parameter's purpose. The description adds no extra meaning beyond restating that filters are modeled explicitly, which is redundant. Baseline 3 is appropriate because the schema carries the semantic weight; the description adds no value beyond that.
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 clearly states a specific verb ('searches'), a resource ('person records'), and the mechanism ('matching the given criteria'). It also notes the wrapper around NBAPI SearchPersonData, which adds context, and explicitly contrasts with a simple 'omit all filters to return every record' behavior. This distinguishes it from sibling tools like get_person that fetch a single record.
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 description gives a concrete usage condition: 'omit all filters to return every record,' implying that providing filters narrows results. It also notes that every filter field is modeled, making it the comprehensive search entry point. While it doesn't explicitly name alternative tools or state when not to use it, the sibling list (e.g., get_person) suggests the distinction, and the guidance is clear enough for an agent to infer when a search is appropriate.
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.
13 tool updates
v0.4.0- Added
get_alarms - Added
get_guide - Added
get_locations - Added
get_mercury_panel - Added
get_mercury_panels - Added
get_network_node - Added
get_network_nodes - Added
get_picture - Added
get_portal_states - Added
get_portal_statuses - Added
get_sio - Added
get_sios - Added
get_virtual_credential_request
14 tool updates
v0.3.0- Changed
get_access_history4 fields changed- removed
Input schema / properties / NEWESTDTTMRemoved value: -{ - "description": "Optional. Newest date/time to include.", - "type": "string" -} - removed
Input schema / properties / OLDESTDTTMRemoved value: -{ - "description": "Optional. Oldest date/time to include.", - "type": "string" -} - added
Input schema / properties / RESOLVEDESCRIPTIONSAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Enrich each returned record with the reader's human-readable READERDESCRIPTION via one GetReaders full-table fetch per call (not per record). Set to false to skip it.", + "type": "boolean" +} - added
Input schema / properties / RESOLVENAMESAdded value: +{ + "description": "Optional (default false). Enrich each returned record with the badge-holder's FIRSTNAME/LASTNAME/FULLNAME/NOTES via one extra GetPerson call per distinct person found in the result.", + "type": "boolean" +}
- Changed
get_access_level1 field changed- added
Input schema / properties / RESOLVEGROUPNAMESAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Resolves TIMESPECGROUPKEY/READERGROUPKEY into new TIMESPECGROUPNAME/READERGROUPNAME sibling fields via one full-table GetTimeSpecGroups fetch and one full-table GetReaderGroups fetch per call (each made only when that axis's key is non-empty; an empty/absent key on one axis yields '' for that axis's name without affecting the other). THREATLEVELGROUPKEY is never resolved — no NBAPI read command exists for threat level groups. Set to false to skip both fetches and return the response exactly as GetAccessLevel provides it.", + "type": "boolean" +}
- Changed
get_access_level_groups1 field changed- added
Input schema / properties / PARTITIONKEYAdded value: +{ + "description": "Optional. Per NBAPI GetAccessLevelGroups — only \"0\" is documented as allowed.", + "type": "string" +}
- Changed
get_access_levels1 field changed- added
Input schema / properties / PARTITIONKEYAdded value: +{ + "description": "Optional. Per NBAPI GetAccessLevels — only \"0\" is documented as allowed.", + "type": "string" +}
- Changed
get_card_access_details2 fields changed- added
Input schema / properties / RESOLVEDESCRIPTIONSAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Enrich each returned ACCESS record with the reader's human-readable READERDESCRIPTION via one GetReaders full-table fetch per call (not per record). Set to false to skip it.", + "type": "boolean" +} - added
Input schema / properties / RESOLVENAMESAdded value: +{ + "description": "Optional (default false). Enrich the top level of the response with the card owner's FIRSTNAME/LASTNAME/FULLNAME/NOTES via a single GetPerson lookup for the response's top-level PERSONID (one lookup per call, not one per ACCESS record — a card has exactly one owner).", + "type": "boolean" +}
- Changed
get_holidays2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - removed
Input schema / properties / STARTFROMKEYRemoved value: -{ - "description": "Optional. Pagination cursor to continue listing from a previous call.", - "type": "string" -}
- Changed
get_portal_group1 field changed- added
Input schema / properties / RESOLVEGROUPNAMESAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Resolves UNLOCKTIMESPECGROUPKEY into a new UNLOCKTIMESPECGROUPNAME sibling field via one full-table GetTimeSpecGroups fetch per call (skipped when UNLOCKTIMESPECGROUPKEY is empty/absent, yielding '' for the name). THREATLEVELGROUPKEY is never resolved — no NBAPI read command exists for threat level groups. Set to false to skip the fetch and return the response exactly as GetPortalGroup provides it.", + "type": "boolean" +}
- Changed
get_portal_groups1 field changed- added
Input schema / properties / RESOLVEGROUPNAMESAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Resolves each group's UNLOCKTIMESPECGROUPKEY bare foreign key into a new sibling UNLOCKTIMESPECGROUPNAME field, via at most one full-table GetTimeSpecGroups fetch per call (not per group) — built once and skipped entirely when every group on the page has an empty/absent UNLOCKTIMESPECGROUPKEY, yielding '' for the name. THREATLEVELGROUPKEY is never resolved — no NBAPI read command exists for threat level groups. Set to false to skip the fetch and return groups exactly as GetPortalGroups provides them.", + "type": "boolean" +}
- Changed
get_portals1 field changed- added
Input schema / properties / RESOLVEDESCRIPTIONSAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Fills in each nested reader's own DESCRIPTION field (GetPortals leaves it unpopulated) via one GetReaders full-table fetch per call (not per portal/reader). Set to false to skip it and return readers exactly as GetPortals provides them.", + "type": "boolean" +}
- Added
get_reader_access_history - Added
get_threat_levels - Changed
get_time_spec_groups1 field changed- added
Input schema / properties / RESOLVEMEMBERNAMESAdded value: +{ + "description": "Optional (default true — on by default; the inverse of this codebase's usual optional-boolean default). Replaces each group's TIMESPECKEYS.TIMESPECKEY bare member key(s) with {TIMESPECKEY, NAME} objects via one GetTimeSpecs full-table fetch per call (not per group/member); an unmatched key resolves to NAME: ''. Set to false to skip the fetch and return TIMESPECKEYS exactly as GetTimeSpecGroups provides it (bare string or array of strings).", + "type": "boolean" +}
- Changed
list_events2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / RESOLVEPARTITIONNAMESAdded value: +{ + "description": "Optional (default true — on by default; an opt-out, not opt-in, default). Enriches each returned event with PARTITIONNAME resolved from its PARTITIONID via one GetPartitions fetch per call (not per event). Set to false to skip it and get the plain ListEvents response.", + "type": "boolean" +}
- Changed
search_person_data9 fields changed- added
Input schema / properties / BLUEDIAMONDENABLEDAdded value: +{ + "description": "Optional. Match on whether BlueDiamond mobile credentials are enabled (\"TRUE\"/\"FALSE\").", + "type": "string" +} - added
Input schema / properties / CARDFORMATAdded value: +{ + "description": "Optional. Match persons holding a card in this card format.", + "type": "string" +} - added
Input schema / properties / CARDSTATUSAdded value: +{ + "description": "Optional. Match persons holding a card with this card status name.", + "type": "string" +} - added
Input schema / properties / CONTACTEMAILAdded value: +{ + "description": "Optional. Match on the person's office email address.", + "type": "string" +} - added
Input schema / properties / MOBILEPHONEAdded value: +{ + "description": "Optional. Match on the person's mobile phone number.", + "type": "string" +} - added
Input schema / properties / MSUENABLEDAdded value: +{ + "description": "Optional. Match on whether MSU mobile credentials are enabled (\"TRUE\"/\"FALSE\").", + "type": "string" +} - added
Input schema / properties / NOTESAdded value: +{ + "description": "Optional. Match on the person record's notes text.", + "type": "string" +} - added
Input schema / properties / VEHICLELICNUMAdded value: +{ + "description": "Optional. Match on a vehicle license plate number.", + "type": "string" +} - added
Input schema / properties / VEHICLETAGNUMAdded value: +{ + "description": "Optional. Match on a vehicle tag number.", + "type": "string" +}
36 tool updates
v0.2.3- First observed
check_connection - First observed
find_portals - First observed
get_access_history - First observed
get_access_level - First observed
get_access_level_group - First observed
get_access_level_groups - First observed
get_access_level_names - First observed
get_access_levels - First observed
get_card_access_details - First observed
get_card_formats - First observed
get_daily_unlock_window - First observed
get_elevators - First observed
get_event_history - First observed
get_floors - First observed
get_holiday - First observed
get_holidays - First observed
get_outputs - First observed
get_partitions - First observed
get_person - First observed
get_portal_group - First observed
get_portal_groups - First observed
get_portals - First observed
get_reader - First observed
get_reader_group - First observed
get_reader_groups - First observed
get_readers - First observed
get_time_spec - First observed
get_time_spec_group - First observed
get_time_spec_groups - First observed
get_time_specs - First observed
get_udf_list_items - First observed
get_udf_lists - First observed
get_unlock_window - First observed
list_events - First observed
ping_app - First observed
search_person_data
TDQS
Scored across 51 tools
Most tools map cleanly to a distinct resource and action, and the singular/plural pairs (e.g. get_time_spec vs get_time_specs) are clearly explained. A few sets such as ping_app vs check_connection, get_portal_states vs get_portal_statuses, and the access-history tools could cause misselection when skimming, but the detailed descriptions disambiguate them.
The dominant get_<entity> / get_<entity>s pattern is predictable and applied across most of the 51 tools. It is diluted by a few different verb styles (check_connection, ping_app, search_person_data, find_portals, list_events, get_guide), but there is no chaotic casing or inconsistent pluralization.
At 51 tools, the surface is far beyond the well-scoped range and even exceeds the 25+ 'too many' threshold. While each tool maps to an NBAPI read command, the sheer number makes the set unwieldy and in need of consolidation or modularization.
The read surface covers many NetBox entities, but there are zero create/update/delete tools, so agents can query but not manage the system. The gap is especially visible because get_locations references set_threat_level and the guide mentions write-safety, yet no such tool is exposed.
Maintenance
Related MCP Connectors
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Scans remote MCP servers for protocol, security, and TLS issues; exposes scan tools via MCP.
Related MCP Servers
AlicenseAqualityCmaintenanceRead-only MCP server for NetBox that enables LLMs to query NetBox objects (devices, IPAM, etc.) and change logs through natural language, with field filtering for token optimization.4234Apache 2.0- AlicenseNot gradedqualityDmaintenanceExposes the UniFi Network Integration API as MCP tools, dynamically loaded from JSON manifests, with read-only mode by default.MIT
- AlicenseAqualityAmaintenanceExposes N-able N-central REST API as MCP tools for managing devices, organizations, users, and more, with support for read-only, write, and full write modes.125MIT
- AlicenseAqualityAmaintenanceProvides MCP tools for governed multi-vendor network device operations, including configuration management (backup, diff, merge, replace, rollback) and read-only queries (facts, interfaces, BGP, LLDP, ARP) via NAPALM, with optional NetBox source-of-truth integration.33MIT