Describe WhisperGraph Label
describe_labelVerify a label exists, retrieve its node count, and list property keys to avoid silent query scans. Use before filtering on a specific property.
Instructions
Describe a single label: confirm it exists, get its node count, and enumerate the property keys observed on that label.
Use this BEFORE writing a query that filters on a specific property. If you write WHERE h.fqdn = "..." but describe_label("HOSTNAME") returns properties = ["name", "threatScore", ...], your query will silently scan the entire label. Verify first.
Argument: label (string, required) — uppercase letters, digits, and underscores only.
Returns: {name, exists, count, properties[], edgesDoc}. Cached 5 minutes.
Tip: edge types are NOT in the response — see the whisper://schema/relationships resource for which edges connect this label to others.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| label | Yes | Label name. Uppercase letters, digits, underscores. Examples: HOSTNAME, IPV4, ASN. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| exists | Yes | ||
| count | No | ||
| properties | No | ||
| edgesDoc | No | ||
| error | No | ||
| suggestion | No | ||
| propertiesError | No |
Implementation Reference
- src/tools/schema-tools.ts:64-85 (handler)The `describeLabel` method on the `SchemaTools` class — the core handler that validates the label, checks cache, and fetches label detail (count + properties) from the backend.
async describeLabel( label: string | null | undefined, credential: Credential | null, ): Promise<Row> { if (!isValidLabel(label)) { return { name: label ?? "", exists: false, error: "invalid label name", suggestion: "Labels are uppercase letters, digits, and underscores (e.g. HOSTNAME, IPV4). " + "See list_labels for the full list.", }; } const cached = this.labelDetailCache.get(label); if (cached) return cached; const detail = await this.fetchLabelDetail(label, credential); this.labelDetailCache.set(label, detail); return detail; } - src/tools/schema-tools.ts:111-144 (handler)The `fetchLabelDetail` private method — fetches node count (from cached labels list) and samples up to 100 nodes to enumerate property keys for the label.
private async fetchLabelDetail(label: string, credential: Credential | null): Promise<Row> { const detail: Row = { name: label }; // Pull the count from the cached labels list rather than running count(n) // against a billion-node label. const count = await this.findLabelCount(label, credential); const labelExists = count !== null; if (count !== null) { detail.count = count; } detail.exists = labelExists; if (!labelExists) { detail.suggestion = "Label not found. Run list_labels for the full list."; return detail; } // Sample 100 nodes to enumerate observed property keys — bounded so it stays // cheap even on billion-scale labels. try { const cypher = `MATCH (n:${label}) WITH n LIMIT 100 ` + "UNWIND keys(n) AS k RETURN DISTINCT k AS property"; const raw = await this.backend.execute(cypher, undefined, credential); detail.properties = (raw.rows ?? []).map((row) => String(row.property)).sort(); } catch (error) { log.warn(`describe_label property sampling failed for "${label}": ${describeError(error)}`); detail.properties = []; detail.propertiesError = "Property sampling failed; see whisper://schema/full"; } detail.edgesDoc = "whisper://schema/relationships"; return detail; } - src/server.ts:68-77 (schema)The `describeLabelOutputShape` object defining the Zod output schema: name, exists, count, properties, edgesDoc, error, suggestion, propertiesError.
const describeLabelOutputShape = { name: z.string(), exists: z.boolean(), count: z.number().optional(), properties: z.array(z.string()).optional(), edgesDoc: z.string().optional(), error: z.string().optional(), suggestion: z.string().optional(), propertiesError: z.string().optional(), }; - src/server.ts:140-160 (registration)Registration of the 'describe_label' tool on the MCP server with input schema (label string), output schema, and the handler callback that calls schemaTools.describeLabel().
server.registerTool( "describe_label", { title: "Describe WhisperGraph Label", description: DESCRIBE_LABEL_DESCRIPTION, inputSchema: { label: z .string() .describe( "Label name. Uppercase letters, digits, underscores. Examples: HOSTNAME, IPV4, ASN.", ), }, outputSchema: describeLabelOutputShape, annotations: READ_ONLY_ANNOTATIONS, }, async (args, extra) => { const credential = resolveCredential(extra.requestInfo?.headers, config.apiKey); const result = await schemaTools.describeLabel(args.label, credential); return toolResult(result); }, ); - src/tools/descriptions.ts:47-55 (helper)The `DESCRIBE_LABEL_DESCRIPTION` constant with the natural-language usage instructions for the tool, telling the LLM when and why to use describe_label.
export const DESCRIBE_LABEL_DESCRIPTION = `Describe a single label: confirm it exists, get its node count, and enumerate the property keys observed on that label. Use this BEFORE writing a query that filters on a specific property. If you write WHERE h.fqdn = "..." but describe_label("HOSTNAME") returns properties = ["name", "threatScore", ...], your query will silently scan the entire label. Verify first. Argument: label (string, required) — uppercase letters, digits, and underscores only. Returns: {name, exists, count, properties[], edgesDoc}. Cached 5 minutes. Tip: edge types are NOT in the response — see the whisper://schema/relationships resource for which edges connect this label to others.`;