Skip to main content
Glama
J-MaFf

s2-netbox-mcp

by J-MaFf

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 — see docs/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.md records the result, including which documented commands are deliberately not implemented and why.

WARNING

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:

  • Login

  • Logout

  • GetAPIVersion

  • GetPerson

  • SearchPersonData

  • GetCardAccessDetails

  • GetCardFormats

  • GetAccessLevel(s)

  • GetAccessLevelGroup(s)

  • GetPortals

  • GetReader(s)

  • GetOutputs

  • GetTimeSpec(s)

  • GetTimeSpecGroup(s)

  • GetHoliday(s)

  • GetPortalGroup(s)

  • GetReaderGroup(s)

  • GetAccessLevelNames

  • GetPartitions

  • GetUDFLists

  • GetUDFListItems

  • GetElevators

  • GetFloors

  • PingApp

  • GetEventHistory

  • ListEvents

  • GetAccessHistory

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

access-model

The person → credential → access level → access level group chain, and the portal-group/time-spec-group name-table collision.

unlock-windows

The holiday + time spec + portal group UNLOCKTIMESPECGROUPKEY recipe, date/time inclusivity rules, and when to prefer the composite tools.

group-and-name-gotchas

modify_portal_group/modify_reader_group replacing a group's entire membership; modify_access_level always needing TIMESPECGROUPKEY.

credentials-and-card-formats

Diagnosing a BIT MISMATCH access-denied event, and remove_person's soft-delete behavior.

api-quirks

STARTFROMKEY/NEXTKEY paging, the missing singular get_portal, and checking write results for the literal SUCCESS.

write-safety

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/npm are on your PATH:

    winget install OpenJS.NodeJS.LTS
  • An 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-mcp

This 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 build

Either 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 .env

Start 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 start

Environment variables

Variable

Required

Default

Description

NETBOX_BASE_URL

Yes

—

Base URL of the NetBox controller's web interface, e.g. https://netbox.example.internal. No trailing slash or path — the client appends NETBOX_API_PATH itself.

NETBOX_USERNAME

Yes

—

NBAPI session-login username.

NETBOX_PASSWORD

Yes

—

NBAPI session-login password. Never logged, never written to any tracked file.

NETBOX_ALLOW_INSECURE_TLS

No

false

Set to true/1/yes to accept a self-signed/on-prem TLS certificate. Explicit opt-in only — any other value (including unset) keeps normal certificate verification.

NETBOX_API_PATH

No

/nbws/goforms/nbapi

The NBAPI path appended to NETBOX_BASE_URL. The default is the verified path on NetBox 6.x controllers. Only set this to override the default — e.g. to the legacy, pre-6.x path /goforms/nbapi, which returns HTTP 410 Gone on 6.x controllers (see Controller prerequisites below). A value without a leading / has one added automatically.

NETBOX_ENABLE_WRITES

No

false

Set to true/1/yes to register the write tools (see Write access below). Unset (or any other value) leaves the server strictly read-only.

NETBOX_ENABLE_DESTRUCTIVE

No

false

Set to true/1/yes, together with NETBOX_ENABLE_WRITES, to additionally register the 11 destructive tools (see Write access below).

NETBOX_EVENT_API_PATH

No

tracks NETBOX_API_PATH

Request path used only for trigger_event. Unset/empty tracks whatever NETBOX_API_PATH resolves to; a non-empty override is used verbatim (leading / added if missing) — e.g. the doc's pre-6.x Event API path /appd/nbapi, if your controller serves it separately.

NETBOX_UNLOCK_HOLIDAY_GROUPS

No

8,7,6

The holiday groups reserved for the managed unlock window, in first,middle,last segment order — see Scheduled unlock windows below. Must be 1-3 distinct integers in 1..8, comma-separated; reserve groups nothing else on the controller uses.

NETBOX_UNLOCK_NAME_PREFIX

No

MCP Unlock Window

Name prefix of every object the managed unlock window creates: the portal group (<prefix>), the time spec group (<prefix> time specs), and the per-segment holidays/time specs (<prefix> first/middle/last). 1-40 characters so the longest name (<prefix> time specs) fits the 64-character NAME limit.

NETBOX_DAILY_UNLOCK_HOLIDAY_GROUP

No

5

