Skip to content
SZ-MCP
Get Support

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.

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.

CallReturns
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_endpoints({ query }) scores every operation on the surface and returns the best matches, up to 20 by default.

Scoring is additive per query token:

MatchScore
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' });

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

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.

The vendored specs are Swagger 2.0, not OpenAPI 3, and two fields inherit from that:

  • requestBody is always null. Swagger 2.0 has no requestBody key. A request body is declared as an entry in parameters with in: "body", and its schema lives there.
  • hasBody is false on 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.

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 };
}