Finding the right endpoint
Discovery on both namespaces follows the same three steps: list the tags, search or browse within them, then read the endpoint’s schema before calling it.
Start with list_tags
Section titled “Start with list_tags”list_tags() takes no arguments and returns every tag on the surface with the
number of operations under it. Tags are the top of the hierarchy: there is no
tag-group level above them.
| Call | Returns |
|---|---|
wifi.list_tags() | All 119 WSG tags with operation counts |
switches.list_tags() | All 42 SwitchM tags with operation counts |
Earlier versions of SZ-MCP also had a list_tag_groups primitive and a group
argument on list_tags. Both were removed: the SmartZone specs define no tag
groups, so they could only ever return an empty list. If an older conversation
or saved prompt still calls list_tag_groups, replace that step with a bare
list_tags().
Search by keyword
Section titled “Search by keyword”search_endpoints({ query }) scores every operation on the surface and returns
the best matches, up to 20 by default.
Scoring is additive per query token:
| Match | Score |
|---|---|
Token appears in operationId | +2 |
| Token appears in the search blob | +1 |
| Token appears in a tag name | +1 |
| Token appears in the path | +1 |
The search blob is built from operationId, tags, summary, description, path
and method. In practice that means operationId and summary are what you
are searching, because the vendored specs populate no operation descriptions
at all — 0 of 1,116 on WSG and 0 of 238 on SwitchM — and no tag descriptions
either.
Queries are tokenised on whitespace and matched case-insensitively as
substrings, so query: 'wlan' hits getWlan, WLAN and /rkszones/{id}/wlans
alike. A query shorter than two characters is rejected.
You can narrow a search with tag or method:
await wifi.search_endpoints({ query: 'profile', tag: 'WLAN', method: 'POST' });Browse a tag exhaustively
Section titled “Browse a tag exhaustively”When you know the tag, list_endpoints_by_tag({ tag }) returns everything under
it — 50 at a time by default, with offset for the rest. Its count field is
the full total, not the page size, so it tells you whether to page again.
Unlike search_endpoints, this is an exact tag match. Tag names come from
list_tags() and must be passed verbatim, including spaces and capitalisation
— 'Switch VLAN Setting', not 'switch-vlan-setting'.
Read the schema before calling
Section titled “Read the schema before calling”get_endpoint_details({ method, path }) returns the operation’s full OpenAPI
definition with $refs inlined, three levels deep. This is the step that tells
you what a call actually requires.
Two quirks to expect in the output
Section titled “Two quirks to expect in the output”The vendored specs are Swagger 2.0, not OpenAPI 3, and two fields inherit from that:
requestBodyis alwaysnull. Swagger 2.0 has norequestBodykey. A request body is declared as an entry inparameterswithin: "body", and its schema lives there.hasBodyisfalseon every operation in the index, for the same reason — on all 1,354 of them, including operations that unambiguously take a body.
So to find out whether an endpoint takes a body, and what shape it is, read
parameters and look for the entry whose in is "body". Do not rely on
hasBody, and do not read requestBody: null as “takes no body”.
You will also see serviceTicket listed as a required query parameter on nearly
every operation. Ignore it — SZ-MCP attaches it server-side, and passing it
yourself is neither needed nor honoured.
The path that works
Section titled “The path that works”Putting it together, as a single program:
async () => { // 1. What areas exist on this surface? const { tags } = await switches.list_tags();
// 2. Find candidates const hits = await switches.search_endpoints({ query: 'vlan', limit: 5 });
// 3. Read the schema of the best one const top = hits.results[0]; const details = await switches.get_endpoint_details({ method: top.method, path: top.path, });
// 4. The body schema lives in parameters, not requestBody const bodyParam = details.parameters?.find(p => p.in === 'body');
return { tagCount: tags.length, top, bodyParam };}