The single holiday group reserved for the managed daily recurring unlock window — see Scheduled daily unlock windows below. Must be a single integer in 1..8, and must not be a member of NETBOX_UNLOCK_HOLIDAY_GROUPS (the two features' reserved groups can never collide).

NETBOX_DAILY_UNLOCK_NAME_PREFIX

No

MCP Daily Unlock Window

Name prefix of every object the managed daily unlock window creates: the portal group (<prefix>), the time spec group (<prefix> time specs), and the one holiday/time spec (<prefix> schedule). 1-40 characters so the longest name fits the 64-character NAME limit.

NETBOX_LIVE_TEST_PORTALKEY

No

—

The PORTALKEY of the one door you designate safe to physically unlock during npm run test:live:write/npm run test:live:write:daily. Read only by those scripts, never by the server itself.

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=true registers 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 tools set_portals_state, schedule_unlock_window, and cancel_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 without NETBOX_ENABLE_DESTRUCTIVE because those objects are server-owned; they never delete anything else.

  • NETBOX_ENABLE_DESTRUCTIVE=true, set in addition to NETBOX_ENABLE_WRITES, registers the 11 destructive tools (each description is DESTRUCTIVE:-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_person refuses a call with DELETED="TRUE" or PERSONPURGE="TRUE", and modify_udf_list_items refuses a call where any list item has DELETE="1" — both name NETBOX_ENABLE_DESTRUCTIVE in the error and send nothing to the controller.

  • Every write tool's description starts with WRITE: (or DESTRUCTIVE: for the 11 above), and every successful write's result text contains the literal SUCCESS followed 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 READERKEY or READERGROUPKEY, 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). Login still returns SUCCESS with a session ID, but every subsequent command — including Logout — fails with APIERROR 5. Fix: tick that checkbox on the Data Integration tab. This server's client detects this exact pattern (a successful re-login followed by another APIERROR 5) and surfaces a tool error naming the checkbox directly.

  • "HTTP 410 Gone." The configured NETBOX_API_PATH is 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/nbapi path is deregistered on 6.x and returns 410 for every request. If you're on a pre-6.x controller, set NETBOX_API_PATH=/goforms/nbapi explicitly; if you're on 6.x and still see this, double-check NETBOX_API_PATH isn'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

.mcp.json in your project, or claude_desktop_config.json

Or add non-interactively via claude mcp add-json s2-netbox-mcp '<json>'

Antigravity

~/.gemini/config/mcp_config.json (Windows: %USERPROFILE%\.gemini\config\mcp_config.json)

Global — applies to every Antigravity session

Gemini CLI

~/.gemini/settings.json, under its own mcpServers key

Or add non-interactively via gemini mcp add s2-netbox-mcp <command> [args] -e KEY=value

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

check_connection

GetAPIVersion

—

get_guide

— (pure in-process lookup; no controller call)

— (optional topic)

get_person

GetPerson

PERSONID

search_person_data

SearchPersonData

— (all filters optional)

get_card_access_details

GetCardAccessDetails

ENCODEDNUM, CARDFORMAT (optional MAXRECORDS/OLDESTDTTM/RESOLVENAMES/RESOLVEDESCRIPTIONS)

get_card_formats

GetCardFormats

—

get_access_level

GetAccessLevel

ACCESSLEVELKEY (optional RESOLVEGROUPNAMES)

get_access_levels

GetAccessLevels

— (optional PARTITIONKEY/STARTFROMKEY/STARTFROMNAME/WANTKEY)

get_access_level_group

GetAccessLevelGroup

ACCESSLEVELGROUPKEY

get_access_level_groups

GetAccessLevelGroups

— (optional STARTFROMKEY/PARTITIONKEY)

get_access_level_names

GetAccessLevelNames

— (optional PARTITIONKEY/STARTFROMNAME)

get_portals

GetPortals

— (optional STARTFROMKEY/RESOLVEDESCRIPTIONS; no single-portal filter — returns each portal with its nested readers)

get_reader

GetReader

READERKEY

get_readers

GetReaders

— (optional STARTFROMKEY; no portal-id filter)

get_outputs

GetOutputs

— (optional STARTFROMKEY)

find_portals

GetPortals + GetReaders (composite)

query (search terms)

get_event_history

GetEventHistory

— (optional EVENTNAME/STARTDTTM/ENDDTTM/NEXTKEY)

list_events

ListEvents

— (optional RESOLVEPARTITIONNAMES, default true)

get_access_history

GetAccessHistory

— (optional STARTLOGID/AFTERLOGID/ORDER/MAXRECORDS/ENCODEDNUM/HOTSTAMP/CARDFORMAT/RESOLVENAMES/RESOLVEDESCRIPTIONS)

get_reader_access_history

GetAccessHistory + GetPerson + GetReaders (composite)

READERKEY (optional SCANWINDOW/MAXMATCHES/RESOLVEDESCRIPTIONS)

get_time_spec

GetTimeSpec

TIMESPECKEY

get_time_specs

GetTimeSpecs

— (optional STARTFROMKEY)

get_time_spec_group

GetTimeSpecGroup

TIMESPECGROUPKEY

get_time_spec_groups

GetTimeSpecGroups

— (optional STARTFROMKEY/RESOLVEMEMBERNAMES)

get_holiday

GetHoliday

HOLIDAYKEY

get_holidays

GetHolidays

— (no calling parameters)

get_portal_group

GetPortalGroup

PORTALGROUPKEY (optional RESOLVEGROUPNAMES)

get_portal_groups

GetPortalGroups

— (optional STARTFROMKEY/RESOLVEGROUPNAMES)

get_reader_group

GetReaderGroup

READERGROUPKEY

get_reader_groups

GetReaderGroups

— (optional STARTFROMKEY)

get_partitions

GetPartitions

—

get_udf_lists

GetUDFLists

—

get_udf_list_items

GetUDFListItems

UDFLISTKEY

get_elevators

GetElevators

— (optional STARTFROMKEY)

get_floors

GetFloors

— (optional STARTFROMKEY)

ping_app

PingApp

—

get_threat_levels

GetThreatLevels

— (optional ALLPARTITIONS)

get_unlock_window

GetPortalGroups + GetPortalGroup + GetTimeSpecGroups + GetTimeSpecs + GetHolidays + GetHoliday (composite)

—

get_daily_unlock_window

GetPortalGroups + GetPortalGroup + GetTimeSpecGroups + GetTimeSpecs + GetHolidays (composite)

—

get_portal_states

GetPortalStates

— (optional PORTALSTATES)

get_portal_statuses

GetPortalStatuses

— (optional ALLPARTITIONS/PORTALKEY/STATEKEY/PARTITIONKEY/LOCATIONKEY; the live state of each door, not its configuration)

get_locations

GetLocations

— (optional ALLPARTITIONS/STARTFROMKEY)

get_alarms

GetAlarms

— (optional ALLPARTITIONS/PARTITIONKEY/ID/EVENTID/ACTIVITYID/OWNERID)

get_picture

GetPicture

PERSONID (returns a Base64 JPEG in PICTURE; may be large)

get_virtual_credential_request

GetVirtualCredentialRequest

PERSONID, CARDFORMAT

get_mercury_panels

GetMercuryPanels

— (optional ALLPARTITIONS/PARTITIONKEY/MERCURYKEY/NAME)

get_mercury_panel

GetMercuryPanel

MERCURYKEY

get_network_nodes

GetNetworkNodes

— (optional ALLPARTITIONS/PARTITIONKEY/NODEKEY/UNIQUEIDENTIFIER/NAME)

get_network_node

GetNetworkNode

NODEKEY (optional PARTITIONKEY)

get_sios

GetSios

MERCURYKEY (no unfiltered SIO listing exists)

get_sio

GetSio

SIOKEY

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

lock_portal

LockPortal

PORTALKEY

write

unlock_portal

UnlockPortal

PORTALKEY

write

momentary_unlock_portal

MomentaryUnlockPortal

PORTALKEY

write

dog_on_next_exit_portal

DogOnNextExitPortal

PORTALKEY

write

activate_output

ActivateOutput

OUTPUTKEY

write

deactivate_output

DeactivateOutput

OUTPUTKEY

write

add_time_spec

AddTimeSpec

NAME

write

modify_time_spec

ModifyTimeSpec

TIMESPECKEY

write

add_time_spec_group

AddTimeSpecGroup

NAME

write

modify_time_spec_group

ModifyTimeSpecGroup

TIMESPECGROUPKEY

write

delete_time_spec

DeleteTimeSpec

TIMESPECKEY

destructive

delete_time_spec_group

DeleteTimeSpecGroup

TIMESPECGROUPKEY

destructive

add_holiday

AddHoliday

HOLIDAYNAME, STARTDATE, ENDDATE, HOLIDAYGROUPS

write

modify_holiday

ModifyHoliday

HOLIDAYKEY

write

delete_holiday

DeleteHoliday

HOLIDAYKEY

destructive

add_portal_group

AddPortalGroup

NAME, PORTALKEYS, UNLOCKTIMESPECGROUPKEY

write

modify_portal_group

ModifyPortalGroup

PORTALGROUPKEY, PORTALKEYS

write

delete_portal_group

DeletePortalGroup

PORTALGROUPKEY

destructive

add_reader_group

AddReaderGroup

NAME, READERKEYS

write

modify_reader_group

ModifyReaderGroup

READERGROUPKEY, READERKEYS

write

delete_reader_group

DeleteReaderGroup

READERGROUPKEY

destructive

add_access_level

AddAccessLevel

ACCESSLEVELNAME, TIMESPECGROUPKEY

write

modify_access_level

ModifyAccessLevel

ACCESSLEVELKEY, TIMESPECGROUPKEY

write

delete_access_level

DeleteAccessLevel

ACCESSLEVELKEY

destructive

add_access_level_group

AddAccessLevelGroup

NAME

write

modify_access_level_group

ModifyAccessLevelGroup

ACCESSLEVELGROUPKEY

write

delete_access_level_group

DeleteAccessLevelGroup

ACCESSLEVELGROUPKEY

destructive

add_person

AddPerson

LASTNAME

write

modify_person

ModifyPerson

PERSONID

write

remove_person

RemovePerson

PERSONID

destructive

add_credential

AddCredential

PERSONID, CARDFORMAT (+ ENCODEDNUM or HOTSTAMP)

write

modify_credential

ModifyCredential

PERSONID

write

remove_credential

RemoveCredential

PERSONID (+ CREDENTIALID or ENCODEDNUM/HOTSTAMP)

destructive

set_threat_level

SetThreatLevel

LEVELNAME (optional LOCATIONKEYS)

write

add_threat_level

AddThreatLevel

LEVELNAME

write

modify_threat_level

ModifyThreatLevel

LEVELNAME, SEQNUM, COLOR

write

remove_threat_level

RemoveThreatLevel

LEVELNAME

destructive

add_threat_level_group

AddThreatLevelGroup

LEVELGROUPNAME

write

modify_threat_level_group

ModifyThreatLevelGroup

LEVELGROUPNAME, LEVELNAMES

write

remove_threat_level_group

RemoveThreatLevelGroup

LEVELGROUPNAME

destructive

trigger_event

TriggerEvent

EVENTNAME, EVENTACTION

write

insert_activity

InsertActivity

ACTIVITYTYPE

write

add_partition

AddPartition

NAME, TIMEZONE

write

switch_partition

SwitchPartition

PARTITIONKEY

write

modify_udf_list_items

ModifyUDFListItems

UDFLISTKEY, LISTITEMS

write

set_portals_state

LockPortal / UnlockPortal / MomentaryUnlockPortal per portal, after GetPortals (composite)

action (portalKeys optional; omitted = every portal)

write

schedule_unlock_window

AddHoliday/ModifyHoliday, AddTimeSpec/ModifyTimeSpec, AddTimeSpecGroup/ModifyTimeSpecGroup, AddPortalGroup/ModifyPortalGroup, plus DeleteTimeSpec/DeleteHoliday of leftover managed segments, plus reads (composite)

start, end (portalKeys, acknowledgeSideEffects, dryRun optional)

write

cancel_unlock_window

ModifyPortalGroup, DeleteHoliday, ModifyTimeSpecGroup, DeleteTimeSpec — managed objects only — plus reads (composite)

—

write

schedule_daily_unlock_window

AddHoliday/ModifyHoliday, AddTimeSpec/ModifyTimeSpec, AddTimeSpecGroup/ModifyTimeSpecGroup, AddPortalGroup/ModifyPortalGroup, plus reads (composite)

startDate, endDate, dailyStartTime, dailyEndTime (portalKeys, acknowledgeSideEffects, dryRun optional)

write

cancel_daily_unlock_window

ModifyPortalGroup, DeleteHoliday, ModifyTimeSpecGroup, DeleteTimeSpec — managed objects only — plus reads (composite)

—

write

add_duty_log

AddDutyLog

PERSONID, LOGTEXT (optional ACTIVITYID/PARTITIONKEY)

write

add_virtual_credential_request

AddVirtualCredentialRequest

PERSONID, CARDFORMAT

write

remove_virtual_credential_request

RemoveVirtualCredentialRequest

PERSONID, CARDFORMAT

destructive

add_mercury_panel

AddMercuryPanel

NAME, TYPE, ENABLED, PARTITIONKEY, NETWORK (nested: IPADDRESS, TLSSECURE)

write

modify_mercury_panel

ModifyMercuryPanel

MERCURYKEY, NAME, ENABLED, NETWORK (nested: IPADDRESS, TLSSECURE)

write

delete_mercury_panel

DeleteMercuryPanel

MERCURYKEY

destructive

add_network_node

AddNetworkNode

NAME, TYPE, ENABLED, PARTITIONKEY, UNIQUEIDENTIFIER, DHCPENABLED

write

modify_network_node

ModifyNetworkNode

NODEKEY

write

delete_network_node

DeleteNetworkNode

NODEKEY

destructive

add_sio

AddSio

MERCURYKEY, NAME, MODEL, CHANNEL, ADDRESS, REVINPUT

write

modify_sio

ModifySio

SIOKEY, NAME, REVINPUT

write

delete_sio

DeleteSio

SIOKEY

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. portalKeys are keys only (use get_portals or find_portals to map names); an unknown key is refused before anything is written.

  • End of day on the NBAPI is 23:59 (the built-in Always uses it), and ENDTIME is 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 stated end minute (observed live: a window ending 08:27 relocked at 08:27:59 controller time). An end of 00:00 means "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 GetEventHistory carries 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_state is the immediate alternative: its UNLOCK is an Extended Unlock that lasts until LOCK, 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's ERRMSG text verbatim.

  • A <CODE>NOT FOUND</CODE> response (e.g. an unknown PERSONID) is returned as a normal, non-error result stating "not found" — it is not thrown as an exception.

  • SIGINT/SIGTERM trigger Logout for 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. portalKeys are keys only (use get_portals/find_portals to map names); an unknown key is refused before anything is written.

  • An overnight-crossing daily window is not supported: dailyEndTime must be strictly later than dailyStartTime (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-DD and times are HH: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 test

Runs 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:live

This 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 prints npm warn Unknown cli config "--go" and the flag never reaches the script — observed live, 2026-09-15). If --go doesn'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

unlock_portal

UnlockPortal (Extended Unlock)

lock_portal

lock_portal

LockPortal

—

momentary_unlock_portal

MomentaryUnlockPortal (relocks itself)

—

dog_on_next_exit_portal

DogOnNextExitPortal

lock_portal

activate_output

ActivateOutput on the portal's strike output

deactivate_output

deactivate_output

DeactivateOutput on the portal's strike output

—

set_portals_state_unlock

the real set_portals_state (setPortalsState) tool, action UNLOCK

set_portals_state_lock

set_portals_state_lock

set_portals_state, action LOCK

—

set_portals_state_momentary

set_portals_state, action MOMENTARY_UNLOCK (relocks itself)

—

set_threat_level

SetThreatLevel LEVELNAME=<--value>

set_threat_level --value Default

trigger_event_activate

TriggerEvent EVENTNAME=<--value> EVENTACTION=ACTIVATE PARTITIONID=1

trigger_event_deactivate

trigger_event_deactivate

TriggerEvent EVENTNAME=<--value> EVENTACTION=DEACTIVATE PARTITIONID=1

—

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 window

PowerShell: see the same-named caveat under "Live write smoke test" above — if --go is silently dropped, use npx tsx scripts/live-check-write-daily.ts --go instead (verified live, 2026-09-15, on portal 02OF01A: 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 real schedule_daily_unlock_window executor, prints an OBSERVE: ... line, polls get_daily_unlock_window every 30 s until one minute after relock, then calls cancel_daily_unlock_window and asserts the managed portal group is on Never with no managed holiday or time spec left (leftBehind is 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), and npm test never 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_picture wraps GetPicture and returns the Base64 JPEG unmodified.

  • Elevator and floor writes — not a choice: neither the v1 nor the v2 guide documents any Add/Modify/Delete command for elevators or floors, so get_elevators/get_floors are 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_alarms reads alarms, but this server does not drive an operator alarm queue.

  • StreamEvents / the persistent /appdevent/nbapi/event push feed

  • MAC-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_portals map 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 tools
check_connectionA

Confirms the server can authenticate to the configured S2 NetBox controller and returns the NBAPI version string (wraps GetAPIVersion). No parameters required.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesRequired. Search terms, e.g. "maintenance office", "electrical closet", or "01OF05".

TDQS

A4.5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
ORDERNoOptional. Sort order for returned records.
HOTSTAMPNoOptional. Restrict results to this hot-stamp number.
AFTERLOGIDNoOptional. Return records strictly after this LOGID.
CARDFORMATNoOptional. Card format of ENCODEDNUM/HOTSTAMP.
ENCODEDNUMNoOptional. Restrict results to this encoded card number.
MAXRECORDSNoOptional. Maximum number of records to return.
STARTLOGIDNoOptional. Begin returning records at this LOGID.
RESOLVENAMESNoOptional (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.
RESOLVEDESCRIPTIONSNoOptional (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

A3.9/5.0
Behavior5/5

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.

Conciseness3/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
ACCESSLEVELKEYYesRequired. The unique ACCESSLEVELKEY of the access level to retrieve.
RESOLVEGROUPNAMESNoOptional (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

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
ACCESSLEVELGROUPKEYYesRequired. The unique ACCESSLEVELGROUPKEY of the access level group to retrieve.

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
PARTITIONKEYNoOptional. Per NBAPI GetAccessLevelGroups — only "0" is documented as allowed.
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

B3.1/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
PARTITIONKEYNoOptional. Per NBAPI GetAccessLevelNames — only "0" is documented as allowed.
STARTFROMNAMENoOptional. Pagination cursor (name) to continue listing from a previous call.

TDQS

B3.3/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
WANTKEYNoOptional. Per NBAPI GetAccessLevels.
PARTITIONKEYNoOptional. Per NBAPI GetAccessLevels — only "0" is documented as allowed.
STARTFROMKEYNoOptional. Pagination cursor (key) to continue listing from a previous call.
STARTFROMNAMENoOptional. Pagination cursor (name) to continue listing from a previous call.

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
IDNoOptional. Return the alarm with this alarm ID.
EVENTIDNoOptional. Return alarms raised by this event ID.
OWNERIDNoOptional. Return alarms owned by this operator/person ID.
ACTIVITYIDNoOptional. Return alarms for this activity ID.
PARTITIONKEYNoOptional. Return alarms for this partition key only.
ALLPARTITIONSNoOptional. "TRUE" to return alarms across all partitions.

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
CARDFORMATYesRequired. The card format of ENCODEDNUM.
ENCODEDNUMYesRequired. The encoded card number whose access details should be retrieved.
MAXRECORDSNoOptional. Maximum number of access records to return.
OLDESTDTTMNoOptional. Oldest date/time to include in the returned access records.
RESOLVENAMESNoOptional (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).
RESOLVEDESCRIPTIONSNoOptional (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

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.1/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness2/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
ENDDTTMNoOptional. End of the date/time range to query (NBAPI-documented format).
NEXTKEYNoOptional. Pagination continuation cursor from a previous call.
EVENTNAMENoOptional. Restrict results to this event name.
STARTDTTMNoOptional. Start of the date/time range to query (NBAPI-documented format).

TDQS

B3.2/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

A3.6/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicNoOptional. 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

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
HOLIDAYKEYYesRequired. The unique HOLIDAYKEY of the holiday to retrieve.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. 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".)
ALLPARTITIONSNoOptional. "TRUE" to list locations across all partitions.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
MERCURYKEYYesRequired. The key of the Mercury panel to retrieve. Use get_mercury_panels to discover keys.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
NAMENoOptional. Restrict the listing to Mercury panels with this name.
MERCURYKEYNoOptional. Restrict the listing to this Mercury panel key.
PARTITIONKEYNoOptional. Restrict the listing to this partition key.
ALLPARTITIONSNoOptional. "TRUE" to list Mercury panels across all partitions.

TDQS

A3.6/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
NODEKEYYesRequired. The key of the network node to retrieve. Use get_network_nodes to discover keys.
PARTITIONKEYNoOptional. Key of the partition the node belongs to.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
NAMENoOptional. Restrict the listing to network nodes with this name.
NODEKEYNoOptional. Restrict the listing to this network node key.
PARTITIONKEYNoOptional. Restrict the listing to this partition key.
ALLPARTITIONSNoOptional. "TRUE" to list network nodes across all partitions.
UNIQUEIDENTIFIERNoOptional. Restrict the listing to the node with this 16-hex-digit unique identifier.

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
PERSONIDYesRequired. The unique PERSONID of the person record to retrieve.
ALLPARTITIONSNoOptional. Per NBAPI GetPerson.
WANTCREDENTIALIDNoOptional. Include CREDENTIALID values on returned access cards.
ACCESSLEVELDETAILSNoOptional. Include full access level details in the response.

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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".

ParametersJSON Schema
NameRequiredDescriptionDefault
PERSONIDYesRequired. The PERSONID of the person whose picture should be retrieved.

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
PORTALGROUPKEYYesRequired. The unique PORTALGROUPKEY of the portal group to retrieve.
RESOLVEGROUPNAMESNoOptional (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

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.
RESOLVEGROUPNAMESNoOptional (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

A4.6/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor — the NEXTKEY from a previous call, to continue listing.
RESOLVEDESCRIPTIONSNoOptional (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

A4.3/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
PORTALSTATESNoOptional. "TRUE" returns portal states across all partitions; omitted, the controller returns states for the default or currently switched partition.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
STATEKEYNoOptional. Report only portals currently in this state (see get_portal_states).
PORTALKEYNoOptional. Report status for this portal only.
LOCATIONKEYNoOptional. Report status for portals at this location only (see get_locations).
PARTITIONKEYNoOptional. Report status for portals in this partition only.
ALLPARTITIONSNoOptional. "TRUE" to report portal status across all partitions.

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
READERKEYYesRequired. The unique READERKEY of the reader to retrieve.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
READERKEYYesRequired. Only access records for this reader are returned.
MAXMATCHESNoOptional. Maximum number of matches to include, in chronological order. Defaults to 100.
SCANWINDOWNoOptional. Number of most-recent system-wide access records to scan. Defaults to 2000.
RESOLVEDESCRIPTIONSNoOptional (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

A4.5/5.0
Behavior5/5

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.

Conciseness3/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
READERGROUPKEYYesRequired. The unique READERGROUPKEY of the reader group to retrieve.

TDQS

B3.4/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
SIOKEYYesRequired. The key of the SIO to retrieve. Use get_sios to discover keys.

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
MERCURYKEYYesRequired. The Mercury panel key whose SIOs should be listed. Use get_mercury_panels to discover keys.

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
ALLPARTITIONSNoOptional. Per NBAPI GetThreatLevels filter.

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
TIMESPECKEYYesRequired. The unique TIMESPECKEY of the time spec to retrieve.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
TIMESPECGROUPKEYYesRequired. The unique TIMESPECGROUPKEY of the time spec group to retrieve.

TDQS

B3.1/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.
RESOLVEMEMBERNAMESNoOptional (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

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
STARTFROMKEYNoOptional. Pagination cursor to continue listing from a previous call.

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
UDFLISTKEYYesRequired. The unique UDFLISTKEY of the UDF list to retrieve.

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
PERSONIDYesRequired. The PERSONID the credential is assigned to.
CARDFORMATYesRequired. Name of the card format used to decode the credential (see get_card_formats).

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
RESOLVEPARTITIONNAMESNoOptional (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

A4.2/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
UDF1NoOptional. Search filter on user-defined field UDF1.
UDF2NoOptional. Search filter on user-defined field UDF2.
UDF3NoOptional. Search filter on user-defined field UDF3.
UDF4NoOptional. Search filter on user-defined field UDF4.
UDF5NoOptional. Search filter on user-defined field UDF5.
UDF6NoOptional. Search filter on user-defined field UDF6.
UDF7NoOptional. Search filter on user-defined field UDF7.
UDF8NoOptional. Search filter on user-defined field UDF8.
UDF9NoOptional. Search filter on user-defined field UDF9.
NOTESNoOptional. Match on the person record's notes text.
UDF10NoOptional. Search filter on user-defined field UDF10.
UDF11NoOptional. Search filter on user-defined field UDF11.
UDF12NoOptional. Search filter on user-defined field UDF12.
UDF13NoOptional. Search filter on user-defined field UDF13.
UDF14NoOptional. Search filter on user-defined field UDF14.
UDF15NoOptional. Search filter on user-defined field UDF15.
UDF16NoOptional. Search filter on user-defined field UDF16.
UDF17NoOptional. Search filter on user-defined field UDF17.
UDF18NoOptional. Search filter on user-defined field UDF18.
UDF19NoOptional. Search filter on user-defined field UDF19.
UDF20NoOptional. Search filter on user-defined field UDF20.
DELETEDNoOptional. Include/exclude deleted person records.
HOTSTAMPNoOptional. Match on a card hot-stamp number.
LASTNAMENoOptional. Match on the person's last name.
PERSONIDNoOptional. Match on a specific PERSONID.
FIRSTNAMENoOptional. Match on the person's first name.
CARDFORMATNoOptional. Match persons holding a card in this card format.
CARDSTATUSNoOptional. Match persons holding a card with this card status name.
MIDDLENAMENoOptional. Match on the person's middle name.
MSUENABLEDNoOptional. Match on whether MSU mobile credentials are enabled ("TRUE"/"FALSE").
ACCESSLEVELNoOptional. Match persons assigned this access level.
MOBILEPHONENoOptional. Match on the person's mobile phone number.
CONTACTEMAILNoOptional. Match on the person's office email address.
ALLPARTITIONSNoOptional. Search across all partitions.
NEWESTLASTMODNoOptional. Only include records last modified on/before this date/time.
OLDESTLASTMODNoOptional. Only include records last modified on/after this date/time.
RAWCARDNUMBERNoOptional. Match on a raw (unformatted) card number.
VEHICLELICNUMNoOptional. Match on a vehicle license plate number.
VEHICLETAGNUMNoOptional. Match on a vehicle tag number.
WILDCARDSEARCHNoOptional. Treat text filters as wildcard patterns.
CASEINSENSITIVENoOptional. Perform a case-insensitive match.
WANTCREDENTIALIDNoOptional. Include CREDENTIALID values on returned access cards.
ACCESSLEVELDETAILSNoOptional. Include full access level details in the response.
BLUEDIAMONDENABLEDNoOptional. Match on whether BlueDiamond mobile credentials are enabled ("TRUE"/"FALSE").

TDQS

A3.6/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 13 tool updatesv0.4.0
    • Addedget_alarms
    • Addedget_guide
    • Addedget_locations
    • Addedget_mercury_panel
    • Addedget_mercury_panels
    • Addedget_network_node
    • Addedget_network_nodes
    • Addedget_picture
    • Addedget_portal_states
    • Addedget_portal_statuses
    • Addedget_sio
    • Addedget_sios
    • Addedget_virtual_credential_request
  2. 14 tool updatesv0.3.0
    • Changedget_access_history4 fields changed
      • removedInput schema / properties / NEWESTDTTM
        Removed value: -{
        -  "description": "Optional. Newest date/time to include.",
        -  "type": "string"
        -}
      • removedInput schema / properties / OLDESTDTTM
        Removed value: -{
        -  "description": "Optional. Oldest date/time to include.",
        -  "type": "string"
        -}
      • addedInput schema / properties / RESOLVEDESCRIPTIONS
        Added 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"
        +}
      • addedInput schema / properties / RESOLVENAMES
        Added 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"
        +}
    • Changedget_access_level1 field changed
      • addedInput schema / properties / RESOLVEGROUPNAMES
        Added 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"
        +}
    • Changedget_access_level_groups1 field changed
      • addedInput schema / properties / PARTITIONKEY
        Added value: +{
        +  "description": "Optional. Per NBAPI GetAccessLevelGroups — only \"0\" is documented as allowed.",
        +  "type": "string"
        +}
    • Changedget_access_levels1 field changed
      • addedInput schema / properties / PARTITIONKEY
        Added value: +{
        +  "description": "Optional. Per NBAPI GetAccessLevels — only \"0\" is documented as allowed.",
        +  "type": "string"
        +}
    • Changedget_card_access_details2 fields changed
      • addedInput schema / properties / RESOLVEDESCRIPTIONS
        Added 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"
        +}
      • addedInput schema / properties / RESOLVENAMES
        Added 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"
        +}
    • Changedget_holidays2 fields changed
      • removedInput schema / additionalProperties
        Removed value: -false
      • removedInput schema / properties / STARTFROMKEY
        Removed value: -{
        -  "description": "Optional. Pagination cursor to continue listing from a previous call.",
        -  "type": "string"
        -}
    • Changedget_portal_group1 field changed
      • addedInput schema / properties / RESOLVEGROUPNAMES
        Added 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"
        +}
    • Changedget_portal_groups1 field changed
      • addedInput schema / properties / RESOLVEGROUPNAMES
        Added 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"
        +}
    • Changedget_portals1 field changed
      • addedInput schema / properties / RESOLVEDESCRIPTIONS
        Added 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"
        +}
    • Addedget_reader_access_history
    • Addedget_threat_levels
    • Changedget_time_spec_groups1 field changed
      • addedInput schema / properties / RESOLVEMEMBERNAMES
        Added 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"
        +}
    • Changedlist_events2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / RESOLVEPARTITIONNAMES
        Added 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"
        +}
    • Changedsearch_person_data9 fields changed
      • addedInput schema / properties / BLUEDIAMONDENABLED
        Added value: +{
        +  "description": "Optional. Match on whether BlueDiamond mobile credentials are enabled (\"TRUE\"/\"FALSE\").",
        +  "type": "string"
        +}
      • addedInput schema / properties / CARDFORMAT
        Added value: +{
        +  "description": "Optional. Match persons holding a card in this card format.",
        +  "type": "string"
        +}
      • addedInput schema / properties / CARDSTATUS
        Added value: +{
        +  "description": "Optional. Match persons holding a card with this card status name.",
        +  "type": "string"
        +}
      • addedInput schema / properties / CONTACTEMAIL
        Added value: +{
        +  "description": "Optional. Match on the person's office email address.",
        +  "type": "string"
        +}
      • addedInput schema / properties / MOBILEPHONE
        Added value: +{
        +  "description": "Optional. Match on the person's mobile phone number.",
        +  "type": "string"
        +}
      • addedInput schema / properties / MSUENABLED
        Added value: +{
        +  "description": "Optional. Match on whether MSU mobile credentials are enabled (\"TRUE\"/\"FALSE\").",
        +  "type": "string"
        +}
      • addedInput schema / properties / NOTES
        Added value: +{
        +  "description": "Optional. Match on the person record's notes text.",
        +  "type": "string"
        +}
      • addedInput schema / properties / VEHICLELICNUM
        Added value: +{
        +  "description": "Optional. Match on a vehicle license plate number.",
        +  "type": "string"
        +}
      • addedInput schema / properties / VEHICLETAGNUM
        Added value: +{
        +  "description": "Optional. Match on a vehicle tag number.",
        +  "type": "string"
        +}
  3. 36 tool updatesv0.2.3
    • First observedcheck_connection
    • First observedfind_portals
    • First observedget_access_history
    • First observedget_access_level
    • First observedget_access_level_group
    • First observedget_access_level_groups
    • First observedget_access_level_names
    • First observedget_access_levels
    • First observedget_card_access_details
    • First observedget_card_formats
    • First observedget_daily_unlock_window
    • First observedget_elevators
    • First observedget_event_history
    • First observedget_floors
    • First observedget_holiday
    • First observedget_holidays
    • First observedget_outputs
    • First observedget_partitions
    • First observedget_person
    • First observedget_portal_group
    • First observedget_portal_groups
    • First observedget_portals
    • First observedget_reader
    • First observedget_reader_group
    • First observedget_reader_groups
    • First observedget_readers
    • First observedget_time_spec
    • First observedget_time_spec_group
    • First observedget_time_spec_groups
    • First observedget_time_specs
    • First observedget_udf_list_items
    • First observedget_udf_lists
    • First observedget_unlock_window
    • First observedlist_events
    • First observedping_app
    • First observedsearch_person_data

TDQS

B3.3/5.0

Scored across 51 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count1/5

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.

Completeness2/5

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

ActivityMaintained
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers