Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
nameYesSite host name (e.g. 'USM')
includeClientsNoInclude connected clients (requires owner key)
clientLimitNoMax clients to fetch
includeNetworksNoInclude network configs (requires owner key)
includeWifiNoInclude WiFi broadcasts (requires owner key)
extractFieldsNoComma-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

  • 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,
      };
    }
  • 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);
  • 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; }
    }
  • 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,
        }],
      }),
    );
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description discloses that fields dependent on connector owner key are auto-skipped, and explains output format differences between ChatGPT and Claude clients. This provides useful behavioral context beyond the schema, though it does not cover aspects like rate limits or data freshness.

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 three sentences, each adding distinct value: purpose, benefit, and behavioral nuance. It is front-loaded with the core function and avoids redundancy, earning its length.

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 complexity (aggregating multiple data types) and lack of output schema, the description adequately explains the returned components and platform-specific output differences. However, it does not address pagination or the meaning of the clientLimit default, leaving minor 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 the schema already documents each parameter. The description adds little semantic value beyond what the schema provides, only reiterating 'requires owner key' based on connector status. 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 clearly states it provides a 'deep aggregated site view' combining multiple components (devices, WAN, clients, networks, WiFi broadcasts) in one call, explicitly replacing 4-5 round-trips. This distinguishes it from sibling tools that focus on individual aspects.

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 indicates when to use the tool (for a comprehensive overview to avoid multiple calls) and hints at prerequisites with 'Connector-dependent fields auto-skip when owner key absent.' It does not explicitly list alternatives but the context implies it is a consolidation tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/us-all/unifi-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server