@anyshift/mcp-proxy 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/__tests__/unit/timeseries.test.d.ts +1 -0
  2. package/dist/__tests__/unit/timeseries.test.js +217 -0
  3. package/dist/fileWriter/writer.js +1 -1
  4. package/dist/index.js +75 -29
  5. package/dist/jq/handler.js +11 -56
  6. package/dist/jq/tool.d.ts +3 -9
  7. package/dist/jq/tool.js +4 -4
  8. package/dist/timeseries/algorithms/index.d.ts +8 -0
  9. package/dist/timeseries/algorithms/index.js +8 -0
  10. package/dist/timeseries/algorithms/mad.d.ts +15 -0
  11. package/dist/timeseries/algorithms/mad.js +44 -0
  12. package/dist/timeseries/algorithms/moving-average.d.ts +15 -0
  13. package/dist/timeseries/algorithms/moving-average.js +72 -0
  14. package/dist/timeseries/algorithms/rolling-quantile.d.ts +16 -0
  15. package/dist/timeseries/algorithms/rolling-quantile.js +78 -0
  16. package/dist/timeseries/algorithms/stats.d.ts +49 -0
  17. package/dist/timeseries/algorithms/stats.js +139 -0
  18. package/dist/timeseries/algorithms/threshold.d.ts +15 -0
  19. package/dist/timeseries/algorithms/threshold.js +49 -0
  20. package/dist/timeseries/handler.d.ts +10 -0
  21. package/dist/timeseries/handler.js +292 -0
  22. package/dist/timeseries/index.d.ts +68 -0
  23. package/dist/timeseries/index.js +26 -0
  24. package/dist/timeseries/tool.d.ts +71 -0
  25. package/dist/timeseries/tool.js +170 -0
  26. package/dist/timeseries/types.d.ts +147 -0
  27. package/dist/timeseries/types.js +4 -0
  28. package/dist/types/index.d.ts +0 -21
  29. package/dist/utils/filename.d.ts +0 -8
  30. package/dist/utils/filename.js +0 -10
  31. package/dist/utils/jq.d.ts +25 -0
  32. package/dist/utils/jq.js +90 -0
  33. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Moving Average based anomaly detection
