Skip to main content
Glama

detect_anomaly

Read-onlyIdempotent

Flag outlier points in numeric series using Z-score or IQR. Returns indices and values to identify anomalies in metrics, fraud signals, or sensor noise.

Instructions

[Premium] Flag outlier points in a numeric series using Z-score (parametric, assumes ~normal) or IQR (robust to skew). Use for monitoring metrics, fraud signals, sensor noise, quality control. Z-score is faster and tighter on near-normal data; IQR is the right default when the distribution has heavy tails or known outliers. Returns indices + values + the underlying statistics. For projecting a series forward, use predict_forecast. Requires ORACLAW_API_KEY.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dataYesNumeric series to scan.
methodNoDefault: zscore.
thresholdNoZ-score: standard deviations above mean (default: 3.0). IQR: multiplier on IQR (default: 1.5).

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
methodYes
anomaliesYes
statsNoFor zscore: {mean, stdDev, threshold}. For iqr: {q1, q3, iqr, lowerBound, upperBound}.
totalPointsNo
anomalyCountYes

Implementation Reference

  • Core Z-Score anomaly detection handler. Computes mean & stdDev from data using simple-statistics, then flags points whose absolute z-score exceeds threshold.
    export function detectAnomaliesZScore(
      data: number[],
      threshold: number = 3.0,
    ): AnomalyResult {
      if (data.length < 2) {
        return { anomalies: [], mean: data[0] ?? 0, stdDev: 0, threshold };
      }
    
      const mu = ssMean(data);
      const sigma = ssStdDev(data);
    
      if (sigma === 0) {
        return { anomalies: [], mean: mu, stdDev: 0, threshold };
      }
    
      const anomalies: Array<{ index: number; value: number; zScore: number }> = [];
    
      for (let i = 0; i < data.length; i++) {
        const z = (data[i]! - mu) / sigma;
        if (Math.abs(z) > threshold) {
          anomalies.push({ index: i, value: data[i]!, zScore: z });
        }
      }
    
      return { anomalies, mean: mu, stdDev: sigma, threshold };
    }
  • Core IQR anomaly detection handler. Computes Q1, Q3, and IQR using simple-statistics, then flags points outside [Q1-k*IQR, Q3+k*IQR].
    export function detectAnomaliesIQR(
      data: number[],
      k: number = 1.5,
    ): IQRAnomalyResult {
      if (data.length < 4) {
        const q1 = data.length > 0 ? ssQuantile(data, 0.25) : 0;
        const q3 = data.length > 0 ? ssQuantile(data, 0.75) : 0;
        const iqr = q3 - q1;
        return {
          anomalies: [],
          q1,
          q3,
          iqr,
          lowerBound: q1 - k * iqr,
          upperBound: q3 + k * iqr,
        };
      }
    
      const q1 = ssQuantile(data, 0.25);
      const q3 = ssQuantile(data, 0.75);
      const iqr = ssIQR(data);
      const lowerBound = q1 - k * iqr;
      const upperBound = q3 + k * iqr;
    
      const anomalies: Array<{ index: number; value: number }> = [];
    
      for (let i = 0; i < data.length; i++) {
        if (data[i]! < lowerBound || data[i]! > upperBound) {
          anomalies.push({ index: i, value: data[i]! });
        }
      }
    
      return { anomalies, q1, q3, iqr, lowerBound, upperBound };
    }
  • MCP tool schema registration for 'detect_anomaly'. Defines input: array of numbers, method enum (zscore/iqr), threshold number.
    {
      name: "detect_anomaly",
      description:
        "Anomaly detection in numeric data using Z-Score or IQR methods. Z-Score flags points exceeding a standard deviation threshold (parametric, assumes normality). IQR flags points outside interquartile fences (non-parametric, robust to skew). Use for fraud detection, quality control, sensor monitoring, or data cleaning.",
      inputSchema: {
        type: "object" as const,
        properties: {
          data: {
            type: "array",
            items: { type: "number" },
            description: "Numeric observations to scan for anomalies",
          },
          method: {
            type: "string",
            enum: ["zscore", "iqr"],
            description: "Detection method (default: zscore)",
          },
          threshold: {
            type: "number",
            description: "Z-score cutoff (default 3.0) or IQR multiplier (default 1.5)",
          },
        },
        required: ["data"],
      },
  • MCP server case handler routing 'detect_anomaly' calls to detectAnomaliesIQR or detectAnomaliesZScore functions.
    case "detect_anomaly": {
      const { data, method, threshold } = args as {
        data: number[];
        method?: "zscore" | "iqr";
        threshold?: number;
      };
      const m = method ?? "zscore";
      if (m === "iqr") {
        const result = detectAnomaliesIQR(data, threshold ?? 1.5);
        return { content: [{ type: "text", text: JSON.stringify({ method: "iqr", anomalies: result.anomalies, q1: result.q1, q3: result.q3, iqr: result.iqr, lowerBound: result.lowerBound, upperBound: result.upperBound, count: result.anomalies.length }, null, 2) }] };
      }
      const result = detectAnomaliesZScore(data, threshold ?? 3.0);
      return { content: [{ type: "text", text: JSON.stringify({ method: "zscore", anomalies: result.anomalies, mean: result.mean, stdDev: result.stdDev, threshold: result.threshold, count: result.anomalies.length }, null, 2) }] };
    }
  • Full anomaly detector module with types (AnomalyResult, IQRAnomalyResult, StreamingResult) and the StreamingAnomalyDetector class (Welford's online algorithm).
    /**
     * Anomaly Detection
     *
     * Three complementary methods for identifying outliers in numeric data:
     *   1. Z-Score — parametric, assumes approximate normality
     *   2. IQR (Interquartile Range) — non-parametric, robust to skew
     *   3. StreamingAnomalyDetector — Welford's online algorithm for real-time feeds
     *
     * Uses `simple-statistics` for robust z-score and IQR primitives.
     */
    
    import {
      mean as ssMean,
      standardDeviation as ssStdDev,
      quantile as ssQuantile,
      interquartileRange as ssIQR,
    } from "simple-statistics";
    
    // ── Types ────────────────────────────────────────────────
    
    /** Result of a batch z-score anomaly scan */
    export interface AnomalyResult {
      /** Detected anomalies with index, value, and z-score */
      anomalies: Array<{ index: number; value: number; zScore: number }>;
      /** Population mean */
      mean: number;
      /** Population standard deviation */
      stdDev: number;
      /** Z-score threshold that was applied */
      threshold: number;
    }
    
    /** Result of a batch IQR anomaly scan */
    export interface IQRAnomalyResult {
      /** Detected anomalies with index and value */
      anomalies: Array<{ index: number; value: number }>;
      /** First quartile (25th percentile) */
      q1: number;
      /** Third quartile (75th percentile) */
      q3: number;
      /** Interquartile range (q3 - q1) */
      iqr: number;
      /** Lower fence: q1 - k * iqr */
      lowerBound: number;
      /** Upper fence: q3 + k * iqr */
      upperBound: number;
    }
    
    /** Single observation result from the streaming detector */
    export interface StreamingResult {
      /** Whether this value was flagged as an anomaly */
      isAnomaly: boolean;
      /** Absolute z-score of this observation */
      zScore: number;
      /** The observed value */
      value: number;
      /** Running mean after incorporating this value */
      mean: number;
      /** Running standard deviation after incorporating this value */
      stdDev: number;
    }
    
    // ── Z-Score Detector ─────────────────────────────────────
    
    /**
     * Detect anomalies using the z-score method.
     *
     * A data point is an anomaly if its absolute z-score exceeds `threshold`.
     * Default threshold of 3.0 corresponds to ~0.27 % of a normal distribution.
     *
     * @param data      - Numeric observations
     * @param threshold - z-score cutoff (default 3.0)
     * @returns AnomalyResult with flagged indices, mean, stdDev, and threshold
     *
     * O(n) time, O(k) space where k = number of anomalies
     */
    export function detectAnomaliesZScore(
      data: number[],
      threshold: number = 3.0,
    ): AnomalyResult {
      if (data.length < 2) {
        return { anomalies: [], mean: data[0] ?? 0, stdDev: 0, threshold };
      }
    
      const mu = ssMean(data);
      const sigma = ssStdDev(data);
    
      if (sigma === 0) {
        return { anomalies: [], mean: mu, stdDev: 0, threshold };
      }
    
      const anomalies: Array<{ index: number; value: number; zScore: number }> = [];
    
      for (let i = 0; i < data.length; i++) {
        const z = (data[i]! - mu) / sigma;
        if (Math.abs(z) > threshold) {
          anomalies.push({ index: i, value: data[i]!, zScore: z });
        }
      }
    
      return { anomalies, mean: mu, stdDev: sigma, threshold };
    }
    
    // ── IQR Detector ─────────────────────────────────────────
    
    /**
     * Detect anomalies using the Interquartile Range (IQR) method.
     *
     * Values outside [Q1 - k*IQR, Q3 + k*IQR] are flagged. The default
     * multiplier k = 1.5 flags "mild" outliers; k = 3.0 flags "extreme" outliers.
     *
     * @param data - Numeric observations
     * @param k    - IQR multiplier (default 1.5)
     * @returns IQRAnomalyResult with flagged indices and fence values
     *
     * O(n log n) due to quantile computation, O(k) space
     */
    export function detectAnomaliesIQR(
      data: number[],
      k: number = 1.5,
    ): IQRAnomalyResult {
      if (data.length < 4) {
        const q1 = data.length > 0 ? ssQuantile(data, 0.25) : 0;
        const q3 = data.length > 0 ? ssQuantile(data, 0.75) : 0;
        const iqr = q3 - q1;
        return {
          anomalies: [],
          q1,
          q3,
          iqr,
          lowerBound: q1 - k * iqr,
          upperBound: q3 + k * iqr,
        };
      }
    
      const q1 = ssQuantile(data, 0.25);
      const q3 = ssQuantile(data, 0.75);
      const iqr = ssIQR(data);
      const lowerBound = q1 - k * iqr;
      const upperBound = q3 + k * iqr;
    
      const anomalies: Array<{ index: number; value: number }> = [];
    
      for (let i = 0; i < data.length; i++) {
        if (data[i]! < lowerBound || data[i]! > upperBound) {
          anomalies.push({ index: i, value: data[i]! });
        }
      }
    
      return { anomalies, q1, q3, iqr, lowerBound, upperBound };
    }
Behavior4/5

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

Annotations already indicate read-only and idempotent. Description adds premium requirement (ORACLAW_API_KEY) and return details (indices, values, statistics), improving transparency beyond annotations.

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?

Five sentences efficiently convey purpose, usage, parameters, and limitations, with 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?

With full schema coverage, annotations, and output schema, description adds algorithm rationale, return structure, and a sibling reference, making it complete.

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 covers all parameters with descriptions. Description adds contextual defaults (thresholds) and method selection criteria (Z-score vs IQR), enhancing semantic understanding.

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?

Description clearly states it detects outliers in numeric series using Z-score or IQR, specifying use cases like monitoring metrics, fraud signals, etc. It distinguishes from sibling tool predict_forecast.

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 explains when to use Z-score (near-normal data) vs IQR (heavy tails), and directs to predict_forecast for projection, providing clear decision guidance.

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/Whatsonyourmind/oraclaw'

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