top-clients-by-bandwidth
Retrieve top N clients on a UniFi site sorted by bandwidth usage, with options for combined, transmit-only, or receive-only metrics.
Instructions
Top N clients by bandwidth on a site (combined / tx-only / rx-only). Requires Cloud Connector (UNIFI_API_KEY_OWNER).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Site host name (e.g., 'USM') | |
| topN | No | Number of top clients to return (default: 10) | |
| metric | No | Bandwidth metric: combined (tx+rx), tx-only, rx-only | combined |
| limit | No | Max clients to fetch from API (default: 200) |
Implementation Reference
- src/tools/analytics.ts:220-284 (handler)The main handler function for the top-clients-by-bandwidth tool. Checks connector availability, resolves the site context, fetches clients from the UniFi Cloud Connector API, ranks them by bandwidth metric (combined/tx/rx), and returns the top N clients with bandwidth stats.
export async function topClientsByBandwidth(params: z.infer<typeof topClientsByBandwidthSchema>) { if (!isConnectorAvailable()) { return { site: params.name, error: "Cloud Connector unavailable. Set UNIFI_API_KEY_OWNER to enable per-client bandwidth analysis.", clients: [], }; } const ctx = await resolveConnectorContext(params.name); if (!ctx) { return { site: params.name, error: `Site '${params.name}' not found or connector unavailable`, clients: [], }; } const resp = await connectorClient.get<{ data: ConnectorClient[] }>( ctx.hostId, `network/integration/v1/sites/${ctx.localSiteId}/clients`, { limit: params.limit }, ); const clients = resp.data ?? []; const ranked = clients .map((c) => { const tx = c.uplink?.txRate ?? c.txRate ?? 0; const rx = c.uplink?.rxRate ?? c.rxRate ?? 0; const combined = tx + rx; const value = params.metric === "tx" ? tx : params.metric === "rx" ? rx : combined; const typeStr = typeof c.type === "string" ? c.type : ""; return { name: c.name ?? c.hostname ?? c.macAddress ?? c.id ?? "unknown", ip: c.ipAddress ?? "", mac: c.macAddress ?? "", type: typeStr, // Derive isWired from `type` when boolean field is absent — UniFi // connector clients usually expose only the type discriminator. isWired: typeof c.isWired === "boolean" ? c.isWired : typeStr.toUpperCase() === "WIRED", txBps: tx, rxBps: rx, combinedBps: combined, value, }; }) .sort((a, b) => b.value - a.value) .slice(0, params.topN); const totalBps = clients.reduce((s, c) => { const tx = c.uplink?.txRate ?? c.txRate ?? 0; const rx = c.uplink?.rxRate ?? c.rxRate ?? 0; return s + tx + rx; }, 0); return { site: ctx.hostName, checkedAt: new Date().toISOString(), metric: params.metric, totalClients: clients.length, totalBpsAcrossSite: totalBps, topN: ranked.length, clients: ranked, }; } - src/tools/analytics.ts:212-218 (schema)Zod schema defining the input parameters: name (site host name), topN (default 10), metric (combined/tx/rx, default combined), and limit (default 200).
export const topClientsByBandwidthSchema = z.object({ name: z.string().describe("Site host name (e.g., 'USM')"), topN: z.coerce.number().optional().default(10).describe("Number of top clients to return (default: 10)"), metric: z.enum(["combined", "tx", "rx"]).optional().default("combined") .describe("Bandwidth metric: combined (tx+rx), tx-only, rx-only"), limit: z.coerce.number().optional().default(200).describe("Max clients to fetch from API (default: 200)"), }); - src/index.ts:102-104 (registration)Registration of the tool with name 'top-clients-by-bandwidth' using the MCP server's tool() function, associating it with the schema and handler via wrapToolHandler.
tool("top-clients-by-bandwidth", "Top N clients by bandwidth on a site (combined / tx-only / rx-only). Requires Cloud Connector (UNIFI_API_KEY_OWNER).", topClientsByBandwidthSchema.shape, wrapToolHandler(topClientsByBandwidth)); - src/tools/analytics.ts:197-210 (helper)TypeScript interface ConnectorClient defining the shape of client data returned from the UniFi Cloud Connector API.
interface ConnectorClient { id?: string; name?: string; hostname?: string; ipAddress?: string; macAddress?: string; type?: string; isWired?: boolean; uplink?: { txRate?: number; rxRate?: number }; txBytes?: number; rxBytes?: number; rxRate?: number; txRate?: number; }