3
+ *
4
+ * Detects points that deviate significantly from a rolling moving average.
5
+ * Good for detecting sudden changes in time series with trends.
6
+ */
7
+ import type { DataPoint, Anomaly, MovingAverageParams } from '../types.js';
8
+ /**
9
+ * Detect anomalies using Moving Average method
10
+ *
11
+ * @param data - Array of data points (should be in time order)
12
+ * @param params - Moving Average parameters
13
+ * @returns Array of detected anomalies
14
+ */
15
+ export declare function detectMovingAverageAnomalies(data: DataPoint[], params: MovingAverageParams): Anomaly[];
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Moving Average based anomaly detection
3
+ *
4
+ * Detects points that deviate significantly from a rolling moving average.
5
+ * Good for detecting sudden changes in time series with trends.
6
+ */
7
+ import { movingAverage, movingStandardDeviation } from './stats.js';
8
+ const DEFAULT_THRESHOLD = 2;
9
+ /**
10
+ * Detect anomalies using Moving Average method
11
+ *
12
+ * @param data - Array of data points (should be in time order)
13
+ * @param params - Moving Average parameters
14
+ * @returns Array of detected anomalies
15
+ */
16
+ export function detectMovingAverageAnomalies(data, params) {
17
+ const { window_size: windowSize, threshold = DEFAULT_THRESHOLD } = params;
18
+ if (windowSize <= 0) {
19
+ throw new Error('window_size must be greater than 0');
20
+ }
21
+ if (windowSize > data.length) {
22
+ throw new Error(`window_size (${windowSize}) cannot be larger than data length (${data.length})`);
23
+ }
24
+ const values = data.map((d) => d.value);
25
+ const ma = movingAverage(values, windowSize);
26
+ const mstd = movingStandardDeviation(values, windowSize);
27
+ const anomalies = [];
28
+ for (let i = 0; i < data.length; i++) {
29
+ // Skip points where we don't have enough data for the window
30
+ if (isNaN(ma[i]) || isNaN(mstd[i])) {
31
+ continue;
32
+ }
33
+ const point = data[i];
34
+ const expectedValue = ma[i];
35
+ const stdDev = mstd[i];
36
+ // If standard deviation is 0, all values in window are the same
37
+ if (stdDev === 0) {
38
+ // Only flag as anomaly if current value differs from the constant
39
+ if (point.value !== expectedValue) {
40
+ anomalies.push({
41
+ index: point.index,
42
+ timestamp: point.timestamp,
43
+ value: point.value,
44
+ reason: `Value ${point.value} differs from constant window value ${expectedValue}`,
45
+ metadata: {
46
+ moving_average: expectedValue,
47
+ moving_std_dev: stdDev,
48
+ window_size: windowSize,
49
+ },
50
+ });
51
+ }
52
+ continue;
53
+ }
54
+ const deviation = Math.abs(point.value - expectedValue);
55
+ const deviationScore = deviation / stdDev;
56
+ if (deviationScore > threshold) {
57
+ anomalies.push({
58
+ index: point.index,
59
+ timestamp: point.timestamp,
60
+ value: point.value,
61
+ reason: `Value deviates ${deviationScore.toFixed(2)} std devs from moving average (expected: ${expectedValue.toFixed(2)}, threshold: ${threshold})`,
62
+ metadata: {
63
+ moving_average: expectedValue,
64
+ moving_std_dev: stdDev,
65
+ deviation_score: deviationScore,
66
+ window_size: windowSize,
67
+ },
68
+ });
69
+ }
70
+ }
71
+ return anomalies;
72
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Rolling Quantile based anomaly detection (similar to Datadog's Basic algorithm)
3
+ *
4
+ * Uses a sliding window to compute rolling percentiles, then flags points
5
+ * outside the expected range. This is non-parametric and works well for
6
+ * any distribution without assuming normality.
7
+ */
8
+ import type { DataPoint, Anomaly, RollingQuantileParams } from '../types.js';
9
+ /**
10
+ * Detect anomalies using Rolling Quantile method
11
+ *
12
+ * @param data - Array of data points (should be in time order)
13
+ * @param params - Rolling Quantile parameters
14
+ * @returns Array of detected anomalies
15
+ */
16
+ export declare function detectRollingQuantileAnomalies(data: DataPoint[], params: RollingQuantileParams): Anomaly[];
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Rolling Quantile based anomaly detection (similar to Datadog's Basic algorithm)
3
+ *
4
+ * Uses a sliding window to compute rolling percentiles, then flags points
5
+ * outside the expected range. This is non-parametric and works well for
6
+ * any distribution without assuming normality.
7
+ */
8
+ import { rollingQuantiles } from './stats.js';
9
+ const DEFAULT_LOWER_QUANTILE = 0.05;
10
+ const DEFAULT_UPPER_QUANTILE = 0.95;
11
+ /**
12
+ * Detect anomalies using Rolling Quantile method
13
+ *
14
+ * @param data - Array of data points (should be in time order)
15
+ * @param params - Rolling Quantile parameters
16
+ * @returns Array of detected anomalies
17
+ */
18
+ export function detectRollingQuantileAnomalies(data, params) {
19
+ const { window_size: windowSize, lower_quantile: lowerQuantile = DEFAULT_LOWER_QUANTILE, upper_quantile: upperQuantile = DEFAULT_UPPER_QUANTILE, } = params;
20
+ if (windowSize <= 0) {
21
+ throw new Error('window_size must be greater than 0');
22
+ }
23
+ if (windowSize > data.length) {
24
+ throw new Error(`window_size (${windowSize}) cannot be larger than data length (${data.length})`);
25
+ }
26
+ if (lowerQuantile < 0 || lowerQuantile > 1) {
27
+ throw new Error('lower_quantile must be between 0 and 1');
28
+ }
29
+ if (upperQuantile < 0 || upperQuantile > 1) {
30
+ throw new Error('upper_quantile must be between 0 and 1');
31
+ }
32
+ if (lowerQuantile >= upperQuantile) {
33
+ throw new Error('lower_quantile must be less than upper_quantile');
34
+ }
35
+ const values = data.map((d) => d.value);
36
+ const { lower, upper } = rollingQuantiles(values, windowSize, lowerQuantile, upperQuantile);
37
+ const anomalies = [];
38
+ for (let i = 0; i < data.length; i++) {
39
+ // Skip points where we don't have enough data for the window
40
+ if (isNaN(lower[i]) || isNaN(upper[i])) {
41
+ continue;
42
+ }
43
+ const point = data[i];
44
+ const lowerBound = lower[i];
45
+ const upperBound = upper[i];
46
+ if (point.value < lowerBound) {
47
+ anomalies.push({
48
+ index: point.index,
49
+ timestamp: point.timestamp,
50
+ value: point.value,
51
+ reason: `Value ${point.value.toFixed(2)} below rolling ${(lowerQuantile * 100).toFixed(0)}th percentile ${lowerBound.toFixed(2)}`,
52
+ metadata: {
53
+ lower_bound: lowerBound,
54
+ upper_bound: upperBound,
55
+ lower_quantile: lowerQuantile,
56
+ upper_quantile: upperQuantile,
57
+ window_size: windowSize,
58
+ },
59
+ });
60
+ }
61
+ else if (point.value > upperBound) {
62
+ anomalies.push({
63
+ index: point.index,
64
+ timestamp: point.timestamp,
65
+ value: point.value,
66
+ reason: `Value ${point.value.toFixed(2)} above rolling ${(upperQuantile * 100).toFixed(0)}th percentile ${upperBound.toFixed(2)}`,
67
+ metadata: {
68
+ lower_bound: lowerBound,
69
+ upper_bound: upperBound,
70
+ lower_quantile: lowerQuantile,
71
+ upper_quantile: upperQuantile,
72
+ window_size: windowSize,
73
+ },
74
+ });
75
+ }
76
+ }
77
+ return anomalies;
78
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Statistical utility functions for anomaly detection
3
+ * Implemented in pure TypeScript for zero dependencies and fast execution
4
+ */
5
+ /**
6
+ * Calculate the mean (average) of an array of numbers
7
+ */
8
+ export declare function mean(values: number[]): number;
9
+ /**
10
+ * Calculate the median of an array of numbers
11
+ */
12
+ export declare function median(values: number[]): number;
13
+ /**
14
+ * Calculate the standard deviation of an array of numbers
15
+ */
16
+ export declare function standardDeviation(values: number[]): number;
17
+ /**
18
+ * Calculate the Median Absolute Deviation (MAD)
19
+ * MAD = median(|Xi - median(X)|)
20
+ */
21
+ export declare function medianAbsoluteDeviation(values: number[]): number;
22
+ /**
23
+ * Calculate modified Z-score using MAD
24
+ * Modified Z-score = 0.6745 * (Xi - median) / MAD
25
+ * The constant 0.6745 makes it comparable to standard Z-scores for normal distributions
26
+ */
27
+ export declare function modifiedZScore(value: number, med: number, mad: number): number;
28
+ /**
29
+ * Calculate a simple moving average for an array at each position
30
+ * Returns an array of the same length, with NaN for positions where window is incomplete
31
+ */
32
+ export declare function movingAverage(values: number[], windowSize: number): number[];
33
+ /**
34
+ * Calculate the standard deviation of a moving window at each position
35
+ */
36
+ export declare function movingStandardDeviation(values: number[], windowSize: number): number[];
37
+ /**
38
+ * Calculate a specific quantile (percentile) of an array
39
+ * Uses linear interpolation between closest ranks
40
+ */
41
+ export declare function quantile(values: number[], q: number): number;
42
+ /**
43
+ * Calculate rolling quantiles (lower and upper bounds) at each position
44
+ * Returns arrays of the same length as input, with NaN for positions where window is incomplete
45
+ */
46
+ export declare function rollingQuantiles(values: number[], windowSize: number, lowerQuantile: number, upperQuantile: number): {
47
+ lower: number[];
48
+ upper: number[];
49
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Statistical utility functions for anomaly detection
3
+ * Implemented in pure TypeScript for zero dependencies and fast execution
4
+ */
5
+ /**
6
+ * Calculate the mean (average) of an array of numbers
7
+ */
8
+ export function mean(values) {
9
+ if (values.length === 0)
10
+ return 0;
11
+ return values.reduce((sum, v) => sum + v, 0) / values.length;
12
+ }
13
+ /**
14
+ * Calculate the median of an array of numbers
15
+ */
16
+ export function median(values) {
17
+ if (values.length === 0)
18
+ return 0;
19
+ const sorted = [...values].sort((a, b) => a - b);
20
+ const mid = Math.floor(sorted.length / 2);
21
+ return sorted.length % 2 !== 0
22
+ ? sorted[mid]
23
+ : (sorted[mid - 1] + sorted[mid]) / 2;
24
+ }
25
+ /**
26
+ * Calculate the standard deviation of an array of numbers
27
+ */
28
+ export function standardDeviation(values) {
29
+ if (values.length < 2)
30
+ return 0;
31
+ const avg = mean(values);
32
+ const squaredDiffs = values.map((v) => Math.pow(v - avg, 2));
33
+ return Math.sqrt(mean(squaredDiffs));
34
+ }
35
+ /**
36
+ * Calculate the Median Absolute Deviation (MAD)
37
+ * MAD = median(|Xi - median(X)|)
38
+ */
39
+ export function medianAbsoluteDeviation(values) {
40
+ if (values.length === 0)
41
+ return 0;
42
+ const med = median(values);
43
+ const absoluteDeviations = values.map((v) => Math.abs(v - med));
44
+ return median(absoluteDeviations);
45
+ }
46
+ /**
47
+ * Calculate modified Z-score using MAD
48
+ * Modified Z-score = 0.6745 * (Xi - median) / MAD
49
+ * The constant 0.6745 makes it comparable to standard Z-scores for normal distributions
50
+ */
51
+ export function modifiedZScore(value, med, mad) {
52
+ if (mad === 0)
53
+ return 0;
54
+ return (0.6745 * (value - med)) / mad;
55
+ }
56
+ /**
57
+ * Calculate a simple moving average for an array at each position
58
+ * Returns an array of the same length, with NaN for positions where window is incomplete
59
+ */
60
+ export function movingAverage(values, windowSize) {
61
+ if (windowSize <= 0 || values.length === 0)
62
+ return [];
63
+ const result = [];
64
+ for (let i = 0; i < values.length; i++) {
65
+ if (i < windowSize - 1) {
66
+ // Not enough data points yet
67
+ result.push(NaN);
68
+ }
69
+ else {
70
+ // Calculate average of the window
71
+ const window = values.slice(i - windowSize + 1, i + 1);
72
+ result.push(mean(window));
73
+ }
74
+ }
75
+ return result;
76
+ }
77
+ /**
78
+ * Calculate the standard deviation of a moving window at each position
79
+ */
80
+ export function movingStandardDeviation(values, windowSize) {
81
+ if (windowSize <= 0 || values.length === 0)
82
+ return [];
83
+ const result = [];
84
+ for (let i = 0; i < values.length; i++) {
85
+ if (i < windowSize - 1) {
86
+ result.push(NaN);
87
+ }
88
+ else {
89
+ const window = values.slice(i - windowSize + 1, i + 1);
90
+ result.push(standardDeviation(window));
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+ /**
96
+ * Calculate a specific quantile (percentile) of an array
97
+ * Uses linear interpolation between closest ranks
98
+ */
99
+ export function quantile(values, q) {
100
+ if (values.length === 0)
101
+ return 0;
102
+ if (q <= 0)
103
+ return Math.min(...values);
104
+ if (q >= 1)
105
+ return Math.max(...values);
106
+ const sorted = [...values].sort((a, b) => a - b);
107
+ const pos = (sorted.length - 1) * q;
108
+ const lower = Math.floor(pos);
109
+ const upper = Math.ceil(pos);
110
+ const weight = pos - lower;
111
+ if (upper >= sorted.length)
112
+ return sorted[sorted.length - 1];
113
+ return sorted[lower] * (1 - weight) + sorted[upper] * weight;
114
+ }
115
+ /**
116
+ * Calculate rolling quantiles (lower and upper bounds) at each position
117
+ * Returns arrays of the same length as input, with NaN for positions where window is incomplete
118
+ */
119
+ export function rollingQuantiles(values, windowSize, lowerQuantile, upperQuantile) {
120
+ if (windowSize <= 0 || values.length === 0) {
121
+ return { lower: [], upper: [] };
122
+ }
123
+ const lower = [];
124
+ const upper = [];
125
+ for (let i = 0; i < values.length; i++) {
126
+ if (i < windowSize - 1) {
127
+ // Not enough data points yet
128
+ lower.push(NaN);
129
+ upper.push(NaN);
130
+ }
131
+ else {
132
+ // Calculate quantiles of the window
133
+ const window = values.slice(i - windowSize + 1, i + 1);
134
+ lower.push(quantile(window, lowerQuantile));
135
+ upper.push(quantile(window, upperQuantile));
136
+ }
137
+ }
138
+ return { lower, upper };
139
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Threshold-based anomaly detection
3
+ *
4
+ * Detects points that cross one or more named thresholds.
5
+ * Supports multiple thresholds with names and directions (upper/lower).
6
+ */
7
+ import type { DataPoint, Anomaly, ThresholdParams } from '../types.js';
8
+ /**
9
+ * Detect anomalies using threshold method
10
+ *
11
+ * @param data - Array of data points
12
+ * @param params - Threshold parameters with named thresholds
13
+ * @returns Array of detected anomalies
14
+ */
15
+ export declare function detectThresholdAnomalies(data: DataPoint[], params: ThresholdParams): Anomaly[];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Threshold-based anomaly detection
3
+ *
4
+ * Detects points that cross one or more named thresholds.
5
+ * Supports multiple thresholds with names and directions (upper/lower).
6
+ */
7
+ /**
8
+ * Detect anomalies using threshold method
9
+ *
10
+ * @param data - Array of data points
11
+ * @param params - Threshold parameters with named thresholds
12
+ * @returns Array of detected anomalies
13
+ */
14
+ export function detectThresholdAnomalies(data, params) {
15
+ const { thresholds } = params;
16
+ if (!thresholds || thresholds.length === 0) {
17
+ throw new Error('At least one threshold must be specified');
18
+ }
19
+ const anomalies = [];
20
+ for (const point of data) {
21
+ const crossedThresholds = [];
22
+ for (const threshold of thresholds) {
23
+ if (threshold.direction === 'upper' && point.value > threshold.value) {
24
+ crossedThresholds.push(threshold);
25
+ }
26
+ else if (threshold.direction === 'lower' && point.value < threshold.value) {
27
+ crossedThresholds.push(threshold);
28
+ }
29
+ }
30
+ if (crossedThresholds.length > 0) {
31
+ const thresholdNames = crossedThresholds.map((t) => t.name);
32
+ const thresholdDetails = crossedThresholds
33
+ .map((t) => `${t.name} (${t.direction}: ${t.value})`)
34
+ .join(', ');
35
+ anomalies.push({
36
+ index: point.index,
37
+ timestamp: point.timestamp,
38
+ value: point.value,
39
+ reason: `Value ${point.value} crossed threshold(s): ${thresholdDetails}`,
40
+ crossed_thresholds: thresholdNames,
41
+ metadata: crossedThresholds.reduce((acc, t) => {
42
+ acc[`threshold_${t.name}`] = t.value;
43
+ return acc;
44
+ }, {}),
45
+ });
46
+ }
47
+ }
48
+ return anomalies;
49
+ }
@@ -0,0 +1,10 @@
1
+ import type { TimeseriesConfig, DetectionType, DetectionParams } from './types.js';
2
+ /**
3
+ * Main handler for time series anomaly detection
4
+ */
5
+ export declare function detectTimeseriesAnomalies(config: TimeseriesConfig, filePath: string, jqQuery: string, valueField: string, detectionType: DetectionType, detectionParams?: DetectionParams, timestampField?: string): Promise<{
6
+ content: Array<{
7
+ type: 'text';
8
+ text: string;
9
+ }>;
10
+ }>;