summarize-site
Consolidate devices, WAN status, clients, networks, and WiFi broadcasts into a single aggregated response, replacing multiple round-trips. Optionally extract specific fields to reduce response size.
Instructions
Deep aggregated site view: devices + WAN status + (opt) clients + networks + WiFi broadcasts in one call. Replaces 4-5 round-trips. Connector-dependent fields auto-skip when owner key absent. Renders an Apps SDK card on ChatGPT clients (Claude clients receive the same JSON text).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Site host name (e.g. 'USM') | |
| includeClients | No | Include connected clients (requires owner key) | |
| clientLimit | No | Max clients to fetch | |
| includeNetworks | No | Include network configs (requires owner key) | |
| includeWifi | No | Include WiFi broadcasts (requires owner key) | |
| extractFields | No | Comma-separated dotted paths to project from response (e.g. 'id,name,owner.name,columns.*.name'). Use `*` as wildcard for arrays/objects. Wrap field names with dots in backticks. Reduces response tokens dramatically on large entities. |
Implementation Reference
- src/tools/aggregations.ts:55-135 (handler)Main handler function for summarize-site. Resolves site by host name, fetches device list (slimmed), WAN status, and optionally fetches clients, networks, and WiFi broadcasts via connector API. Returns aggregated response.
export async function summarizeSite(params: z.infer<typeof summarizeSiteSchema>) { const name = params.name; const hostEntry = await resolveDevicesByHostName(name); if (!hostEntry) { return { site: name, error: `site '${name}' not found`, summary: { found: false } }; } const sitesResp = await unifiClient.get<{ data: Array<{ hostId: string; statistics: SiteStats }> }>("/sites").catch(() => ({ data: [] })); const siteData = sitesResp.data.find((s) => s.hostId === hostEntry.hostId); let connectorCtx = null; if (isConnectorAvailable() && (params.includeClients || params.includeNetworks || params.includeWifi)) { connectorCtx = await resolveConnectorContext(name).catch(() => null); } const caveats: string[] = []; const ctx = connectorCtx; const { clients, networks, wifi } = await aggregate( { clients: ctx && params.includeClients ? () => connectorClient.get(ctx.hostId, `network/integration/v1/sites/${ctx.localSiteId}/clients`, { limit: params.clientLimit }) : () => Promise.resolve(null), networks: ctx && params.includeNetworks ? () => connectorClient.get(ctx.hostId, `network/integration/v1/sites/${ctx.localSiteId}/networks`) : () => Promise.resolve(null), wifi: ctx && params.includeWifi ? () => connectorClient.get(ctx.hostId, `network/integration/v1/sites/${ctx.localSiteId}/wifi-broadcasts`) : () => Promise.resolve(null), }, caveats, ); // Slim each device to drop noise fields (uidb icon blob, adoptionTime, // isManaged, note) that bloat the response without aiding LLM analysis. // Callers needing full device payload should use list-devices directly. const devices = hostEntry.devices.map((d) => ({ id: d.id, mac: d.mac, name: d.name, model: d.model, shortname: d.shortname, ip: d.ip, productLine: d.productLine, status: d.status, version: d.version, firmwareStatus: d.firmwareStatus, isConsole: d.isConsole, startupTime: d.startupTime, })); const onlineDevices = devices.filter((d) => d.status === "online").length; const wans = siteData?.statistics.wans ?? {}; const minWanUptime = Object.values(wans).reduce<number | null>((acc, w) => { const u = w.wanUptime ?? null; if (u === null) return acc; return acc === null ? u : Math.min(acc, u); }, null); return { site: name, hostId: hostEntry.hostId, devices: { total: devices.length, online: onlineDevices, offline: devices.length - onlineDevices, list: devices }, wan: wans, clients, networks, wifiBroadcasts: wifi, summary: { gateway: siteData?.statistics.gateway?.shortname ?? "unknown", deviceOnlinePct: devices.length === 0 ? 0 : Math.round((onlineDevices / devices.length) * 1000) / 10, minWanUptime, // connectorAvailable reflects the owner-key capability, independent of // whether this call requested any connector-backed includeX flag. connectorAvailable: isConnectorAvailable(), // connectorResolved reports whether we actually fetched a connector // context for this call (false when no includeX flag was set). connectorResolved: !!connectorCtx, clientsIncluded: !!clients, }, caveats, }; } - src/tools/aggregations.ts:39-46 (schema)Zod schema for summarize-site tool input validation: name (string), includeClients (bool, default false), clientLimit (number, default 50), includeNetworks (bool, default true), includeWifi (bool, default true), extractFields (optional string).
export const summarizeSiteSchema = z.object({ name: z.string().describe("Site host name (e.g. 'USM')"), includeClients: z.boolean().optional().default(false).describe("Include connected clients (requires owner key)"), clientLimit: z.coerce.number().optional().default(50).describe("Max clients to fetch"), includeNetworks: z.boolean().optional().default(true).describe("Include network configs (requires owner key)"), includeWifi: z.boolean().optional().default(true).describe("Include WiFi broadcasts (requires owner key)"), extractFields: ef, }); - src/index.ts:340-344 (registration)Registration of the summarize-site tool with the MCP server. Uses summarizeSiteSchema for validation and summarizeSiteWithCard as the handler (which wraps the main handler with card metadata).
tool("summarize-site", "Deep aggregated site view: devices + WAN status + (opt) clients + networks + WiFi broadcasts in one call. Replaces 4-5 round-trips. Connector-dependent fields auto-skip when owner key absent. Renders an Apps SDK card on ChatGPT clients (Claude clients receive the same JSON text).", summarizeSiteSchema.shape, summarizeSiteWithCard); - src/index.ts:323-339 (helper)Card-aware wrapper around the main handler. Wraps summarizeSite with error/redaction handling via wrapToolHandler, then adds _meta for Apps SDK UI card rendering (openai/outputTemplate) on success.
const SUMMARIZE_SITE_CARD_URI = "ui://widget/summarize-site.html"; const wrappedSummarizeSite = wrapToolHandler(summarizeSite); async function summarizeSiteWithCard(args: Parameters<typeof wrappedSummarizeSite>[0]) { const result = await wrappedSummarizeSite(args); if (result.isError) return result; try { const structured = JSON.parse(result.content[0].text); return { ...result, structuredContent: structured, _meta: { "openai/outputTemplate": SUMMARIZE_SITE_CARD_URI, "ui.resourceUri": SUMMARIZE_SITE_CARD_URI, }, }; } catch { return result; } } - src/resources.ts:170-189 (helper)MCP resource registration for the summarize-site-card UI template. Serves the HTML template used by Apps SDK (ChatGPT) clients to render the card output.
server.registerResource( "summarize-site-card", "ui://widget/summarize-site.html", { title: "Site Summary card", description: "Apps SDK UI template rendered with summarize-site tool output", mimeType: "text/html+skybridge", _meta: { "openai/outputTemplate": "ui://widget/summarize-site.html", "ui.resourceUri": "ui://widget/summarize-site.html", }, }, async (uri) => ({ contents: [{ uri: uri.toString(), mimeType: "text/html+skybridge", text: SUMMARIZE_SITE_HTML, }], }), );