Skip to main content
Glama
whisper-sec

WhisperGraph MCP Server

Official

Describe WhisperGraph Label

describe_label
Read-onlyIdempotent

Verify 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

TableJSON Schema
NameRequiredDescriptionDefault
labelYesLabel name. Uppercase letters, digits, underscores. Examples: HOSTNAME, IPV4, ASN.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYes
existsYes
countNo
propertiesNo
edgesDocNo
errorNo
suggestionNo
propertiesErrorNo

Implementation Reference

  • 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;
    }
  • 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;
    }
  • 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);
      },
    );
  • 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.`;
Behavior5/5

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

Annotations already indicate readOnlyHint=true, destructiveHint=false, idempotentHint=true. Description adds caching behavior (5 minutes) and return structure, going beyond annotations without contradiction.

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?

Concise, each sentence provides distinct value: overview, usage guidance, argument details, return fields, and a tip. No redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema (signal states true), description covers key return fields, caching, and a caveat about edge types. It is complete for a describe tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. Description adds value by specifying allowed characters (uppercase, digits, underscores) and providing examples like HOSTNAME, IPV4.

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 describes a single label: confirms existence, gets node count, and enumerates property keys. It distinguishes from sibling tools like list_labels which lists all labels.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly advises to use this before writing a query that filters on a property, and warns about silent scans if property doesn't exist. Also notes that edge types are not returned, directing to a resource.

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/whisper-sec/whisper-graph-mcp'

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