@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.
- package/dist/__tests__/unit/timeseries.test.d.ts +1 -0
- package/dist/__tests__/unit/timeseries.test.js +217 -0
- package/dist/fileWriter/writer.js +1 -1
- package/dist/index.js +75 -29
- package/dist/jq/handler.js +11 -56
- package/dist/jq/tool.d.ts +3 -9
- package/dist/jq/tool.js +4 -4
- package/dist/timeseries/algorithms/index.d.ts +8 -0
- package/dist/timeseries/algorithms/index.js +8 -0
- package/dist/timeseries/algorithms/mad.d.ts +15 -0
- package/dist/timeseries/algorithms/mad.js +44 -0
- package/dist/timeseries/algorithms/moving-average.d.ts +15 -0
- package/dist/timeseries/algorithms/moving-average.js +72 -0
- package/dist/timeseries/algorithms/rolling-quantile.d.ts +16 -0
- package/dist/timeseries/algorithms/rolling-quantile.js +78 -0
- package/dist/timeseries/algorithms/stats.d.ts +49 -0
- package/dist/timeseries/algorithms/stats.js +139 -0
- package/dist/timeseries/algorithms/threshold.d.ts +15 -0
- package/dist/timeseries/algorithms/threshold.js +49 -0
- package/dist/timeseries/handler.d.ts +10 -0
- package/dist/timeseries/handler.js +292 -0
- package/dist/timeseries/index.d.ts +68 -0
- package/dist/timeseries/index.js +26 -0
- package/dist/timeseries/tool.d.ts +71 -0
- package/dist/timeseries/tool.js +170 -0
- package/dist/timeseries/types.d.ts +147 -0
- package/dist/timeseries/types.js +4 -0
- package/dist/types/index.d.ts +0 -21
- package/dist/utils/filename.d.ts +0 -8
- package/dist/utils/filename.js +0 -10
- package/dist/utils/jq.d.ts +25 -0
- package/dist/utils/jq.js +90 -0
- package/package.json +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Series Anomaly Detection Types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* A single data point in the time series
|
|
6
|
+
*/
|
|
7
|
+
export interface DataPoint {
|
|
8
|
+
/** Index in the original array */
|
|
9
|
+
index: number;
|
|
10
|
+
/** Timestamp value (if available) */
|
|
11
|
+
timestamp?: string | number;
|
|
12
|
+
/** The numeric value */
|
|
13
|
+
value: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Threshold direction for threshold-based detection
|
|
17
|
+
*/
|
|
18
|
+
export type ThresholdDirection = 'upper' | 'lower';
|
|
19
|
+
/**
|
|
20
|
+
* A named threshold with direction
|
|
21
|
+
*/
|
|
22
|
+
export interface NamedThreshold {
|
|
23
|
+
/** Name/label for this threshold */
|
|
24
|
+
name: string;
|
|
25
|
+
/** The threshold value */
|
|
26
|
+
value: number;
|
|
27
|
+
/** Whether this is an upper or lower bound */
|
|
28
|
+
direction: ThresholdDirection;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Detection types supported by the tool
|
|
32
|
+
*/
|
|
33
|
+
export type DetectionType = 'rolling_quantile' | 'mad' | 'moving_average' | 'threshold';
|
|
34
|
+
/**
|
|
35
|
+
* Parameters for Rolling Quantile detection (similar to Datadog Basic)
|
|
36
|
+
*/
|
|
37
|
+
export interface RollingQuantileParams {
|
|
38
|
+
/** Window size for the rolling quantile calculation */
|
|
39
|
+
window_size: number;
|
|
40
|
+
/** Lower quantile for the expected range (default: 0.05 = 5th percentile) */
|
|
41
|
+
lower_quantile?: number;
|
|
42
|
+
/** Upper quantile for the expected range (default: 0.95 = 95th percentile) */
|
|
43
|
+
upper_quantile?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parameters for MAD (Median Absolute Deviation) detection
|
|
47
|
+
*/
|
|
48
|
+
export interface MadParams {
|
|
49
|
+
/** Multiplier for MAD threshold (default: 3) */
|
|
50
|
+
threshold?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parameters for Moving Average detection
|
|
54
|
+
*/
|
|
55
|
+
export interface MovingAverageParams {
|
|
56
|
+
/** Window size for the moving average */
|
|
57
|
+
window_size: number;
|
|
58
|
+
/** Number of standard deviations from moving average (default: 2) */
|
|
59
|
+
threshold?: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Parameters for Threshold detection
|
|
63
|
+
*/
|
|
64
|
+
export interface ThresholdParams {
|
|
65
|
+
/** Named thresholds with direction */
|
|
66
|
+
thresholds: NamedThreshold[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Union type for all detection parameters
|
|
70
|
+
*/
|
|
71
|
+
export type DetectionParams = RollingQuantileParams | MadParams | MovingAverageParams | ThresholdParams;
|
|
72
|
+
/**
|
|
73
|
+
* A detected anomaly (individual point)
|
|
74
|
+
*/
|
|
75
|
+
export interface Anomaly {
|
|
76
|
+
/** Index in the original data */
|
|
77
|
+
index: number;
|
|
78
|
+
/** Timestamp if available (raw value from data) */
|
|
79
|
+
timestamp?: string | number;
|
|
80
|
+
/** UTC time string if timestamp was a Unix timestamp */
|
|
81
|
+
utc_time?: string;
|
|
82
|
+
/** The anomalous value */
|
|
83
|
+
value: number;
|
|
84
|
+
/** Reason/description of why this is an anomaly */
|
|
85
|
+
reason: string;
|
|
86
|
+
/** For threshold detection: which thresholds were crossed */
|
|
87
|
+
crossed_thresholds?: string[];
|
|
88
|
+
/** Additional metadata (e.g., z-score value, percent change) */
|
|
89
|
+
metadata?: Record<string, number>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A pooled anomaly (consecutive anomalies grouped together)
|
|
93
|
+
*/
|
|
94
|
+
export interface PooledAnomaly {
|
|
95
|
+
/** Start index in the original data */
|
|
96
|
+
start_index: number;
|
|
97
|
+
/** End index in the original data (inclusive) */
|
|
98
|
+
end_index: number;
|
|
99
|
+
/** Number of anomalous points in this pool */
|
|
100
|
+
count: number;
|
|
101
|
+
/** Start timestamp if available */
|
|
102
|
+
start_timestamp?: string | number;
|
|
103
|
+
/** End timestamp if available */
|
|
104
|
+
end_timestamp?: string | number;
|
|
105
|
+
/** Start UTC time string */
|
|
106
|
+
start_utc?: string;
|
|
107
|
+
/** End UTC time string */
|
|
108
|
+
end_utc?: string;
|
|
109
|
+
/** Minimum value in the pool */
|
|
110
|
+
min_value: number;
|
|
111
|
+
/** Maximum value in the pool */
|
|
112
|
+
max_value: number;
|
|
113
|
+
/** Average value in the pool */
|
|
114
|
+
avg_value: number;
|
|
115
|
+
/** Summary reason for the pool */
|
|
116
|
+
reason: string;
|
|
117
|
+
/** For threshold detection: union of all crossed thresholds */
|
|
118
|
+
crossed_thresholds?: string[];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Result of anomaly detection
|
|
122
|
+
*/
|
|
123
|
+
export interface AnomalyDetectionResult {
|
|
124
|
+
/** Total number of data points analyzed */
|
|
125
|
+
total_points: number;
|
|
126
|
+
/** Number of anomalous points detected */
|
|
127
|
+
anomaly_count: number;
|
|
128
|
+
/** Percentage of points that are anomalies */
|
|
129
|
+
anomaly_percentage: number;
|
|
130
|
+
/** Number of anomaly pools (consecutive anomalies grouped) */
|
|
131
|
+
pool_count: number;
|
|
132
|
+
/** List of pooled anomalies (consecutive anomalies grouped together) */
|
|
133
|
+
anomalies: PooledAnomaly[];
|
|
134
|
+
/** Detection method used */
|
|
135
|
+
detection_type: DetectionType;
|
|
136
|
+
/** Parameters used for detection */
|
|
137
|
+
parameters: DetectionParams;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Configuration for the timeseries anomaly detection tool
|
|
141
|
+
*/
|
|
142
|
+
export interface TimeseriesConfig {
|
|
143
|
+
/** Paths where the tool is allowed to read files */
|
|
144
|
+
allowedPaths: string[];
|
|
145
|
+
/** Timeout for jq query execution in milliseconds (default: 30000) */
|
|
146
|
+
timeoutMs?: number;
|
|
147
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -53,27 +53,6 @@ export interface FileWriterResult {
|
|
|
53
53
|
text: string;
|
|
54
54
|
}>;
|
|
55
55
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Schema analysis result for a JSON structure
|
|
58
|
-
*/
|
|
59
|
-
export interface JsonSchema {
|
|
60
|
-
type: string;
|
|
61
|
-
properties?: Record<string, unknown>;
|
|
62
|
-
items?: unknown;
|
|
63
|
-
length?: number;
|
|
64
|
-
_hasNulls?: boolean;
|
|
65
|
-
_keysAreNumeric?: boolean;
|
|
66
|
-
_accessPattern?: string;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Nullable fields extracted from schema
|
|
70
|
-
*/
|
|
71
|
-
export interface NullableFields {
|
|
72
|
-
/** Fields that are always null */
|
|
73
|
-
alwaysNull: string[];
|
|
74
|
-
/** Fields that can be null (mixed types) */
|
|
75
|
-
nullable: string[];
|
|
76
|
-
}
|
|
77
56
|
/**
|
|
78
57
|
* Unified response format for all tool calls
|
|
79
58
|
* Provides a consistent structure for LLM consumption
|
package/dist/utils/filename.d.ts
CHANGED
|
@@ -6,11 +6,3 @@
|
|
|
6
6
|
* @returns Tool ID like "1697834567123_met_qry_a3b4c5"
|
|
7
7
|
*/
|
|
8
8
|
export declare const generateToolId: (toolName: string, args: Record<string, unknown>, toolAbbreviations?: Record<string, string>) => string;
|
|
9
|
-
/**
|
|
10
|
-
* Generate LLM-friendly compact filename
|
|
11
|
-
* @param toolName - Name of the tool that generated the data
|
|
12
|
-
* @param args - Arguments passed to the tool
|
|
13
|
-
* @param toolAbbreviations - Optional custom abbreviations for tool names
|
|
14
|
-
* @returns Compact filename like "1697834567123_met_qry_a3b4c5.json"
|
|
15
|
-
*/
|
|
16
|
-
export declare const generateCompactFilename: (toolName: string, args: Record<string, unknown>, toolAbbreviations?: Record<string, string>) => string;
|
package/dist/utils/filename.js
CHANGED
|
@@ -40,13 +40,3 @@ export const generateToolId = (toolName, args, toolAbbreviations) => {
|
|
|
40
40
|
const argsHash = hashArgs(args);
|
|
41
41
|
return `${timestamp}_${toolAbbrev}_${argsHash}`;
|
|
42
42
|
};
|
|
43
|
-
/**
|
|
44
|
-
* Generate LLM-friendly compact filename
|
|
45
|
-
* @param toolName - Name of the tool that generated the data
|
|
46
|
-
* @param args - Arguments passed to the tool
|
|
47
|
-
* @param toolAbbreviations - Optional custom abbreviations for tool names
|
|
48
|
-
* @returns Compact filename like "1697834567123_met_qry_a3b4c5.json"
|
|
49
|
-
*/
|
|
50
|
-
export const generateCompactFilename = (toolName, args, toolAbbreviations) => {
|
|
51
|
-
return `${generateToolId(toolName, args, toolAbbreviations)}.json`;
|
|
52
|
-
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dangerous patterns that could access environment variables or system info in jq queries
|
|
3
|
+
*/
|
|
4
|
+
export declare const DANGEROUS_JQ_PATTERNS: RegExp[];
|
|
5
|
+
/**
|
|
6
|
+
* Validate a jq query for dangerous patterns
|
|
7
|
+
* @throws Error if the query contains dangerous patterns
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateJqQuery(jqQuery: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Execute a jq query and return the raw stdout string
|
|
12
|
+
* @param jqQuery - The jq query to execute
|
|
13
|
+
* @param filePath - Path to the JSON file
|
|
14
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
15
|
+
* @returns Raw stdout string from jq
|
|
16
|
+
*/
|
|
17
|
+
export declare function runJq(jqQuery: string, filePath: string, timeoutMs: number): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Execute a jq query and return the parsed JSON result
|
|
20
|
+
* @param jqQuery - The jq query to execute
|
|
21
|
+
* @param filePath - Path to the JSON file
|
|
22
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
23
|
+
* @returns Parsed JSON result
|
|
24
|
+
*/
|
|
25
|
+
export declare function runJqParsed(jqQuery: string, filePath: string, timeoutMs: number): Promise<unknown>;
|
package/dist/utils/jq.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Dangerous patterns that could access environment variables or system info in jq queries
|
|
4
|
+
*/
|
|
5
|
+
export const DANGEROUS_JQ_PATTERNS = [
|
|
6
|
+
/\$ENV/i, // $ENV variable access
|
|
7
|
+
/env\./i, // env.VARIABLE access
|
|
8
|
+
/@env/i, // @env function
|
|
9
|
+
/\.env\[/i, // .env["VARIABLE"] access
|
|
10
|
+
/getenv/i, // getenv function
|
|
11
|
+
/\$__loc__/i, // location info that might leak paths
|
|
12
|
+
/input_filename/i, // input filename access
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Validate a jq query for dangerous patterns
|
|
16
|
+
* @throws Error if the query contains dangerous patterns
|
|
17
|
+
*/
|
|
18
|
+
export function validateJqQuery(jqQuery) {
|
|
19
|
+
const isDangerous = DANGEROUS_JQ_PATTERNS.some((pattern) => pattern.test(jqQuery));
|
|
20
|
+
if (isDangerous) {
|
|
21
|
+
throw new Error('The jq query contains patterns that could access environment variables or system information. Please use a different query.');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Execute a jq query and return the raw stdout string
|
|
26
|
+
* @param jqQuery - The jq query to execute
|
|
27
|
+
* @param filePath - Path to the JSON file
|
|
28
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
29
|
+
* @returns Raw stdout string from jq
|
|
30
|
+
*/
|
|
31
|
+
export async function runJq(jqQuery, filePath, timeoutMs) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
let settled = false;
|
|
34
|
+
const jqProcess = spawn('jq', [jqQuery, filePath], {
|
|
35
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
+
timeout: timeoutMs,
|
|
37
|
+
});
|
|
38
|
+
let stdout = '';
|
|
39
|
+
let stderr = '';
|
|
40
|
+
jqProcess.stdout.on('data', (data) => {
|
|
41
|
+
stdout += data.toString();
|
|
42
|
+
});
|
|
43
|
+
jqProcess.stderr.on('data', (data) => {
|
|
44
|
+
stderr += data.toString();
|
|
45
|
+
});
|
|
46
|
+
// Handle timeout
|
|
47
|
+
const timeoutId = setTimeout(() => {
|
|
48
|
+
if (!settled && !jqProcess.killed) {
|
|
49
|
+
settled = true;
|
|
50
|
+
jqProcess.kill('SIGTERM');
|
|
51
|
+
reject(new Error(`jq command timed out after ${timeoutMs}ms`));
|
|
52
|
+
}
|
|
53
|
+
}, timeoutMs);
|
|
54
|
+
jqProcess.on('close', (code) => {
|
|
55
|
+
clearTimeout(timeoutId);
|
|
56
|
+
if (settled)
|
|
57
|
+
return;
|
|
58
|
+
settled = true;
|
|
59
|
+
if (code === 0) {
|
|
60
|
+
resolve(stdout.trim());
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
reject(new Error(`jq command failed with exit code ${code}: ${stderr.trim()}`));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
jqProcess.on('error', (error) => {
|
|
67
|
+
clearTimeout(timeoutId);
|
|
68
|
+
if (settled)
|
|
69
|
+
return;
|
|
70
|
+
settled = true;
|
|
71
|
+
reject(new Error(`Failed to execute jq command: ${error.message}`));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Execute a jq query and return the parsed JSON result
|
|
77
|
+
* @param jqQuery - The jq query to execute
|
|
78
|
+
* @param filePath - Path to the JSON file
|
|
79
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
80
|
+
* @returns Parsed JSON result
|
|
81
|
+
*/
|
|
82
|
+
export async function runJqParsed(jqQuery, filePath, timeoutMs) {
|
|
83
|
+
const stdout = await runJq(jqQuery, filePath, timeoutMs);
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(stdout);
|
|
86
|
+
}
|
|
87
|
+
catch (parseError) {
|
|
88
|
+
throw new Error(`Failed to parse jq output as JSON: ${stdout.slice(0, 200)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|