mcp-compression-proxy 1.0.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 +97 -0
- package/LICENSE +21 -0
- package/README.md +842 -0
- package/dist/cli/commands.d.ts +25 -0
- package/dist/cli/commands.js +152 -0
- package/dist/cli/daemon.d.ts +4 -0
- package/dist/cli/daemon.js +336 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +269 -0
- package/dist/cli/ipc-client.d.ts +11 -0
- package/dist/cli/ipc-client.js +81 -0
- package/dist/cli/payload-interceptor.d.ts +6 -0
- package/dist/cli/payload-interceptor.js +49 -0
- package/dist/config/loader.d.ts +53 -0
- package/dist/config/loader.js +332 -0
- package/dist/config/schema.d.ts +164 -0
- package/dist/config/schema.js +127 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +821 -0
- package/dist/mcp/client-manager.d.ts +65 -0
- package/dist/mcp/client-manager.js +197 -0
- package/dist/services/compression-cache.d.ts +112 -0
- package/dist/services/compression-cache.js +238 -0
- package/dist/services/compression-persistence.d.ts +36 -0
- package/dist/services/compression-persistence.js +111 -0
- package/dist/services/compression-sampler.d.ts +89 -0
- package/dist/services/compression-sampler.js +171 -0
- package/dist/services/session-manager.d.ts +64 -0
- package/dist/services/session-manager.js +160 -0
- package/dist/services/stats-service.d.ts +101 -0
- package/dist/services/stats-service.js +246 -0
- package/dist/types/compression.d.ts +38 -0
- package/dist/types/compression.js +5 -0
- package/dist/types/index.d.ts +108 -0
- package/dist/types/index.js +2 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.js +11 -0
- package/package.json +110 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { readFileSync, existsSync, statSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir as osHomedir } from 'os';
|
|
4
|
+
import { serverConfigSchema, } from './schema.js';
|
|
5
|
+
import Ajv from 'ajv';
|
|
6
|
+
const ajv = new Ajv({ allErrors: true });
|
|
7
|
+
const validate = ajv.compile(serverConfigSchema);
|
|
8
|
+
/**
|
|
9
|
+
* Get home directory (testable)
|
|
10
|
+
*/
|
|
11
|
+
function homedir() {
|
|
12
|
+
return process.env.HOME || osHomedir();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Expands environment variables in a string.
|
|
16
|
+
*
|
|
17
|
+
* Supported syntax:
|
|
18
|
+
* - `${VAR}` - substitutes the variable, or '' when it is not set
|
|
19
|
+
* - `${VAR:-fallback}` - substitutes the variable, or `fallback` when unset/empty
|
|
20
|
+
* - `$${VAR}` - escape hatch, produces the literal text `${VAR}`
|
|
21
|
+
*
|
|
22
|
+
* Names of `${VAR}` references that could not be resolved are collected into
|
|
23
|
+
* `unresolved` so the caller can warn instead of silently injecting an empty
|
|
24
|
+
* string (a common cause of confusing downstream 401s).
|
|
25
|
+
*/
|
|
26
|
+
function expandEnvVars(value, unresolved) {
|
|
27
|
+
return value.replace(/(\$?)\$\{([^}]+)\}/g, (match, escape, expression) => {
|
|
28
|
+
// `$${VAR}` is an escape for a literal `${VAR}`
|
|
29
|
+
if (escape) {
|
|
30
|
+
return match.slice(1);
|
|
31
|
+
}
|
|
32
|
+
const separatorIndex = expression.indexOf(':-');
|
|
33
|
+
const varName = separatorIndex === -1 ? expression : expression.slice(0, separatorIndex);
|
|
34
|
+
const defaultValue = separatorIndex === -1 ? undefined : expression.slice(separatorIndex + 2);
|
|
35
|
+
const resolved = process.env[varName];
|
|
36
|
+
if (resolved) {
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
if (defaultValue !== undefined) {
|
|
40
|
+
return defaultValue;
|
|
41
|
+
}
|
|
42
|
+
unresolved.add(varName);
|
|
43
|
+
return '';
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Recursively expands environment variables in an object.
|
|
48
|
+
*
|
|
49
|
+
* Generic rather than `any`: expansion only ever rewrites string leaves, so
|
|
50
|
+
* the shape is preserved. Returning `any` here silently erased
|
|
51
|
+
* ServerConfigJSON at the one call site that needs it most - the value that
|
|
52
|
+
* has just been schema-validated.
|
|
53
|
+
*/
|
|
54
|
+
function expandEnvVarsInObject(obj, unresolved) {
|
|
55
|
+
if (typeof obj === 'string') {
|
|
56
|
+
return expandEnvVars(obj, unresolved);
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(obj)) {
|
|
59
|
+
return obj.map((item) => expandEnvVarsInObject(item, unresolved));
|
|
60
|
+
}
|
|
61
|
+
if (obj !== null && typeof obj === 'object') {
|
|
62
|
+
const result = {};
|
|
63
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
64
|
+
result[key] = expandEnvVarsInObject(value, unresolved);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
return obj;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validates JSON config against schema
|
|
72
|
+
*/
|
|
73
|
+
function validateConfig(config) {
|
|
74
|
+
if (!validate(config)) {
|
|
75
|
+
const errors = validate.errors
|
|
76
|
+
?.map((err) => {
|
|
77
|
+
const path = err.instancePath || 'root';
|
|
78
|
+
const data = err.data ? JSON.stringify(err.data, null, 2) : 'undefined';
|
|
79
|
+
return ` - ${path}: ${err.message}\n Data: ${data}`;
|
|
80
|
+
})
|
|
81
|
+
.join('\n');
|
|
82
|
+
console.error(`[Config] Validation failed:\n${errors}`);
|
|
83
|
+
throw new Error(`Invalid server configuration:\n${errors}`);
|
|
84
|
+
}
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Load and parse JSON config from a file
|
|
89
|
+
*/
|
|
90
|
+
function loadJSONConfig(filePath) {
|
|
91
|
+
if (!existsSync(filePath)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
96
|
+
const parsed = JSON.parse(content);
|
|
97
|
+
const validated = validateConfig(parsed);
|
|
98
|
+
// Expand environment variables
|
|
99
|
+
const unresolved = new Set();
|
|
100
|
+
const expanded = expandEnvVarsInObject(validated, unresolved);
|
|
101
|
+
if (unresolved.size > 0) {
|
|
102
|
+
console.error(`[Config] WARNING: ${filePath} references environment variable(s) that are not set: ` +
|
|
103
|
+
`${[...unresolved].join(', ')}. They were replaced with an empty string, which usually ` +
|
|
104
|
+
`surfaces later as an authentication failure in the downstream server. Export them before ` +
|
|
105
|
+
`starting your MCP client, or use \${VAR:-default} to supply a fallback.`);
|
|
106
|
+
}
|
|
107
|
+
return expanded;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error instanceof SyntaxError) {
|
|
111
|
+
// Keep the parser's own error as `cause`: it carries the offset of the
|
|
112
|
+
// offending token, which is what actually locates the typo.
|
|
113
|
+
throw new Error(`Invalid JSON in ${filePath}: ${error.message}`, { cause: error });
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get config file paths
|
|
120
|
+
* User-level: ~/.mcp-compression-proxy/servers.json
|
|
121
|
+
* Project-level: ./servers.json
|
|
122
|
+
*/
|
|
123
|
+
function getConfigPaths() {
|
|
124
|
+
return {
|
|
125
|
+
user: join(homedir(), '.mcp-compression-proxy', 'servers.json'),
|
|
126
|
+
project: join(process.cwd(), 'servers.json'),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Convert wildcard pattern to regex
|
|
131
|
+
* Supports * wildcard, case-insensitive
|
|
132
|
+
*/
|
|
133
|
+
function patternToRegex(pattern) {
|
|
134
|
+
// Escape regex special chars except *
|
|
135
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
136
|
+
// Convert * to .*
|
|
137
|
+
const regexPattern = escaped.replace(/\*/g, '.*');
|
|
138
|
+
// Case insensitive
|
|
139
|
+
return new RegExp(`^${regexPattern}$`, 'i');
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if tool name matches any ignore pattern
|
|
143
|
+
*/
|
|
144
|
+
export function matchesIgnorePattern(toolName, patterns) {
|
|
145
|
+
if (!patterns || patterns.length === 0) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return patterns.some(pattern => {
|
|
149
|
+
const regex = patternToRegex(pattern);
|
|
150
|
+
return regex.test(toolName);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Load and aggregate server configuration from JSON files
|
|
155
|
+
* 1. Load user-level config and collect patterns
|
|
156
|
+
* 2. Load project-level config and append servers
|
|
157
|
+
* 3. Aggregate exclude and noCompress patterns from both configs
|
|
158
|
+
*/
|
|
159
|
+
export function loadJSONServers() {
|
|
160
|
+
const paths = getConfigPaths();
|
|
161
|
+
let aggregatedServers = [];
|
|
162
|
+
let aggregatedExcludePatterns = [];
|
|
163
|
+
let aggregatedNoCompressPatterns = [];
|
|
164
|
+
let defaultTimeout;
|
|
165
|
+
let cliConfig;
|
|
166
|
+
let inheritEnv;
|
|
167
|
+
let compressionFallbackBehavior = 'original';
|
|
168
|
+
let hasAnyConfig = false;
|
|
169
|
+
// Step 1: Load user-level config
|
|
170
|
+
const userConfig = loadJSONConfig(paths.user);
|
|
171
|
+
if (userConfig) {
|
|
172
|
+
hasAnyConfig = true;
|
|
173
|
+
console.error(`[Config] Loaded user-level configuration from: ${paths.user}`);
|
|
174
|
+
console.error(`[Config] User config contains ${userConfig.mcpServers.length} servers`);
|
|
175
|
+
aggregatedServers = [...userConfig.mcpServers];
|
|
176
|
+
if (userConfig.excludeTools) {
|
|
177
|
+
aggregatedExcludePatterns = [...userConfig.excludeTools];
|
|
178
|
+
}
|
|
179
|
+
if (userConfig.noCompressTools) {
|
|
180
|
+
aggregatedNoCompressPatterns = [...userConfig.noCompressTools];
|
|
181
|
+
}
|
|
182
|
+
if (userConfig.defaultTimeout) {
|
|
183
|
+
defaultTimeout = userConfig.defaultTimeout;
|
|
184
|
+
}
|
|
185
|
+
if (userConfig.cli) {
|
|
186
|
+
cliConfig = { ...userConfig.cli };
|
|
187
|
+
}
|
|
188
|
+
if (userConfig.inheritEnv !== undefined) {
|
|
189
|
+
inheritEnv = userConfig.inheritEnv;
|
|
190
|
+
}
|
|
191
|
+
if (userConfig.compressionFallbackBehavior) {
|
|
192
|
+
compressionFallbackBehavior = userConfig.compressionFallbackBehavior;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.error(`[Config] No user-level config found at: ${paths.user}`);
|
|
197
|
+
}
|
|
198
|
+
// Step 2: Load project-level config and append
|
|
199
|
+
const projectConfig = loadJSONConfig(paths.project);
|
|
200
|
+
if (projectConfig) {
|
|
201
|
+
hasAnyConfig = true;
|
|
202
|
+
console.error(`[Config] Loaded project-level configuration from: ${paths.project}`);
|
|
203
|
+
console.error(`[Config] Project config contains ${projectConfig.mcpServers.length} servers`);
|
|
204
|
+
// Append project servers
|
|
205
|
+
aggregatedServers = [...aggregatedServers, ...projectConfig.mcpServers];
|
|
206
|
+
// Append project exclude patterns
|
|
207
|
+
if (projectConfig.excludeTools) {
|
|
208
|
+
aggregatedExcludePatterns = [...aggregatedExcludePatterns, ...projectConfig.excludeTools];
|
|
209
|
+
}
|
|
210
|
+
// Append project noCompress patterns
|
|
211
|
+
if (projectConfig.noCompressTools) {
|
|
212
|
+
aggregatedNoCompressPatterns = [...aggregatedNoCompressPatterns, ...projectConfig.noCompressTools];
|
|
213
|
+
}
|
|
214
|
+
// Project-level settings override user-level
|
|
215
|
+
if (projectConfig.defaultTimeout) {
|
|
216
|
+
defaultTimeout = projectConfig.defaultTimeout;
|
|
217
|
+
}
|
|
218
|
+
// Project-level CLI config overrides user-level
|
|
219
|
+
if (projectConfig.cli) {
|
|
220
|
+
cliConfig = { ...cliConfig, ...projectConfig.cli };
|
|
221
|
+
}
|
|
222
|
+
if (projectConfig.inheritEnv !== undefined) {
|
|
223
|
+
inheritEnv = projectConfig.inheritEnv;
|
|
224
|
+
}
|
|
225
|
+
if (projectConfig.compressionFallbackBehavior) {
|
|
226
|
+
compressionFallbackBehavior = projectConfig.compressionFallbackBehavior;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.error(`[Config] No project-level config found at: ${paths.project}`);
|
|
231
|
+
}
|
|
232
|
+
if (!hasAnyConfig) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
// Log disabled servers
|
|
236
|
+
const disabled = aggregatedServers.filter(s => s.enabled === false);
|
|
237
|
+
if (disabled.length > 0) {
|
|
238
|
+
console.error(`[Config] Found ${disabled.length} disabled server(s): ${disabled.map(s => s.name).join(', ')}`);
|
|
239
|
+
}
|
|
240
|
+
// Log exclude patterns
|
|
241
|
+
if (aggregatedExcludePatterns.length > 0) {
|
|
242
|
+
console.error(`[Config] Tool exclude patterns: ${aggregatedExcludePatterns.join(', ')}`);
|
|
243
|
+
}
|
|
244
|
+
// Log noCompress patterns
|
|
245
|
+
if (aggregatedNoCompressPatterns.length > 0) {
|
|
246
|
+
console.error(`[Config] Tool noCompress patterns: ${aggregatedNoCompressPatterns.join(', ')}`);
|
|
247
|
+
}
|
|
248
|
+
// Log default timeout
|
|
249
|
+
if (defaultTimeout) {
|
|
250
|
+
console.error(`[Config] Default timeout: ${defaultTimeout} seconds`);
|
|
251
|
+
}
|
|
252
|
+
// Log environment inheritance policy when it deviates from the default
|
|
253
|
+
if (inheritEnv !== undefined && inheritEnv !== true) {
|
|
254
|
+
console.error(`[Config] Environment inheritance: ${inheritEnv === false ? 'safe defaults only' : `allowlist (${inheritEnv.join(', ')})`}`);
|
|
255
|
+
}
|
|
256
|
+
// Log fallback behavior when it deviates from the default
|
|
257
|
+
if (compressionFallbackBehavior !== 'original') {
|
|
258
|
+
console.error(`[Config] Compression fallback behavior: ${compressionFallbackBehavior}`);
|
|
259
|
+
}
|
|
260
|
+
console.error(`[Config] Total servers after aggregation: ${aggregatedServers.length}`);
|
|
261
|
+
return {
|
|
262
|
+
servers: aggregatedServers,
|
|
263
|
+
excludePatterns: aggregatedExcludePatterns,
|
|
264
|
+
noCompressPatterns: aggregatedNoCompressPatterns,
|
|
265
|
+
defaultTimeout,
|
|
266
|
+
cli: cliConfig,
|
|
267
|
+
inheritEnv,
|
|
268
|
+
compressionFallbackBehavior,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Fingerprint of the config files, used to detect edits between reads.
|
|
273
|
+
* Missing files are part of the fingerprint so creating one invalidates too.
|
|
274
|
+
*/
|
|
275
|
+
function configSignature() {
|
|
276
|
+
const paths = getConfigPaths();
|
|
277
|
+
return [paths.user, paths.project]
|
|
278
|
+
.map((path) => {
|
|
279
|
+
try {
|
|
280
|
+
const stat = statSync(path);
|
|
281
|
+
return `${path}:${stat.mtimeMs}:${stat.size}`;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return `${path}:missing`;
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
.join('|');
|
|
288
|
+
}
|
|
289
|
+
let configCache = null;
|
|
290
|
+
/**
|
|
291
|
+
* Cached wrapper around {@link loadJSONServers}.
|
|
292
|
+
*
|
|
293
|
+
* `tools/list` runs on every client refresh, and re-reading, re-validating and
|
|
294
|
+
* re-logging both config files each time is pure overhead. The cache is keyed
|
|
295
|
+
* on file mtime/size, so edits are still picked up without a restart.
|
|
296
|
+
*/
|
|
297
|
+
export function loadJSONServersCached() {
|
|
298
|
+
const signature = configSignature();
|
|
299
|
+
if (configCache && configCache.signature === signature) {
|
|
300
|
+
return configCache.result;
|
|
301
|
+
}
|
|
302
|
+
const result = loadJSONServers();
|
|
303
|
+
configCache = { signature, result };
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Drop the cached config. Primarily for tests that swap config files
|
|
308
|
+
* within a single process.
|
|
309
|
+
*/
|
|
310
|
+
export function clearConfigCache() {
|
|
311
|
+
configCache = null;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Get the path that would be used for config
|
|
315
|
+
* (for migration script purposes)
|
|
316
|
+
*/
|
|
317
|
+
export function getConfigPath() {
|
|
318
|
+
return join(homedir(), '.mcp-compression-proxy', 'servers.json');
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get the daemon Unix socket path
|
|
322
|
+
*/
|
|
323
|
+
export function getSocketPath() {
|
|
324
|
+
return join(homedir(), '.mcp-compression-proxy', 'daemon.sock');
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get the daemon PID file path
|
|
328
|
+
*/
|
|
329
|
+
export function getPidFilePath() {
|
|
330
|
+
return join(homedir(), '.mcp-compression-proxy', 'daemon.pid');
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema for MCP server configuration
|
|
3
|
+
*/
|
|
4
|
+
export declare const serverConfigSchema: {
|
|
5
|
+
$schema: string;
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {
|
|
8
|
+
mcpServers: {
|
|
9
|
+
type: string;
|
|
10
|
+
items: {
|
|
11
|
+
type: string;
|
|
12
|
+
properties: {
|
|
13
|
+
name: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
minLength: number;
|
|
17
|
+
};
|
|
18
|
+
command: {
|
|
19
|
+
type: string;
|
|
20
|
+
description: string;
|
|
21
|
+
minLength: number;
|
|
22
|
+
};
|
|
23
|
+
args: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
items: {
|
|
27
|
+
type: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
env: {
|
|
31
|
+
type: string;
|
|
32
|
+
description: string;
|
|
33
|
+
additionalProperties: {
|
|
34
|
+
type: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
inheritEnv: {
|
|
38
|
+
description: string;
|
|
39
|
+
oneOf: ({
|
|
40
|
+
type: string;
|
|
41
|
+
items?: undefined;
|
|
42
|
+
} | {
|
|
43
|
+
type: string;
|
|
44
|
+
items: {
|
|
45
|
+
type: string;
|
|
46
|
+
};
|
|
47
|
+
})[];
|
|
48
|
+
};
|
|
49
|
+
enabled: {
|
|
50
|
+
type: string;
|
|
51
|
+
description: string;
|
|
52
|
+
};
|
|
53
|
+
timeout: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
type: {
|
|
58
|
+
type: string;
|
|
59
|
+
description: string;
|
|
60
|
+
};
|
|
61
|
+
autoApprove: {
|
|
62
|
+
type: string;
|
|
63
|
+
description: string;
|
|
64
|
+
items: {
|
|
65
|
+
type: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
required: string[];
|
|
70
|
+
additionalProperties: boolean;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
excludeTools: {
|
|
74
|
+
type: string;
|
|
75
|
+
description: string;
|
|
76
|
+
items: {
|
|
77
|
+
type: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
noCompressTools: {
|
|
81
|
+
type: string;
|
|
82
|
+
description: string;
|
|
83
|
+
items: {
|
|
84
|
+
type: string;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
defaultTimeout: {
|
|
88
|
+
type: string;
|
|
89
|
+
description: string;
|
|
90
|
+
minimum: number;
|
|
91
|
+
};
|
|
92
|
+
cli: {
|
|
93
|
+
type: string;
|
|
94
|
+
description: string;
|
|
95
|
+
properties: {
|
|
96
|
+
payloadThreshold: {
|
|
97
|
+
type: string;
|
|
98
|
+
description: string;
|
|
99
|
+
minimum: number;
|
|
100
|
+
default: number;
|
|
101
|
+
};
|
|
102
|
+
autoStartDaemon: {
|
|
103
|
+
type: string;
|
|
104
|
+
description: string;
|
|
105
|
+
default: boolean;
|
|
106
|
+
};
|
|
107
|
+
daemonLogLevel: {
|
|
108
|
+
type: string;
|
|
109
|
+
description: string;
|
|
110
|
+
enum: string[];
|
|
111
|
+
default: string;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
additionalProperties: boolean;
|
|
115
|
+
};
|
|
116
|
+
inheritEnv: {
|
|
117
|
+
description: string;
|
|
118
|
+
oneOf: ({
|
|
119
|
+
type: string;
|
|
120
|
+
items?: undefined;
|
|
121
|
+
} | {
|
|
122
|
+
type: string;
|
|
123
|
+
items: {
|
|
124
|
+
type: string;
|
|
125
|
+
};
|
|
126
|
+
})[];
|
|
127
|
+
};
|
|
128
|
+
compressionFallbackBehavior: {
|
|
129
|
+
type: string;
|
|
130
|
+
description: string;
|
|
131
|
+
enum: string[];
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
required: string[];
|
|
135
|
+
additionalProperties: boolean;
|
|
136
|
+
};
|
|
137
|
+
/** Environment inheritance policy: all, none/safe-defaults, or an allowlist. */
|
|
138
|
+
export type InheritEnv = boolean | string[];
|
|
139
|
+
/** How to describe a tool that has no compressed description cached yet. */
|
|
140
|
+
export type CompressionFallbackBehavior = 'original' | 'blank';
|
|
141
|
+
export type ServerConfigJSON = {
|
|
142
|
+
mcpServers: Array<{
|
|
143
|
+
name: string;
|
|
144
|
+
command: string;
|
|
145
|
+
args?: string[];
|
|
146
|
+
env?: Record<string, string>;
|
|
147
|
+
inheritEnv?: InheritEnv;
|
|
148
|
+
enabled?: boolean;
|
|
149
|
+
timeout?: number;
|
|
150
|
+
type?: string;
|
|
151
|
+
autoApprove?: string[];
|
|
152
|
+
}>;
|
|
153
|
+
excludeTools?: string[];
|
|
154
|
+
noCompressTools?: string[];
|
|
155
|
+
defaultTimeout?: number;
|
|
156
|
+
cli?: {
|
|
157
|
+
payloadThreshold?: number;
|
|
158
|
+
autoStartDaemon?: boolean;
|
|
159
|
+
daemonLogLevel?: string;
|
|
160
|
+
};
|
|
161
|
+
inheritEnv?: InheritEnv;
|
|
162
|
+
compressionFallbackBehavior?: CompressionFallbackBehavior;
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema for MCP server configuration
|
|
3
|
+
*/
|
|
4
|
+
export const serverConfigSchema = {
|
|
5
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
mcpServers: {
|
|
9
|
+
type: 'array',
|
|
10
|
+
items: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
name: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Unique name for the MCP server',
|
|
16
|
+
minLength: 1,
|
|
17
|
+
},
|
|
18
|
+
command: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Command to execute',
|
|
21
|
+
minLength: 1,
|
|
22
|
+
},
|
|
23
|
+
args: {
|
|
24
|
+
type: 'array',
|
|
25
|
+
description: 'Command arguments',
|
|
26
|
+
items: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
env: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
description: 'Environment variables',
|
|
33
|
+
additionalProperties: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
inheritEnv: {
|
|
38
|
+
description: "Which of the proxy's own environment variables this server inherits. true = inherit all (default), false = inherit only the transport's safe defaults (PATH, HOME, ...), or an array of variable names to inherit. Values in `env` always take precedence. Overrides the top-level `inheritEnv`.",
|
|
39
|
+
oneOf: [
|
|
40
|
+
{ type: 'boolean' },
|
|
41
|
+
{ type: 'array', items: { type: 'string' } },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
enabled: {
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
description: 'Whether the server is enabled',
|
|
47
|
+
},
|
|
48
|
+
timeout: {
|
|
49
|
+
type: 'number',
|
|
50
|
+
description: 'Server timeout in seconds',
|
|
51
|
+
},
|
|
52
|
+
type: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'Server transport type (usually "stdio")',
|
|
55
|
+
},
|
|
56
|
+
autoApprove: {
|
|
57
|
+
type: 'array',
|
|
58
|
+
description: 'Tools to auto-approve',
|
|
59
|
+
items: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
required: ['name', 'command'],
|
|
65
|
+
additionalProperties: true,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
excludeTools: {
|
|
69
|
+
type: 'array',
|
|
70
|
+
description: 'Tool name patterns to exclude from tool list entirely (supports wildcards, case-insensitive). Examples: "server__*" (all tools from server), "*__set*" (tools with "set" in name)',
|
|
71
|
+
items: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
noCompressTools: {
|
|
76
|
+
type: 'array',
|
|
77
|
+
description: 'Tool name patterns whose original descriptions should always be shown to the LLM (supports wildcards, case-insensitive). Tools are still compressed and cached in the background for efficiency, but their original descriptions are always displayed when listing tools.',
|
|
78
|
+
items: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
defaultTimeout: {
|
|
83
|
+
type: 'number',
|
|
84
|
+
description: 'Default timeout in seconds for all servers (can be overridden per-server). Default is 30 seconds if not specified.',
|
|
85
|
+
minimum: 1,
|
|
86
|
+
},
|
|
87
|
+
cli: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
description: 'CLI (mcp-cli) configuration for lazy-loading mode',
|
|
90
|
+
properties: {
|
|
91
|
+
payloadThreshold: {
|
|
92
|
+
type: 'number',
|
|
93
|
+
description: 'Character threshold for redirecting large tool outputs to temp files. Default: 500.',
|
|
94
|
+
minimum: 0,
|
|
95
|
+
default: 500,
|
|
96
|
+
},
|
|
97
|
+
autoStartDaemon: {
|
|
98
|
+
type: 'boolean',
|
|
99
|
+
description: 'Auto-start daemon when running CLI commands. Default: true.',
|
|
100
|
+
default: true,
|
|
101
|
+
},
|
|
102
|
+
daemonLogLevel: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: 'Log level for the daemon process. Default: "info".',
|
|
105
|
+
enum: ['debug', 'info', 'warn', 'error'],
|
|
106
|
+
default: 'info',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
additionalProperties: false,
|
|
110
|
+
},
|
|
111
|
+
inheritEnv: {
|
|
112
|
+
description: "Default environment inheritance for all servers (can be overridden per-server). true = pass the proxy's full environment to every backend server (default), false = pass only the transport's safe defaults (PATH, HOME, ...), or an array of variable names to pass through.",
|
|
113
|
+
oneOf: [
|
|
114
|
+
{ type: 'boolean' },
|
|
115
|
+
{ type: 'array', items: { type: 'string' } },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
compressionFallbackBehavior: {
|
|
119
|
+
type: 'string',
|
|
120
|
+
description: "What to show for a tool that has no compressed description yet. 'original' (default) shows the server's original description; 'blank' shows an empty description so uncompressed tools consume no context.",
|
|
121
|
+
enum: ['original', 'blank'],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ['mcpServers'],
|
|
125
|
+
additionalProperties: false,
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=schema.js.map
|
package/dist/index.d.ts
ADDED