@slbdn/mcp-tester 1.1.0 → 1.3.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/CHANGELOG.md +103 -0
- package/README.md +278 -7
- package/dist/cli/index.js +114 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/MCPClient.d.ts +86 -0
- package/dist/client/MCPClient.d.ts.map +1 -1
- package/dist/client/MCPClient.js +198 -53
- package/dist/client/MCPClient.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/errors.d.ts +43 -5
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +86 -7
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/logger.d.ts +51 -24
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +103 -9
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/masking.d.ts +66 -0
- package/dist/utils/masking.d.ts.map +1 -0
- package/dist/utils/masking.js +171 -0
- package/dist/utils/masking.js.map +1 -0
- package/dist/utils/validation.d.ts +48 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +249 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +10 -6
package/dist/utils/logger.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logging utilities for MCP client.
|
|
3
3
|
*
|
|
4
|
-
* Provides configurable logging with multiple output levels
|
|
4
|
+
* Provides configurable logging with multiple output levels, secret masking,
|
|
5
|
+
* request timing, and JSON pretty-printing for protocol debugging.
|
|
6
|
+
*
|
|
7
|
+
* All log output is automatically masked to prevent sensitive data leakage.
|
|
5
8
|
*/
|
|
9
|
+
import { maskSecrets } from './masking.js';
|
|
6
10
|
const LOG_LEVELS = {
|
|
7
11
|
debug: 0,
|
|
8
12
|
info: 1,
|
|
@@ -11,27 +15,117 @@ const LOG_LEVELS = {
|
|
|
11
15
|
none: 99,
|
|
12
16
|
};
|
|
13
17
|
/**
|
|
14
|
-
*
|
|
18
|
+
* ANSI color codes for terminal output.
|
|
19
|
+
* Only used when output is a TTY.
|
|
20
|
+
*/
|
|
21
|
+
const COLORS = {
|
|
22
|
+
debug: '\x1b[36m', // cyan
|
|
23
|
+
info: '\x1b[32m', // green
|
|
24
|
+
warn: '\x1b[33m', // yellow
|
|
25
|
+
error: '\x1b[31m', // red
|
|
26
|
+
dim: '\x1b[2m', // dim
|
|
27
|
+
reset: '\x1b[0m', // reset
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Check if stderr is a TTY (terminal).
|
|
31
|
+
*/
|
|
32
|
+
function isTTY() {
|
|
33
|
+
return process.stderr?.isTTY ?? false;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Colorize a string for terminal output.
|
|
37
|
+
*/
|
|
38
|
+
function colorize(text, color, useColors) {
|
|
39
|
+
if (!useColors || !isTTY())
|
|
40
|
+
return text;
|
|
41
|
+
return `${color}${text}${COLORS.reset}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Format a timestamp for log messages.
|
|
45
|
+
*/
|
|
46
|
+
function formatTimestamp() {
|
|
47
|
+
const now = new Date();
|
|
48
|
+
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
|
49
|
+
return `${now.toISOString().slice(11, 23)}.${ms}`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pretty-print a JSON-serializable value with indentation and masking.
|
|
53
|
+
*/
|
|
54
|
+
export function prettyPrint(value, shouldMask = true) {
|
|
55
|
+
if (value === null || value === undefined)
|
|
56
|
+
return String(value);
|
|
57
|
+
if (typeof value === 'string') {
|
|
58
|
+
return shouldMask ? maskSecrets(value) : value;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const json = JSON.stringify(value, null, 2);
|
|
62
|
+
return shouldMask ? maskSecrets(json) : json;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
const str = String(value);
|
|
66
|
+
return shouldMask ? maskSecrets(str) : str;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Start a timer and return a function that returns elapsed milliseconds.
|
|
15
71
|
*
|
|
16
72
|
* @example
|
|
17
73
|
* ```typescript
|
|
18
|
-
* const
|
|
19
|
-
*
|
|
74
|
+
* const elapsed = startTimer();
|
|
75
|
+
* await someOperation();
|
|
76
|
+
* logger.debug(`Operation completed in ${elapsed()}ms`);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function startTimer() {
|
|
80
|
+
const start = performance.now();
|
|
81
|
+
return () => Math.round(performance.now() - start);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Mask all arguments in an array by converting to strings and masking secrets.
|
|
85
|
+
*/
|
|
86
|
+
function maskArgs(args, shouldMask) {
|
|
87
|
+
if (!shouldMask)
|
|
88
|
+
return args;
|
|
89
|
+
return args.map((arg) => (typeof arg === 'string' ? maskSecrets(arg) : maskSecrets(String(arg))));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Logger implementation that outputs to stderr with automatic secret masking,
|
|
93
|
+
* timestamps, colors, and JSON pretty-printing.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const logger = new ConsoleLogger({ level: 'debug', prefix: 'MCPClient' });
|
|
98
|
+
* logger.info('Starting...'); // [MCPClient] INFO: Starting...
|
|
99
|
+
* logger.info('API key:', 'sk-abc123def456ghi789'); // [MCPClient] INFO: API key: sk-ab...789
|
|
20
100
|
* ```
|
|
21
101
|
*/
|
|
22
102
|
export class ConsoleLogger {
|
|
23
103
|
level;
|
|
24
104
|
prefix;
|
|
105
|
+
shouldMask;
|
|
106
|
+
useTimestamps;
|
|
107
|
+
useColors;
|
|
25
108
|
constructor(options = {}) {
|
|
26
109
|
this.level = LOG_LEVELS[options.level || 'info'];
|
|
27
110
|
this.prefix = options.prefix || '';
|
|
111
|
+
this.shouldMask = options.maskSecrets !== false; // default true
|
|
112
|
+
this.useTimestamps = options.timestamps ?? false;
|
|
113
|
+
this.useColors = options.colors ?? true;
|
|
28
114
|
}
|
|
29
115
|
shouldLog(messageLevel) {
|
|
30
116
|
return LOG_LEVELS[messageLevel] >= this.level;
|
|
31
117
|
}
|
|
32
118
|
format(level, args) {
|
|
33
119
|
const prefix = this.prefix ? `[${this.prefix}] ` : '';
|
|
34
|
-
|
|
120
|
+
const levelStr = level.toUpperCase();
|
|
121
|
+
const masked = maskArgs(args, this.shouldMask);
|
|
122
|
+
const parts = [];
|
|
123
|
+
if (this.useTimestamps) {
|
|
124
|
+
parts.push(colorize(`[${formatTimestamp()}]`, COLORS.dim, this.useColors));
|
|
125
|
+
}
|
|
126
|
+
parts.push(colorize(`${prefix}${levelStr}:`, COLORS[level] || COLORS.reset, this.useColors));
|
|
127
|
+
parts.push(...masked);
|
|
128
|
+
return parts.length > 1 ? parts : [`${prefix}${levelStr}:`, ...masked];
|
|
35
129
|
}
|
|
36
130
|
debug(...args) {
|
|
37
131
|
if (this.shouldLog('debug')) {
|
|
@@ -70,9 +164,9 @@ export class ConsoleLogger {
|
|
|
70
164
|
* ```
|
|
71
165
|
*/
|
|
72
166
|
export class NoOpLogger {
|
|
73
|
-
debug() { }
|
|
74
|
-
info() { }
|
|
75
|
-
warn() { }
|
|
76
|
-
error() { }
|
|
167
|
+
debug(..._args) { }
|
|
168
|
+
info(..._args) { }
|
|
169
|
+
warn(..._args) { }
|
|
170
|
+
error(..._args) { }
|
|
77
171
|
}
|
|
78
172
|
//# sourceMappingURL=logger.js.map
|
package/dist/utils/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret masking utility for preventing sensitive data from appearing in logs.
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects and masks common secret patterns like API keys,
|
|
5
|
+
* tokens, passwords, and connection strings. Also masks values of known
|
|
6
|
+
* sensitive environment variable keys.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { maskSecrets, addSecretPattern } from '@slbdn/mcp-tester';
|
|
11
|
+
*
|
|
12
|
+
* // Built-in patterns are applied automatically
|
|
13
|
+
* maskSecrets('API key: sk-abc123def456ghi789');
|
|
14
|
+
* // → 'API key: sk-ab...789'
|
|
15
|
+
*
|
|
16
|
+
* // Add custom patterns
|
|
17
|
+
* addSecretPattern(/my-secret-key-[a-zA-Z0-9]+/g, 'MyOrg API key');
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* A secret masking pattern with a regex and human-readable name.
|
|
22
|
+
*/
|
|
23
|
+
export interface SecretPattern {
|
|
24
|
+
/** Regex pattern to match secrets. */
|
|
25
|
+
pattern: RegExp;
|
|
26
|
+
/** Human-readable name for the pattern (used in diagnostics). */
|
|
27
|
+
name: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Mask a string value, keeping the first few and last few characters visible
|
|
31
|
+
* and replacing the rest with `...`.
|
|
32
|
+
*
|
|
33
|
+
* @param value - The secret value to mask
|
|
34
|
+
* @param visibleChars - How many characters to keep visible at start and end
|
|
35
|
+
* @returns Masked string like "sk-ab...789"
|
|
36
|
+
*/
|
|
37
|
+
export declare function maskValue(value: string, visibleChars?: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Mask secrets in a string by applying all active secret patterns
|
|
40
|
+
* and masking values of known sensitive environment variable keys.
|
|
41
|
+
*
|
|
42
|
+
* @param input - The value to mask (any type — converted to string)
|
|
43
|
+
* @returns String with secrets replaced by masked versions
|
|
44
|
+
*/
|
|
45
|
+
export declare function maskSecrets(input: unknown): string;
|
|
46
|
+
/**
|
|
47
|
+
* Add a custom secret pattern to the masking rules.
|
|
48
|
+
*
|
|
49
|
+
* @param pattern - Regex pattern to match secrets
|
|
50
|
+
* @param name - Human-readable name for the pattern
|
|
51
|
+
*/
|
|
52
|
+
export declare function addSecretPattern(pattern: RegExp, name?: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Reset all secret patterns to the built-in defaults.
|
|
55
|
+
* Removes any patterns added via `addSecretPattern()`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resetSecretPatterns(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Get the list of currently active secret patterns.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getSecretPatterns(): SecretPattern[];
|
|
62
|
+
/**
|
|
63
|
+
* Get the set of sensitive environment variable key names.
|
|
64
|
+
*/
|
|
65
|
+
export declare function getSensitiveEnvKeys(): ReadonlySet<string>;
|
|
66
|
+
//# sourceMappingURL=masking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masking.d.ts","sourceRoot":"","sources":["../../src/utils/masking.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;CACd;AAuFD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,GAAE,MAAsB,GAAG,MAAM,CAOrF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA2BlD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAiB,GAAG,IAAI,CAE/E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,aAAa,EAAE,CAEnD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,WAAW,CAAC,MAAM,CAAC,CAEzD"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret masking utility for preventing sensitive data from appearing in logs.
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects and masks common secret patterns like API keys,
|
|
5
|
+
* tokens, passwords, and connection strings. Also masks values of known
|
|
6
|
+
* sensitive environment variable keys.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { maskSecrets, addSecretPattern } from '@slbdn/mcp-tester';
|
|
11
|
+
*
|
|
12
|
+
* // Built-in patterns are applied automatically
|
|
13
|
+
* maskSecrets('API key: sk-abc123def456ghi789');
|
|
14
|
+
* // → 'API key: sk-ab...789'
|
|
15
|
+
*
|
|
16
|
+
* // Add custom patterns
|
|
17
|
+
* addSecretPattern(/my-secret-key-[a-zA-Z0-9]+/g, 'MyOrg API key');
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Environment variable keys that commonly contain secrets.
|
|
22
|
+
* Values for these keys will be masked in log output.
|
|
23
|
+
*/
|
|
24
|
+
const SENSITIVE_ENV_KEYS = new Set([
|
|
25
|
+
'API_KEY',
|
|
26
|
+
'API_SECRET',
|
|
27
|
+
'SECRET_KEY',
|
|
28
|
+
'SECRET',
|
|
29
|
+
'PRIVATE_KEY',
|
|
30
|
+
'ACCESS_KEY',
|
|
31
|
+
'ACCESS_TOKEN',
|
|
32
|
+
'ACCESS_SECRET',
|
|
33
|
+
'AUTH_TOKEN',
|
|
34
|
+
'AUTHORIZATION',
|
|
35
|
+
'BEARER_TOKEN',
|
|
36
|
+
'TOKEN',
|
|
37
|
+
'REFRESH_TOKEN',
|
|
38
|
+
'SESSION_TOKEN',
|
|
39
|
+
'SESSION_ID',
|
|
40
|
+
'ID_TOKEN',
|
|
41
|
+
'PASSWORD',
|
|
42
|
+
'PASSWD',
|
|
43
|
+
'DB_PASSWORD',
|
|
44
|
+
'DATABASE_PASSWORD',
|
|
45
|
+
'REDIS_PASSWORD',
|
|
46
|
+
'MONGO_PASSWORD',
|
|
47
|
+
'DATABASE_URL',
|
|
48
|
+
'DB_URL',
|
|
49
|
+
'MONGODB_URI',
|
|
50
|
+
'POSTGRES_URL',
|
|
51
|
+
'MYSQL_URL',
|
|
52
|
+
'REDIS_URL',
|
|
53
|
+
'AWS_ACCESS_KEY_ID',
|
|
54
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
55
|
+
'AWS_SESSION_TOKEN',
|
|
56
|
+
'AZURE_CLIENT_SECRET',
|
|
57
|
+
'GCP_API_KEY',
|
|
58
|
+
'GOOGLE_API_KEY',
|
|
59
|
+
'OPENAI_API_KEY',
|
|
60
|
+
'ANTHROPIC_API_KEY',
|
|
61
|
+
'GITHUB_TOKEN',
|
|
62
|
+
'GITLAB_TOKEN',
|
|
63
|
+
'NPM_TOKEN',
|
|
64
|
+
'NPM_AUTH_TOKEN',
|
|
65
|
+
'SLACK_TOKEN',
|
|
66
|
+
'SLACK_WEBHOOK',
|
|
67
|
+
'SENTRY_DSN',
|
|
68
|
+
'ENCRYPTION_KEY',
|
|
69
|
+
'SIGNING_KEY',
|
|
70
|
+
'JWT_SECRET',
|
|
71
|
+
'CSRF_SECRET',
|
|
72
|
+
'APP_SECRET',
|
|
73
|
+
]);
|
|
74
|
+
const OPENAI_KEY_REGEX = /\bsk-(?:proj|ant)-[a-zA-Z0-9_-]{10,}/g;
|
|
75
|
+
const SK_KEY_REGEX = /\bsk-[a-zA-Z0-9]{12,}/g;
|
|
76
|
+
const AWS_KEY_REGEX = /\bAKIA[A-Z0-9]{16}\b/g;
|
|
77
|
+
const BEARER_REGEX = /\bBearer\s+[a-zA-Z0-9._-]{8,}/gi;
|
|
78
|
+
const JWT_REGEX = /\beyJ[a-zA-Z0-9_-]{10,}/g;
|
|
79
|
+
const HEX_REGEX = /\b[a-f0-9]{40,}\b/g;
|
|
80
|
+
const URL_CREDS_REGEX = /:[a-zA-Z0-9._-]{4,}@[a-zA-Z0-9.-]+/g;
|
|
81
|
+
const KV_REGEX = /\b(?:password|passwd|secret|token|key|auth|credential)s?\s*[:=]\s*["']?[a-zA-Z0-9._/@+-]{8,}["']?/gi;
|
|
82
|
+
/**
|
|
83
|
+
* Built-in secret patterns that are automatically masked.
|
|
84
|
+
*/
|
|
85
|
+
const DEFAULT_SECRET_PATTERNS = [
|
|
86
|
+
{ pattern: OPENAI_KEY_REGEX, name: 'OpenAI/Anthropic API key' },
|
|
87
|
+
{ pattern: SK_KEY_REGEX, name: 'API key (sk-)' },
|
|
88
|
+
{ pattern: AWS_KEY_REGEX, name: 'AWS access key' },
|
|
89
|
+
{ pattern: BEARER_REGEX, name: 'Bearer token' },
|
|
90
|
+
{ pattern: JWT_REGEX, name: 'JWT token' },
|
|
91
|
+
{ pattern: HEX_REGEX, name: 'Hex token' },
|
|
92
|
+
{ pattern: URL_CREDS_REGEX, name: 'URL credentials' },
|
|
93
|
+
{ pattern: KV_REGEX, name: 'Key-value secret' },
|
|
94
|
+
];
|
|
95
|
+
/** Active secret patterns (built-in + user-added). */
|
|
96
|
+
let activePatterns = [...DEFAULT_SECRET_PATTERNS];
|
|
97
|
+
/** Number of characters to keep visible at start/end when masking. */
|
|
98
|
+
const VISIBLE_CHARS = 3;
|
|
99
|
+
/**
|
|
100
|
+
* Mask a string value, keeping the first few and last few characters visible
|
|
101
|
+
* and replacing the rest with `...`.
|
|
102
|
+
*
|
|
103
|
+
* @param value - The secret value to mask
|
|
104
|
+
* @param visibleChars - How many characters to keep visible at start and end
|
|
105
|
+
* @returns Masked string like "sk-ab...789"
|
|
106
|
+
*/
|
|
107
|
+
export function maskValue(value, visibleChars = VISIBLE_CHARS) {
|
|
108
|
+
if (visibleChars <= 0 || value.length <= visibleChars * 2 + 3) {
|
|
109
|
+
return '***';
|
|
110
|
+
}
|
|
111
|
+
const start = value.slice(0, visibleChars);
|
|
112
|
+
const end = value.slice(-visibleChars);
|
|
113
|
+
return `${start}...${end}`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Mask secrets in a string by applying all active secret patterns
|
|
117
|
+
* and masking values of known sensitive environment variable keys.
|
|
118
|
+
*
|
|
119
|
+
* @param input - The value to mask (any type — converted to string)
|
|
120
|
+
* @returns String with secrets replaced by masked versions
|
|
121
|
+
*/
|
|
122
|
+
export function maskSecrets(input) {
|
|
123
|
+
if (input === null || input === undefined) {
|
|
124
|
+
return String(input);
|
|
125
|
+
}
|
|
126
|
+
let result = String(input);
|
|
127
|
+
// 1. First mask values of known sensitive environment variable keys
|
|
128
|
+
// Matches: KEY=value, KEY="value", KEY: value, KEY: "value"
|
|
129
|
+
result = result.replace(/\b([A-Z_][A-Z0-9_]{2,})(\s*[:=]\s*)(["']?)([^\s"']{4,})\3/g, (_match, key, separator, quote, _value) => {
|
|
130
|
+
if (SENSITIVE_ENV_KEYS.has(key.toUpperCase())) {
|
|
131
|
+
return `${key}${separator}${quote}***${quote}`;
|
|
132
|
+
}
|
|
133
|
+
return _match;
|
|
134
|
+
});
|
|
135
|
+
// 2. Then apply built-in and user-added secret patterns
|
|
136
|
+
for (const { pattern } of activePatterns) {
|
|
137
|
+
// Create fresh regex to avoid lastIndex issues from global flag
|
|
138
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
139
|
+
result = result.replace(regex, (match) => maskValue(match));
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Add a custom secret pattern to the masking rules.
|
|
145
|
+
*
|
|
146
|
+
* @param pattern - Regex pattern to match secrets
|
|
147
|
+
* @param name - Human-readable name for the pattern
|
|
148
|
+
*/
|
|
149
|
+
export function addSecretPattern(pattern, name = 'custom') {
|
|
150
|
+
activePatterns.push({ pattern, name });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reset all secret patterns to the built-in defaults.
|
|
154
|
+
* Removes any patterns added via `addSecretPattern()`.
|
|
155
|
+
*/
|
|
156
|
+
export function resetSecretPatterns() {
|
|
157
|
+
activePatterns = [...DEFAULT_SECRET_PATTERNS];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get the list of currently active secret patterns.
|
|
161
|
+
*/
|
|
162
|
+
export function getSecretPatterns() {
|
|
163
|
+
return [...activePatterns];
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get the set of sensitive environment variable key names.
|
|
167
|
+
*/
|
|
168
|
+
export function getSensitiveEnvKeys() {
|
|
169
|
+
return SENSITIVE_ENV_KEYS;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=masking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masking.js","sourceRoot":"","sources":["../../src/utils/masking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validation utilities for MCPClient.
|
|
3
|
+
*
|
|
4
|
+
* Validates all user-facing inputs and throws descriptive `MCPClientError`
|
|
5
|
+
* with actionable messages when validation fails.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validate `start()` config — command is required and must be a non-empty string.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateServerConfig(config: unknown): asserts config is {
|
|
11
|
+
command: string;
|
|
12
|
+
args?: string[];
|
|
13
|
+
env?: Record<string, string | undefined>;
|
|
14
|
+
startupDelay?: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Validate `callTool()` options — tool name is required.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateToolCallOptions(options: unknown): asserts options is {
|
|
20
|
+
name: string;
|
|
21
|
+
arguments?: Record<string, unknown>;
|
|
22
|
+
timeout?: number;
|
|
23
|
+
retries?: number;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Validate a resource URI — must be a non-empty string.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateResourceUri(uri: unknown): asserts uri is string;
|
|
29
|
+
/**
|
|
30
|
+
* Validate prompt name — must be a non-empty string.
|
|
31
|
+
*/
|
|
32
|
+
export declare function validatePromptName(name: unknown): asserts name is string;
|
|
33
|
+
/**
|
|
34
|
+
* Validate prompt arguments — must be an object with string values if provided.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validatePromptArgs(args: unknown): asserts args is Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Validate sampling request params.
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateSamplingRequest(request: unknown): asserts request is {
|
|
41
|
+
messages: unknown[];
|
|
42
|
+
maxTokens: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Validate client constructor options.
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateClientOptions(options: unknown): void;
|
|
48
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAwGA;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAqFA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,IAAI,MAAM,CAsBvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAqBxE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBxF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI;IAC5E,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CA+BA;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyG5D"}
|