ultra-lean-mcp-proxy 0.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/bin/cli.mjs +311 -0
- package/package.json +23 -0
- package/src/compress.mjs +170 -0
- package/src/config.mjs +496 -0
- package/src/delta.mjs +188 -0
- package/src/installer.mjs +1756 -0
- package/src/proxy.mjs +1122 -0
- package/src/result-compression.mjs +332 -0
- package/src/state.mjs +293 -0
- package/src/tools-hash-sync.mjs +52 -0
- package/src/watcher.mjs +530 -0
package/src/proxy.mjs
ADDED
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultra Lean MCP Proxy - stdio proxy with composable v2 optimization pipeline.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { compressDescription, compressSchema } from './compress.mjs';
|
|
7
|
+
import { loadProxyConfig, featureEnabledForTool, cacheTtlForTool } from './config.mjs';
|
|
8
|
+
import { ProxyState, cloneJson, isMutatingToolName, makeCacheKey } from './state.mjs';
|
|
9
|
+
import { createDelta, stableHash } from './delta.mjs';
|
|
10
|
+
import {
|
|
11
|
+
TokenCounter,
|
|
12
|
+
makeCompressionOptions,
|
|
13
|
+
compressResult,
|
|
14
|
+
estimateCompressibility,
|
|
15
|
+
tokenSavings,
|
|
16
|
+
} from './result-compression.mjs';
|
|
17
|
+
import { computeToolsHash, parseIfNoneMatch } from './tools-hash-sync.mjs';
|
|
18
|
+
|
|
19
|
+
const SEARCH_TOOL_NAME = 'ultra_lean_mcp_proxy.search_tools';
|
|
20
|
+
|
|
21
|
+
function createLineReader(stream, onLine) {
|
|
22
|
+
let buffer = '';
|
|
23
|
+
stream.on('data', (chunk) => {
|
|
24
|
+
buffer += chunk.toString('utf-8');
|
|
25
|
+
let idx;
|
|
26
|
+
while ((idx = buffer.indexOf('\n')) !== -1) {
|
|
27
|
+
const line = buffer.slice(0, idx).trim();
|
|
28
|
+
buffer = buffer.slice(idx + 1);
|
|
29
|
+
if (line) onLine(line);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
stream.on('end', () => {
|
|
33
|
+
const remaining = buffer.trim();
|
|
34
|
+
if (remaining) onLine(remaining);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function jsonSize(value) {
|
|
39
|
+
return Buffer.byteLength(JSON.stringify(value), 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function runtimeMetricsSnapshot(metrics) {
|
|
43
|
+
return {
|
|
44
|
+
upstream_requests: Number(metrics.upstreamRequests),
|
|
45
|
+
upstream_request_tokens: Number(metrics.upstreamRequestTokens),
|
|
46
|
+
upstream_request_bytes: Number(metrics.upstreamRequestBytes),
|
|
47
|
+
upstream_responses: Number(metrics.upstreamResponses),
|
|
48
|
+
upstream_response_tokens: Number(metrics.upstreamResponseTokens),
|
|
49
|
+
upstream_response_bytes: Number(metrics.upstreamResponseBytes),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function featureHealthKey(feature, toolName) {
|
|
54
|
+
return `${feature}:${toolName || '_global'}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function featureIsActive(featureStates, key, cfg) {
|
|
58
|
+
if (!cfg.autoDisableEnabled) return true;
|
|
59
|
+
const state = featureStates[key] || { regressionStreak: 0, cooldownRemaining: 0 };
|
|
60
|
+
featureStates[key] = state;
|
|
61
|
+
if (state.cooldownRemaining > 0) {
|
|
62
|
+
state.cooldownRemaining -= 1;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function recordFeatureOutcome(featureStates, key, outcome, cfg) {
|
|
69
|
+
if (!cfg.autoDisableEnabled) return;
|
|
70
|
+
const state = featureStates[key] || { regressionStreak: 0, cooldownRemaining: 0 };
|
|
71
|
+
featureStates[key] = state;
|
|
72
|
+
if (outcome === 'success') {
|
|
73
|
+
state.regressionStreak = 0;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (outcome === 'neutral') {
|
|
77
|
+
state.regressionStreak = Math.max(0, state.regressionStreak - 1);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (outcome === 'hurt') {
|
|
81
|
+
state.regressionStreak += 1;
|
|
82
|
+
if (state.regressionStreak >= cfg.autoDisableThreshold) {
|
|
83
|
+
state.regressionStreak = 0;
|
|
84
|
+
state.cooldownRemaining = cfg.autoDisableCooldownRequests;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function extractToolCall(msg) {
|
|
90
|
+
const params = msg?.params;
|
|
91
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
92
|
+
return [null, {}];
|
|
93
|
+
}
|
|
94
|
+
const name = typeof params.name === 'string' ? params.name : null;
|
|
95
|
+
const argumentsValue = params.arguments;
|
|
96
|
+
const argumentsObj = argumentsValue && typeof argumentsValue === 'object' && !Array.isArray(argumentsValue)
|
|
97
|
+
? argumentsValue
|
|
98
|
+
: {};
|
|
99
|
+
return [name, argumentsObj];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function clientSupportsToolsHashSync(params) {
|
|
103
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) return false;
|
|
104
|
+
const caps = params.capabilities;
|
|
105
|
+
if (!caps || typeof caps !== 'object' || Array.isArray(caps)) return false;
|
|
106
|
+
const experimental = caps.experimental;
|
|
107
|
+
if (!experimental || typeof experimental !== 'object' || Array.isArray(experimental)) return false;
|
|
108
|
+
const proxyExt = experimental.ultra_lean_mcp_proxy;
|
|
109
|
+
if (!proxyExt || typeof proxyExt !== 'object' || Array.isArray(proxyExt)) return false;
|
|
110
|
+
const toolsHashSync = proxyExt.tools_hash_sync;
|
|
111
|
+
if (!toolsHashSync || typeof toolsHashSync !== 'object' || Array.isArray(toolsHashSync)) return false;
|
|
112
|
+
const version = toolsHashSync.version;
|
|
113
|
+
if (typeof version === 'number') return version === 1;
|
|
114
|
+
if (typeof version === 'string') return version.trim() === '1';
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function extractToolsHashIfNoneMatch(params, algorithm) {
|
|
119
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
120
|
+
return { provided: false, valid: false, value: null };
|
|
121
|
+
}
|
|
122
|
+
const proxyExt = params._ultra_lean_mcp_proxy;
|
|
123
|
+
if (!proxyExt || typeof proxyExt !== 'object' || Array.isArray(proxyExt)) {
|
|
124
|
+
return { provided: false, valid: false, value: null };
|
|
125
|
+
}
|
|
126
|
+
const toolsHashSync = proxyExt.tools_hash_sync;
|
|
127
|
+
if (!toolsHashSync || typeof toolsHashSync !== 'object' || Array.isArray(toolsHashSync)) {
|
|
128
|
+
return { provided: false, valid: false, value: null };
|
|
129
|
+
}
|
|
130
|
+
if (!Object.prototype.hasOwnProperty.call(toolsHashSync, 'if_none_match')) {
|
|
131
|
+
return { provided: false, valid: false, value: null };
|
|
132
|
+
}
|
|
133
|
+
const normalized = parseIfNoneMatch(toolsHashSync.if_none_match, { expectedAlgorithm: algorithm });
|
|
134
|
+
if (!normalized) {
|
|
135
|
+
return { provided: true, valid: false, value: null };
|
|
136
|
+
}
|
|
137
|
+
return { provided: true, valid: true, value: normalized };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function injectInitializeToolsHashCapability(result, algorithm) {
|
|
141
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) return result;
|
|
142
|
+
const out = cloneJson(result);
|
|
143
|
+
if (!out.capabilities || typeof out.capabilities !== 'object' || Array.isArray(out.capabilities)) {
|
|
144
|
+
out.capabilities = {};
|
|
145
|
+
}
|
|
146
|
+
if (
|
|
147
|
+
!out.capabilities.experimental
|
|
148
|
+
|| typeof out.capabilities.experimental !== 'object'
|
|
149
|
+
|| Array.isArray(out.capabilities.experimental)
|
|
150
|
+
) {
|
|
151
|
+
out.capabilities.experimental = {};
|
|
152
|
+
}
|
|
153
|
+
if (
|
|
154
|
+
!out.capabilities.experimental.ultra_lean_mcp_proxy
|
|
155
|
+
|| typeof out.capabilities.experimental.ultra_lean_mcp_proxy !== 'object'
|
|
156
|
+
|| Array.isArray(out.capabilities.experimental.ultra_lean_mcp_proxy)
|
|
157
|
+
) {
|
|
158
|
+
out.capabilities.experimental.ultra_lean_mcp_proxy = {};
|
|
159
|
+
}
|
|
160
|
+
out.capabilities.experimental.ultra_lean_mcp_proxy.tools_hash_sync = {
|
|
161
|
+
version: 1,
|
|
162
|
+
algorithm,
|
|
163
|
+
};
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function toolsHashScopeKey(cfg, profileFingerprint) {
|
|
168
|
+
return `${cfg.sessionId}:${cfg.serverName}:${profileFingerprint}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function buildProfileFingerprint(cfg, upstreamCommand) {
|
|
172
|
+
return stableHash({
|
|
173
|
+
server_name: cfg.serverName,
|
|
174
|
+
command: upstreamCommand.join(' '),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildSearchToolDefinition(toolNames = null) {
|
|
179
|
+
const baseDesc = 'Search available tools and return full schemas on demand.';
|
|
180
|
+
let description = baseDesc;
|
|
181
|
+
if (toolNames && toolNames.length > 0) {
|
|
182
|
+
description = baseDesc
|
|
183
|
+
+ ' Use "select:<tool_name>" for direct selection, or keywords to search.\n\n'
|
|
184
|
+
+ 'Available tools (must be loaded via this tool before use):\n'
|
|
185
|
+
+ toolNames.join('\n');
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
name: SEARCH_TOOL_NAME,
|
|
189
|
+
description,
|
|
190
|
+
inputSchema: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
query: { type: 'string', description: 'Search query' },
|
|
194
|
+
server: { type: 'string', description: 'Optional server name' },
|
|
195
|
+
top_k: { type: 'integer', description: 'Max number of results', default: 8 },
|
|
196
|
+
include_schemas: {
|
|
197
|
+
type: 'boolean',
|
|
198
|
+
description: 'Include inputSchema in matches',
|
|
199
|
+
default: false,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: ['query'],
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function applyDefinitionCompression(tools) {
|
|
208
|
+
const out = [];
|
|
209
|
+
for (const tool of tools) {
|
|
210
|
+
const item = cloneJson(tool);
|
|
211
|
+
if (Object.prototype.hasOwnProperty.call(item, 'description')) {
|
|
212
|
+
item.description = compressDescription(String(item.description));
|
|
213
|
+
}
|
|
214
|
+
const schema = item.inputSchema || item.input_schema;
|
|
215
|
+
if (schema && typeof schema === 'object') {
|
|
216
|
+
compressSchema(schema);
|
|
217
|
+
}
|
|
218
|
+
out.push(item);
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function stripSchemaMetadata(schema, depth = 0) {
|
|
224
|
+
if (typeof schema !== 'object' || schema === null) return schema;
|
|
225
|
+
const out = {};
|
|
226
|
+
if (schema.type !== undefined) out.type = schema.type;
|
|
227
|
+
if (Array.isArray(schema.required) && schema.required.length > 0) out.required = [...schema.required];
|
|
228
|
+
if (Array.isArray(schema.enum)) out.enum = [...schema.enum];
|
|
229
|
+
if (typeof schema.format === 'string') out.format = schema.format;
|
|
230
|
+
if (typeof schema.pattern === 'string') out.pattern = schema.pattern;
|
|
231
|
+
if (schema.const !== undefined) out.const = schema.const;
|
|
232
|
+
if (typeof schema.$ref === 'string') out.$ref = schema.$ref;
|
|
233
|
+
if (typeof schema.minimum === 'number') out.minimum = schema.minimum;
|
|
234
|
+
if (typeof schema.maximum === 'number') out.maximum = schema.maximum;
|
|
235
|
+
if (typeof schema.minLength === 'number') out.minLength = schema.minLength;
|
|
236
|
+
if (typeof schema.maxLength === 'number') out.maxLength = schema.maxLength;
|
|
237
|
+
if (typeof schema.minItems === 'number') out.minItems = schema.minItems;
|
|
238
|
+
if (typeof schema.maxItems === 'number') out.maxItems = schema.maxItems;
|
|
239
|
+
if (typeof schema.description === 'string' && depth <= 1) {
|
|
240
|
+
out.description = compressDescription(schema.description);
|
|
241
|
+
}
|
|
242
|
+
if (schema.properties && typeof schema.properties === 'object' && !Array.isArray(schema.properties)) {
|
|
243
|
+
out.properties = {};
|
|
244
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
245
|
+
out.properties[key] = stripSchemaMetadata(value, depth + 1);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (Array.isArray(schema.items)) {
|
|
249
|
+
out.items = schema.items.map(s => stripSchemaMetadata(s, depth + 1));
|
|
250
|
+
} else if (schema.items && typeof schema.items === 'object') {
|
|
251
|
+
out.items = stripSchemaMetadata(schema.items, depth + 1);
|
|
252
|
+
}
|
|
253
|
+
for (const key of ['anyOf', 'oneOf', 'allOf']) {
|
|
254
|
+
if (Array.isArray(schema[key])) {
|
|
255
|
+
out[key] = schema[key].map(s => stripSchemaMetadata(s, depth + 1));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (schema.not && typeof schema.not === 'object' && !Array.isArray(schema.not)) {
|
|
259
|
+
out.not = stripSchemaMetadata(schema.not, depth + 1);
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function minimalTool(tool) {
|
|
265
|
+
const name = tool?.name || '';
|
|
266
|
+
const description = tool?.description || '';
|
|
267
|
+
const schema = tool?.inputSchema || tool?.input_schema || {};
|
|
268
|
+
return {
|
|
269
|
+
name,
|
|
270
|
+
description: compressDescription(description) || description,
|
|
271
|
+
inputSchema: stripSchemaMetadata(schema, 0),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function handleToolsListResult(
|
|
276
|
+
result,
|
|
277
|
+
state,
|
|
278
|
+
cfg,
|
|
279
|
+
metrics,
|
|
280
|
+
tokenCounter,
|
|
281
|
+
{
|
|
282
|
+
toolsHashSyncNegotiated,
|
|
283
|
+
profileFingerprint,
|
|
284
|
+
ifNoneMatch = null,
|
|
285
|
+
ifNoneMatchProvided = false,
|
|
286
|
+
ifNoneMatchValid = false,
|
|
287
|
+
} = {}
|
|
288
|
+
) {
|
|
289
|
+
const tools = result?.tools;
|
|
290
|
+
if (!Array.isArray(tools)) return result;
|
|
291
|
+
|
|
292
|
+
metrics.toolsListRequests += 1;
|
|
293
|
+
const originalSize = jsonSize(result);
|
|
294
|
+
|
|
295
|
+
let processedTools = cloneJson(tools);
|
|
296
|
+
if (cfg.definitionCompressionEnabled) {
|
|
297
|
+
processedTools = applyDefinitionCompression(processedTools);
|
|
298
|
+
}
|
|
299
|
+
state.setTools(processedTools);
|
|
300
|
+
|
|
301
|
+
let visibleTools = processedTools;
|
|
302
|
+
let lazyAllowed = false;
|
|
303
|
+
if (cfg.lazyLoadingEnabled) {
|
|
304
|
+
const toolCount = processedTools.length;
|
|
305
|
+
const toolTokens = tokenCounter.count({ tools: processedTools });
|
|
306
|
+
lazyAllowed = toolCount >= cfg.lazyMinTools || toolTokens >= cfg.lazyMinTokens;
|
|
307
|
+
}
|
|
308
|
+
if (lazyAllowed) {
|
|
309
|
+
if (cfg.lazyMode === 'search_only') {
|
|
310
|
+
visibleTools = [];
|
|
311
|
+
} else if (cfg.lazyMode === 'catalog') {
|
|
312
|
+
visibleTools = processedTools.map((t) => ({ name: t.name, inputSchema: { type: 'object' } }));
|
|
313
|
+
} else if (cfg.lazyMode === 'minimal') {
|
|
314
|
+
visibleTools = processedTools.map((tool) => minimalTool(tool));
|
|
315
|
+
}
|
|
316
|
+
const toolNames = cfg.lazyMode === 'catalog'
|
|
317
|
+
? processedTools.map((t) => t.name)
|
|
318
|
+
: null;
|
|
319
|
+
visibleTools = [...visibleTools, buildSearchToolDefinition(toolNames)];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const out = cloneJson(result);
|
|
323
|
+
out.tools = visibleTools;
|
|
324
|
+
const compressedSize = jsonSize(out);
|
|
325
|
+
const saved = originalSize - compressedSize;
|
|
326
|
+
if (saved > 0) {
|
|
327
|
+
metrics.toolsListSavedBytes += saved;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!(cfg.toolsHashSyncEnabled && toolsHashSyncNegotiated)) {
|
|
331
|
+
return out;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const scopeKey = toolsHashScopeKey(cfg, profileFingerprint);
|
|
335
|
+
try {
|
|
336
|
+
const toolsHash = computeToolsHash(visibleTools, {
|
|
337
|
+
algorithm: cfg.toolsHashSyncAlgorithm,
|
|
338
|
+
includeServerFingerprint: cfg.toolsHashSyncIncludeServerFingerprint,
|
|
339
|
+
serverFingerprint: profileFingerprint,
|
|
340
|
+
});
|
|
341
|
+
state.toolsHashSetLast(scopeKey, toolsHash);
|
|
342
|
+
|
|
343
|
+
const conditionalMatch = Boolean(ifNoneMatchValid && ifNoneMatch === toolsHash);
|
|
344
|
+
if (conditionalMatch) {
|
|
345
|
+
const hitCount = state.toolsHashRecordHit(scopeKey);
|
|
346
|
+
metrics.toolsHashSyncHits += 1;
|
|
347
|
+
const forceRefresh = (hitCount % cfg.toolsHashSyncRefreshInterval) === 0;
|
|
348
|
+
if (!forceRefresh) {
|
|
349
|
+
const notModified = cloneJson(out);
|
|
350
|
+
notModified.tools = [];
|
|
351
|
+
if (!notModified._ultra_lean_mcp_proxy || typeof notModified._ultra_lean_mcp_proxy !== 'object') {
|
|
352
|
+
notModified._ultra_lean_mcp_proxy = {};
|
|
353
|
+
}
|
|
354
|
+
notModified._ultra_lean_mcp_proxy.tools_hash_sync = {
|
|
355
|
+
not_modified: true,
|
|
356
|
+
tools_hash: toolsHash,
|
|
357
|
+
};
|
|
358
|
+
metrics.toolsHashSyncNotModified += 1;
|
|
359
|
+
const byteDelta = Math.max(0, jsonSize(out) - jsonSize(notModified));
|
|
360
|
+
if (byteDelta > 0) metrics.toolsHashSyncSavedBytes += byteDelta;
|
|
361
|
+
const tokenDelta = Math.max(0, tokenCounter.count(out) - tokenCounter.count(notModified));
|
|
362
|
+
if (tokenDelta > 0) metrics.toolsHashSyncSavedTokens += tokenDelta;
|
|
363
|
+
return notModified;
|
|
364
|
+
}
|
|
365
|
+
} else if (ifNoneMatchProvided && ifNoneMatchValid) {
|
|
366
|
+
metrics.toolsHashSyncMisses += 1;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
state.toolsHashResetHits(scopeKey);
|
|
370
|
+
if (!out._ultra_lean_mcp_proxy || typeof out._ultra_lean_mcp_proxy !== 'object') {
|
|
371
|
+
out._ultra_lean_mcp_proxy = {};
|
|
372
|
+
}
|
|
373
|
+
out._ultra_lean_mcp_proxy.tools_hash_sync = {
|
|
374
|
+
not_modified: false,
|
|
375
|
+
tools_hash: toolsHash,
|
|
376
|
+
};
|
|
377
|
+
return out;
|
|
378
|
+
} catch {
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function buildSearchResult(state, cfg, argumentsValue) {
|
|
384
|
+
const query = String(argumentsValue?.query || '').trim();
|
|
385
|
+
const topKRaw = argumentsValue?.top_k ?? cfg.lazyTopK;
|
|
386
|
+
const includeSchemas = Boolean(argumentsValue?.include_schemas);
|
|
387
|
+
const parsedTopK = Number.parseInt(String(topKRaw), 10);
|
|
388
|
+
const topK = Number.isFinite(parsedTopK) && parsedTopK > 0 ? parsedTopK : cfg.lazyTopK;
|
|
389
|
+
|
|
390
|
+
const matches = state.searchTools(query, topK, includeSchemas);
|
|
391
|
+
const topScore = matches.length > 0 ? Number(matches[0].score || 0) : 0;
|
|
392
|
+
const payload = {
|
|
393
|
+
server: cfg.serverName,
|
|
394
|
+
query,
|
|
395
|
+
count: matches.length,
|
|
396
|
+
matches,
|
|
397
|
+
};
|
|
398
|
+
if (cfg.lazyFallbackFullOnLowConfidence && topScore < cfg.lazyMinConfidenceScore) {
|
|
399
|
+
payload.fallback = 'full_tools_due_low_confidence';
|
|
400
|
+
payload.top_score = topScore;
|
|
401
|
+
payload.tools = state.getTools();
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
structuredContent: payload,
|
|
405
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function toolCacheAllowed(cfg, toolName) {
|
|
410
|
+
if (!toolName || !cfg.cachingEnabled) return false;
|
|
411
|
+
if (!featureEnabledForTool(cfg, toolName, 'caching', true)) return false;
|
|
412
|
+
if (!cfg.cacheMutatingTools && isMutatingToolName(toolName)) return false;
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function minifyRedundantTextContent(content, originalPayload) {
|
|
417
|
+
const kept = [];
|
|
418
|
+
let removed = 0;
|
|
419
|
+
for (const item of content) {
|
|
420
|
+
if (!item || typeof item !== 'object' || item.type !== 'text') {
|
|
421
|
+
kept.push(item);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const text = item.text;
|
|
425
|
+
if (typeof text !== 'string') {
|
|
426
|
+
kept.push(item);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
const trimmed = text.trim();
|
|
430
|
+
if (!(trimmed.startsWith('{') || trimmed.startsWith('['))) {
|
|
431
|
+
kept.push(item);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
const parsed = JSON.parse(trimmed);
|
|
436
|
+
if (JSON.stringify(parsed) === JSON.stringify(originalPayload)) {
|
|
437
|
+
removed += 1;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
} catch {
|
|
441
|
+
// keep on parse errors
|
|
442
|
+
}
|
|
443
|
+
kept.push(item);
|
|
444
|
+
}
|
|
445
|
+
if (removed > 0 && kept.length === 0) {
|
|
446
|
+
kept.push({ type: 'text', text: '[ultra-lean-mcp-proxy] structured result' });
|
|
447
|
+
}
|
|
448
|
+
return { content: kept, changed: removed > 0 };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function applyResultCompression(
|
|
452
|
+
result,
|
|
453
|
+
toolName,
|
|
454
|
+
cfg,
|
|
455
|
+
metrics,
|
|
456
|
+
tokenCounter,
|
|
457
|
+
featureStates,
|
|
458
|
+
keyRegistry,
|
|
459
|
+
keyRegistryCounter
|
|
460
|
+
) {
|
|
461
|
+
if (!cfg.resultCompressionEnabled) return result;
|
|
462
|
+
if (!featureEnabledForTool(cfg, toolName, 'result_compression', true)) return result;
|
|
463
|
+
|
|
464
|
+
const featureKey = featureHealthKey('result_compression', toolName);
|
|
465
|
+
if (!featureIsActive(featureStates, featureKey, cfg)) return result;
|
|
466
|
+
|
|
467
|
+
const options = makeCompressionOptions({
|
|
468
|
+
mode: cfg.resultCompressionMode,
|
|
469
|
+
stripNulls: cfg.resultStripNulls,
|
|
470
|
+
stripDefaults: cfg.resultStripDefaults,
|
|
471
|
+
minPayloadBytes: cfg.resultMinPayloadBytes,
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
let outcome = 'neutral';
|
|
475
|
+
try {
|
|
476
|
+
if (
|
|
477
|
+
result
|
|
478
|
+
&& typeof result === 'object'
|
|
479
|
+
&& !Array.isArray(result)
|
|
480
|
+
&& (Array.isArray(result.structuredContent) || (
|
|
481
|
+
result.structuredContent
|
|
482
|
+
&& typeof result.structuredContent === 'object'
|
|
483
|
+
))
|
|
484
|
+
) {
|
|
485
|
+
const out = cloneJson(result);
|
|
486
|
+
const original = out.structuredContent;
|
|
487
|
+
if (estimateCompressibility(original) < cfg.resultMinCompressibility) {
|
|
488
|
+
recordFeatureOutcome(featureStates, featureKey, 'neutral', cfg);
|
|
489
|
+
return result;
|
|
490
|
+
}
|
|
491
|
+
const env = compressResult(original, options, {
|
|
492
|
+
keyRegistry,
|
|
493
|
+
registryCounter: keyRegistryCounter,
|
|
494
|
+
reuseKeys: cfg.resultSharedKeyRegistry,
|
|
495
|
+
keyBootstrapInterval: cfg.resultKeyBootstrapInterval,
|
|
496
|
+
});
|
|
497
|
+
if (env.compressed) {
|
|
498
|
+
const tokenDelta = tokenSavings(original, env, tokenCounter);
|
|
499
|
+
const minRequired = Math.max(
|
|
500
|
+
cfg.resultMinTokenSavingsAbs,
|
|
501
|
+
Math.floor(tokenCounter.count(original) * cfg.resultMinTokenSavingsRatio)
|
|
502
|
+
);
|
|
503
|
+
if (tokenDelta >= minRequired) {
|
|
504
|
+
out.structuredContent = env;
|
|
505
|
+
if (!out._ultra_lean_mcp_proxy || typeof out._ultra_lean_mcp_proxy !== 'object') {
|
|
506
|
+
out._ultra_lean_mcp_proxy = {};
|
|
507
|
+
}
|
|
508
|
+
out._ultra_lean_mcp_proxy.result_compression = {
|
|
509
|
+
saved_bytes: env.savedBytes || 0,
|
|
510
|
+
saved_ratio: env.savedRatio || 0,
|
|
511
|
+
saved_tokens: tokenDelta,
|
|
512
|
+
};
|
|
513
|
+
metrics.resultCompressions += 1;
|
|
514
|
+
metrics.resultSavedBytes += Number(env.savedBytes || 0);
|
|
515
|
+
outcome = 'success';
|
|
516
|
+
if (cfg.resultMinifyRedundantText && Array.isArray(out.content)) {
|
|
517
|
+
const minified = minifyRedundantTextContent(out.content, original);
|
|
518
|
+
if (minified.changed) {
|
|
519
|
+
out.content = minified.content;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
recordFeatureOutcome(featureStates, featureKey, outcome, cfg);
|
|
523
|
+
return out;
|
|
524
|
+
}
|
|
525
|
+
if (tokenDelta < 0) outcome = 'hurt';
|
|
526
|
+
}
|
|
527
|
+
recordFeatureOutcome(featureStates, featureKey, outcome, cfg);
|
|
528
|
+
return result;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (result && typeof result === 'object' && !Array.isArray(result) && Array.isArray(result.content)) {
|
|
532
|
+
const out = cloneJson(result);
|
|
533
|
+
let changed = false;
|
|
534
|
+
let totalSavedBytes = 0;
|
|
535
|
+
let totalSavedTokens = 0;
|
|
536
|
+
for (const item of out.content) {
|
|
537
|
+
if (!item || typeof item !== 'object' || item.type !== 'text') continue;
|
|
538
|
+
if (typeof item.text !== 'string') continue;
|
|
539
|
+
const trimmed = item.text.trim();
|
|
540
|
+
if (!(trimmed.startsWith('{') || trimmed.startsWith('['))) continue;
|
|
541
|
+
let parsed;
|
|
542
|
+
try {
|
|
543
|
+
parsed = JSON.parse(trimmed);
|
|
544
|
+
} catch {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (estimateCompressibility(parsed) < cfg.resultMinCompressibility) continue;
|
|
548
|
+
const env = compressResult(parsed, options, {
|
|
549
|
+
keyRegistry,
|
|
550
|
+
registryCounter: keyRegistryCounter,
|
|
551
|
+
reuseKeys: cfg.resultSharedKeyRegistry,
|
|
552
|
+
keyBootstrapInterval: cfg.resultKeyBootstrapInterval,
|
|
553
|
+
});
|
|
554
|
+
if (!env.compressed) continue;
|
|
555
|
+
const tokenDelta = tokenSavings(parsed, env, tokenCounter);
|
|
556
|
+
const minRequired = Math.max(
|
|
557
|
+
cfg.resultMinTokenSavingsAbs,
|
|
558
|
+
Math.floor(tokenCounter.count(parsed) * cfg.resultMinTokenSavingsRatio)
|
|
559
|
+
);
|
|
560
|
+
if (tokenDelta >= minRequired) {
|
|
561
|
+
item.text = JSON.stringify(env);
|
|
562
|
+
changed = true;
|
|
563
|
+
totalSavedBytes += Number(env.savedBytes || 0);
|
|
564
|
+
totalSavedTokens += tokenDelta;
|
|
565
|
+
outcome = 'success';
|
|
566
|
+
} else if (tokenDelta < 0 && outcome !== 'success') {
|
|
567
|
+
outcome = 'hurt';
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (changed) {
|
|
571
|
+
if (!out._ultra_lean_mcp_proxy || typeof out._ultra_lean_mcp_proxy !== 'object') {
|
|
572
|
+
out._ultra_lean_mcp_proxy = {};
|
|
573
|
+
}
|
|
574
|
+
out._ultra_lean_mcp_proxy.result_compression = {
|
|
575
|
+
saved_bytes: totalSavedBytes,
|
|
576
|
+
saved_tokens: totalSavedTokens,
|
|
577
|
+
};
|
|
578
|
+
metrics.resultCompressions += 1;
|
|
579
|
+
metrics.resultSavedBytes += totalSavedBytes;
|
|
580
|
+
recordFeatureOutcome(featureStates, featureKey, 'success', cfg);
|
|
581
|
+
return out;
|
|
582
|
+
}
|
|
583
|
+
recordFeatureOutcome(featureStates, featureKey, outcome, cfg);
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
} catch {
|
|
587
|
+
recordFeatureOutcome(featureStates, featureKey, 'neutral', cfg);
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
recordFeatureOutcome(featureStates, featureKey, 'neutral', cfg);
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function applyDeltaResponse(result, historyKey, toolName, state, cfg, metrics, deltaCounters, tokenCounter) {
|
|
596
|
+
const previous = state.historyGet(historyKey);
|
|
597
|
+
state.historySet(historyKey, result);
|
|
598
|
+
|
|
599
|
+
if (!cfg.deltaResponsesEnabled) return result;
|
|
600
|
+
if (!featureEnabledForTool(cfg, toolName, 'delta_responses', true)) return result;
|
|
601
|
+
if (previous === null) {
|
|
602
|
+
deltaCounters[historyKey] = 0;
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
if ((deltaCounters[historyKey] || 0) >= cfg.deltaSnapshotInterval) {
|
|
606
|
+
deltaCounters[historyKey] = 0;
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const fullTokens = tokenCounter.count(result);
|
|
611
|
+
|
|
612
|
+
if (JSON.stringify(previous) === JSON.stringify(result)) {
|
|
613
|
+
const delta = {
|
|
614
|
+
encoding: 'lapc-delta-v1',
|
|
615
|
+
unchanged: true,
|
|
616
|
+
currentHash: stableHash(result),
|
|
617
|
+
};
|
|
618
|
+
const payload = { delta };
|
|
619
|
+
if (tokenCounter.count(payload) >= fullTokens) return result;
|
|
620
|
+
deltaCounters[historyKey] = (deltaCounters[historyKey] || 0) + 1;
|
|
621
|
+
metrics.deltaResponses += 1;
|
|
622
|
+
metrics.deltaSavedBytes += Math.max(0, jsonSize(result) - jsonSize(payload));
|
|
623
|
+
return {
|
|
624
|
+
structuredContent: payload,
|
|
625
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
const delta = createDelta(previous, result, cfg.deltaMinSavingsRatio, cfg.deltaMaxPatchBytes);
|
|
631
|
+
if (!delta) return result;
|
|
632
|
+
const patchRatio = Number(delta.fullBytes || 0) > 0
|
|
633
|
+
? Number(delta.patchBytes || 0) / Number(delta.fullBytes || 1)
|
|
634
|
+
: 0;
|
|
635
|
+
if (patchRatio > cfg.deltaMaxPatchRatio) return result;
|
|
636
|
+
const payload = { delta };
|
|
637
|
+
if (tokenCounter.count(payload) >= fullTokens) return result;
|
|
638
|
+
deltaCounters[historyKey] = (deltaCounters[historyKey] || 0) + 1;
|
|
639
|
+
metrics.deltaResponses += 1;
|
|
640
|
+
metrics.deltaSavedBytes += Number(delta.savedBytes || 0);
|
|
641
|
+
return {
|
|
642
|
+
structuredContent: payload,
|
|
643
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
644
|
+
};
|
|
645
|
+
} catch {
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function resolveSpawnOptions(commandName) {
|
|
651
|
+
const normalized = typeof commandName === 'string' ? commandName.trim().toLowerCase() : '';
|
|
652
|
+
const isCmdShim = normalized === 'cmd' || normalized === 'cmd.exe';
|
|
653
|
+
const useShellOnWindows = process.platform === 'win32'
|
|
654
|
+
&& typeof commandName === 'string'
|
|
655
|
+
&& !isCmdShim
|
|
656
|
+
&& !/[\\/]/.test(commandName)
|
|
657
|
+
&& !/^[A-Za-z]:/.test(commandName);
|
|
658
|
+
return {
|
|
659
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
660
|
+
...(useShellOnWindows ? { shell: true } : {}),
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function parseMessageLine(line) {
|
|
665
|
+
try {
|
|
666
|
+
return JSON.parse(line);
|
|
667
|
+
} catch {
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function traceInbound(traceRpc, msg) {
|
|
673
|
+
if (!traceRpc) return;
|
|
674
|
+
if (msg.method) {
|
|
675
|
+
const idPart = msg.id !== undefined ? ` id=${msg.id}` : '';
|
|
676
|
+
const kind = msg.id !== undefined ? 'request' : 'notification';
|
|
677
|
+
process.stderr.write(`[ultra-lean-mcp-proxy] rpc<- client ${kind} method=${msg.method}${idPart}\n`);
|
|
678
|
+
} else if (msg.id !== undefined) {
|
|
679
|
+
process.stderr.write(`[ultra-lean-mcp-proxy] rpc<- client response id=${msg.id}\n`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function traceUpstream(traceRpc, msg, pending) {
|
|
684
|
+
if (!traceRpc) return;
|
|
685
|
+
if (msg.method) {
|
|
686
|
+
const idPart = msg.id !== undefined ? ` id=${msg.id}` : '';
|
|
687
|
+
const kind = msg.id !== undefined ? 'request' : 'notification';
|
|
688
|
+
process.stderr.write(`[ultra-lean-mcp-proxy] rpc<- upstream ${kind} method=${msg.method}${idPart}\n`);
|
|
689
|
+
} else if (msg.id !== undefined) {
|
|
690
|
+
const req = pending.get(msg.id);
|
|
691
|
+
const methodPart = req?.method ? ` method=${req.method}` : '';
|
|
692
|
+
const status = msg.error ? 'error' : 'result';
|
|
693
|
+
process.stderr.write(
|
|
694
|
+
`[ultra-lean-mcp-proxy] rpc<- upstream response id=${msg.id}${methodPart} status=${status}\n`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export function runProxy(upstreamCommand, options = {}) {
|
|
700
|
+
const traceRpc = Boolean(options.traceRpc);
|
|
701
|
+
const cfg = loadProxyConfig({
|
|
702
|
+
upstreamCommand,
|
|
703
|
+
configPath: options.configPath || null,
|
|
704
|
+
cliOverrides: {
|
|
705
|
+
stats: options.stats,
|
|
706
|
+
verbose: options.verbose,
|
|
707
|
+
sessionId: options.sessionId,
|
|
708
|
+
strictConfig: options.strictConfig,
|
|
709
|
+
resultCompression: options.resultCompression,
|
|
710
|
+
deltaResponses: options.deltaResponses,
|
|
711
|
+
lazyLoading: options.lazyLoading,
|
|
712
|
+
toolsHashSync: options.toolsHashSync,
|
|
713
|
+
caching: options.caching,
|
|
714
|
+
cacheTtl: options.cacheTtl,
|
|
715
|
+
deltaMinSavings: options.deltaMinSavings,
|
|
716
|
+
lazyMode: options.lazyMode,
|
|
717
|
+
toolsHashRefreshInterval: options.toolsHashRefreshInterval,
|
|
718
|
+
searchTopK: options.searchTopK,
|
|
719
|
+
resultCompressionMode: options.resultCompressionMode,
|
|
720
|
+
configPath: options.configPath,
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
if (options.stats) cfg.stats = true;
|
|
725
|
+
|
|
726
|
+
if (options.dumpEffectiveConfig) {
|
|
727
|
+
process.stderr.write(`${JSON.stringify(cfg, null, 2)}\n`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const featureSummary = [
|
|
731
|
+
`definition=${cfg.definitionCompressionEnabled ? 'on' : 'off'}`,
|
|
732
|
+
`result=${cfg.resultCompressionEnabled ? 'on' : 'off'}`,
|
|
733
|
+
`delta=${cfg.deltaResponsesEnabled ? 'on' : 'off'}`,
|
|
734
|
+
`lazy=${cfg.lazyLoadingEnabled ? cfg.lazyMode : 'off'}`,
|
|
735
|
+
`tools_hash_sync=${cfg.toolsHashSyncEnabled ? 'on' : 'off'}`,
|
|
736
|
+
`cache=${cfg.cachingEnabled ? 'on' : 'off'}`,
|
|
737
|
+
].join(',');
|
|
738
|
+
process.stderr.write(`[ultra-lean-mcp-proxy] runtime=npm features=${featureSummary}\n`);
|
|
739
|
+
if (traceRpc) {
|
|
740
|
+
process.stderr.write('[ultra-lean-mcp-proxy] trace-rpc enabled\n');
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const upstream = spawn(upstreamCommand[0], upstreamCommand.slice(1), resolveSpawnOptions(upstreamCommand[0]));
|
|
744
|
+
const profileFingerprint = buildProfileFingerprint(cfg, upstreamCommand);
|
|
745
|
+
|
|
746
|
+
const pending = new Map();
|
|
747
|
+
const state = new ProxyState(cfg.cacheMaxEntries);
|
|
748
|
+
const tokenCounter = new TokenCounter();
|
|
749
|
+
const featureStates = {};
|
|
750
|
+
const deltaCounters = {};
|
|
751
|
+
const keyRegistry = {};
|
|
752
|
+
const keyRegistryCounter = {};
|
|
753
|
+
let toolsHashSyncNegotiated = false;
|
|
754
|
+
|
|
755
|
+
const metrics = {
|
|
756
|
+
toolsListRequests: 0,
|
|
757
|
+
toolsListSavedBytes: 0,
|
|
758
|
+
toolsHashSyncHits: 0,
|
|
759
|
+
toolsHashSyncMisses: 0,
|
|
760
|
+
toolsHashSyncNotModified: 0,
|
|
761
|
+
toolsHashSyncSavedBytes: 0,
|
|
762
|
+
toolsHashSyncSavedTokens: 0,
|
|
763
|
+
cacheHits: 0,
|
|
764
|
+
cacheMisses: 0,
|
|
765
|
+
resultCompressions: 0,
|
|
766
|
+
resultSavedBytes: 0,
|
|
767
|
+
deltaResponses: 0,
|
|
768
|
+
deltaSavedBytes: 0,
|
|
769
|
+
searchCalls: 0,
|
|
770
|
+
upstreamRequests: 0,
|
|
771
|
+
upstreamRequestBytes: 0,
|
|
772
|
+
upstreamRequestTokens: 0,
|
|
773
|
+
upstreamResponses: 0,
|
|
774
|
+
upstreamResponseBytes: 0,
|
|
775
|
+
upstreamResponseTokens: 0,
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
function sendToClient(msg) {
|
|
779
|
+
if (cfg.stats && msg && typeof msg === 'object') {
|
|
780
|
+
const result = msg.result;
|
|
781
|
+
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
|
782
|
+
if (!result._ultra_lean_mcp_proxy || typeof result._ultra_lean_mcp_proxy !== 'object') {
|
|
783
|
+
result._ultra_lean_mcp_proxy = {};
|
|
784
|
+
}
|
|
785
|
+
result._ultra_lean_mcp_proxy.runtime_metrics = runtimeMetricsSnapshot(metrics);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
process.stdout.write(`${JSON.stringify(msg)}\n`);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
createLineReader(process.stdin, (line) => {
|
|
792
|
+
const msg = parseMessageLine(line);
|
|
793
|
+
if (!msg) {
|
|
794
|
+
if (traceRpc) {
|
|
795
|
+
process.stderr.write('[ultra-lean-mcp-proxy] rpc<- client non-json-line\n');
|
|
796
|
+
}
|
|
797
|
+
upstream.stdin.write(`${line}\n`);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
traceInbound(traceRpc, msg);
|
|
802
|
+
|
|
803
|
+
const method = msg.method;
|
|
804
|
+
const reqId = msg.id;
|
|
805
|
+
let shortCircuited = false;
|
|
806
|
+
|
|
807
|
+
if (typeof method === 'string' && reqId !== undefined) {
|
|
808
|
+
try {
|
|
809
|
+
if (method === 'initialize') {
|
|
810
|
+
pending.set(reqId, {
|
|
811
|
+
method,
|
|
812
|
+
clientToolsHashSyncSupported: clientSupportsToolsHashSync(msg.params),
|
|
813
|
+
});
|
|
814
|
+
} else if (method === 'tools/list') {
|
|
815
|
+
const conditional = extractToolsHashIfNoneMatch(msg.params, cfg.toolsHashSyncAlgorithm);
|
|
816
|
+
if (
|
|
817
|
+
cfg.toolsHashSyncEnabled
|
|
818
|
+
&& toolsHashSyncNegotiated
|
|
819
|
+
&& conditional.valid
|
|
820
|
+
&& typeof conditional.value === 'string'
|
|
821
|
+
) {
|
|
822
|
+
const scopeKey = toolsHashScopeKey(cfg, profileFingerprint);
|
|
823
|
+
const entry = state.toolsHashGet(scopeKey);
|
|
824
|
+
if (entry && entry.lastHash === conditional.value) {
|
|
825
|
+
const nextHit = (entry.conditionalHits || 0) + 1;
|
|
826
|
+
const forceRefresh = (nextHit % cfg.toolsHashSyncRefreshInterval) === 0;
|
|
827
|
+
if (!forceRefresh) {
|
|
828
|
+
state.toolsHashRecordHit(scopeKey);
|
|
829
|
+
metrics.toolsHashSyncHits += 1;
|
|
830
|
+
metrics.toolsHashSyncNotModified += 1;
|
|
831
|
+
sendToClient({
|
|
832
|
+
jsonrpc: msg.jsonrpc || '2.0',
|
|
833
|
+
id: reqId,
|
|
834
|
+
result: {
|
|
835
|
+
tools: [],
|
|
836
|
+
_ultra_lean_mcp_proxy: {
|
|
837
|
+
tools_hash_sync: {
|
|
838
|
+
not_modified: true,
|
|
839
|
+
tools_hash: conditional.value,
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
});
|
|
844
|
+
shortCircuited = true;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (!shortCircuited) {
|
|
849
|
+
pending.set(reqId, {
|
|
850
|
+
method,
|
|
851
|
+
toolsHashIfNoneMatch: conditional.value,
|
|
852
|
+
toolsHashIfNoneMatchProvided: conditional.provided,
|
|
853
|
+
toolsHashIfNoneMatchValid: conditional.valid,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
} else if (method === 'tools/call') {
|
|
857
|
+
const [toolName, argumentsValue] = extractToolCall(msg);
|
|
858
|
+
|
|
859
|
+
if (cfg.lazyLoadingEnabled && toolName === SEARCH_TOOL_NAME) {
|
|
860
|
+
let searchResult = buildSearchResult(state, cfg, argumentsValue);
|
|
861
|
+
searchResult = applyResultCompression(
|
|
862
|
+
searchResult,
|
|
863
|
+
toolName,
|
|
864
|
+
cfg,
|
|
865
|
+
metrics,
|
|
866
|
+
tokenCounter,
|
|
867
|
+
featureStates,
|
|
868
|
+
keyRegistry,
|
|
869
|
+
keyRegistryCounter
|
|
870
|
+
);
|
|
871
|
+
metrics.searchCalls += 1;
|
|
872
|
+
sendToClient({
|
|
873
|
+
jsonrpc: msg.jsonrpc || '2.0',
|
|
874
|
+
id: reqId,
|
|
875
|
+
result: searchResult,
|
|
876
|
+
});
|
|
877
|
+
shortCircuited = true;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (!shortCircuited) {
|
|
881
|
+
let cacheKey = null;
|
|
882
|
+
if (toolCacheAllowed(cfg, toolName)) {
|
|
883
|
+
cacheKey = makeCacheKey(cfg.sessionId, cfg.serverName, toolName, argumentsValue);
|
|
884
|
+
const cached = state.cacheGet(cacheKey);
|
|
885
|
+
if (cached !== null) {
|
|
886
|
+
metrics.cacheHits += 1;
|
|
887
|
+
const delivered = applyDeltaResponse(
|
|
888
|
+
cached,
|
|
889
|
+
cacheKey,
|
|
890
|
+
toolName,
|
|
891
|
+
state,
|
|
892
|
+
cfg,
|
|
893
|
+
metrics,
|
|
894
|
+
deltaCounters,
|
|
895
|
+
tokenCounter
|
|
896
|
+
);
|
|
897
|
+
sendToClient({
|
|
898
|
+
jsonrpc: msg.jsonrpc || '2.0',
|
|
899
|
+
id: reqId,
|
|
900
|
+
result: delivered,
|
|
901
|
+
});
|
|
902
|
+
shortCircuited = true;
|
|
903
|
+
} else {
|
|
904
|
+
metrics.cacheMisses += 1;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (!shortCircuited) {
|
|
909
|
+
pending.set(reqId, {
|
|
910
|
+
method,
|
|
911
|
+
toolName,
|
|
912
|
+
argumentsValue,
|
|
913
|
+
cacheKey,
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
} else {
|
|
918
|
+
pending.set(reqId, { method });
|
|
919
|
+
}
|
|
920
|
+
} catch {
|
|
921
|
+
// fail-open
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (shortCircuited) {
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
upstream.stdin.write(`${JSON.stringify(msg)}\n`);
|
|
930
|
+
metrics.upstreamRequests += 1;
|
|
931
|
+
metrics.upstreamRequestBytes += jsonSize(msg);
|
|
932
|
+
metrics.upstreamRequestTokens += tokenCounter.count(msg);
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
createLineReader(upstream.stdout, (line) => {
|
|
936
|
+
const msg = parseMessageLine(line);
|
|
937
|
+
if (!msg) {
|
|
938
|
+
if (traceRpc) {
|
|
939
|
+
process.stderr.write('[ultra-lean-mcp-proxy] rpc<- upstream non-json-line\n');
|
|
940
|
+
}
|
|
941
|
+
process.stdout.write(`${line}\n`);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
metrics.upstreamResponses += 1;
|
|
946
|
+
metrics.upstreamResponseBytes += jsonSize(msg);
|
|
947
|
+
metrics.upstreamResponseTokens += tokenCounter.count(msg);
|
|
948
|
+
|
|
949
|
+
traceUpstream(traceRpc, msg, pending);
|
|
950
|
+
|
|
951
|
+
const reqId = msg.id;
|
|
952
|
+
if (reqId !== undefined && Object.prototype.hasOwnProperty.call(msg, 'result')) {
|
|
953
|
+
const pendingReq = pending.get(reqId);
|
|
954
|
+
pending.delete(reqId);
|
|
955
|
+
|
|
956
|
+
if (pendingReq && pendingReq.method === 'initialize') {
|
|
957
|
+
if (cfg.toolsHashSyncEnabled && pendingReq.clientToolsHashSyncSupported) {
|
|
958
|
+
toolsHashSyncNegotiated = true;
|
|
959
|
+
try {
|
|
960
|
+
msg.result = injectInitializeToolsHashCapability(msg.result, cfg.toolsHashSyncAlgorithm);
|
|
961
|
+
} catch {
|
|
962
|
+
// fail-open
|
|
963
|
+
}
|
|
964
|
+
} else {
|
|
965
|
+
toolsHashSyncNegotiated = false;
|
|
966
|
+
}
|
|
967
|
+
} else if (pendingReq && pendingReq.method === 'tools/list') {
|
|
968
|
+
try {
|
|
969
|
+
msg.result = handleToolsListResult(
|
|
970
|
+
msg.result,
|
|
971
|
+
state,
|
|
972
|
+
cfg,
|
|
973
|
+
metrics,
|
|
974
|
+
tokenCounter,
|
|
975
|
+
{
|
|
976
|
+
toolsHashSyncNegotiated,
|
|
977
|
+
profileFingerprint,
|
|
978
|
+
ifNoneMatch: pendingReq.toolsHashIfNoneMatch,
|
|
979
|
+
ifNoneMatchProvided: pendingReq.toolsHashIfNoneMatchProvided,
|
|
980
|
+
ifNoneMatchValid: pendingReq.toolsHashIfNoneMatchValid,
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
} catch {
|
|
984
|
+
// fail-open
|
|
985
|
+
}
|
|
986
|
+
} else if (pendingReq && pendingReq.method === 'tools/call') {
|
|
987
|
+
try {
|
|
988
|
+
const rawUpstreamResult = cloneJson(msg.result);
|
|
989
|
+
let result = msg.result;
|
|
990
|
+
result = applyResultCompression(
|
|
991
|
+
result,
|
|
992
|
+
pendingReq.toolName,
|
|
993
|
+
cfg,
|
|
994
|
+
metrics,
|
|
995
|
+
tokenCounter,
|
|
996
|
+
featureStates,
|
|
997
|
+
keyRegistry,
|
|
998
|
+
keyRegistryCounter
|
|
999
|
+
);
|
|
1000
|
+
|
|
1001
|
+
if (
|
|
1002
|
+
cfg.cachingEnabled
|
|
1003
|
+
&& !cfg.cacheMutatingTools
|
|
1004
|
+
&& pendingReq.toolName
|
|
1005
|
+
&& isMutatingToolName(pendingReq.toolName)
|
|
1006
|
+
) {
|
|
1007
|
+
const scopePrefix = `${cfg.sessionId}:${cfg.serverName}:`;
|
|
1008
|
+
state.cacheInvalidatePrefix(scopePrefix);
|
|
1009
|
+
state.historyInvalidatePrefix(`cache_raw:${scopePrefix}`);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const cacheKey = pendingReq.cacheKey;
|
|
1013
|
+
if (cacheKey && toolCacheAllowed(cfg, pendingReq.toolName)) {
|
|
1014
|
+
const baseTtl = cacheTtlForTool(cfg, pendingReq.toolName);
|
|
1015
|
+
let ttl = baseTtl;
|
|
1016
|
+
if (cfg.cacheAdaptiveTtl && baseTtl > 0) {
|
|
1017
|
+
const rawKey = `cache_raw:${cacheKey}`;
|
|
1018
|
+
const previousRaw = state.historyGet(rawKey);
|
|
1019
|
+
if (previousRaw !== null) {
|
|
1020
|
+
const changed = JSON.stringify(previousRaw) !== JSON.stringify(rawUpstreamResult);
|
|
1021
|
+
if (changed) {
|
|
1022
|
+
ttl = Math.max(cfg.cacheTtlMinSeconds, Math.floor(baseTtl * 0.5));
|
|
1023
|
+
} else {
|
|
1024
|
+
ttl = Math.min(cfg.cacheTtlMaxSeconds, Math.floor(baseTtl * 1.5));
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
ttl = Math.min(Math.max(ttl, cfg.cacheTtlMinSeconds), cfg.cacheTtlMaxSeconds);
|
|
1028
|
+
state.historySet(rawKey, rawUpstreamResult);
|
|
1029
|
+
}
|
|
1030
|
+
state.cacheSet(cacheKey, result, ttl);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
const historyKey = cacheKey || makeCacheKey(
|
|
1034
|
+
cfg.sessionId,
|
|
1035
|
+
cfg.serverName,
|
|
1036
|
+
pendingReq.toolName || '_unknown',
|
|
1037
|
+
pendingReq.argumentsValue || {}
|
|
1038
|
+
);
|
|
1039
|
+
result = applyDeltaResponse(
|
|
1040
|
+
result,
|
|
1041
|
+
historyKey,
|
|
1042
|
+
pendingReq.toolName,
|
|
1043
|
+
state,
|
|
1044
|
+
cfg,
|
|
1045
|
+
metrics,
|
|
1046
|
+
deltaCounters,
|
|
1047
|
+
tokenCounter
|
|
1048
|
+
);
|
|
1049
|
+
msg.result = result;
|
|
1050
|
+
} catch {
|
|
1051
|
+
// fail-open
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
} else if (reqId !== undefined && Object.prototype.hasOwnProperty.call(msg, 'error')) {
|
|
1055
|
+
const pendingReq = pending.get(reqId);
|
|
1056
|
+
pending.delete(reqId);
|
|
1057
|
+
if (pendingReq && pendingReq.method === 'initialize') {
|
|
1058
|
+
toolsHashSyncNegotiated = false;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
sendToClient(msg);
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
upstream.stderr.on('data', (chunk) => {
|
|
1066
|
+
process.stderr.write(chunk);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
function shutdown(code) {
|
|
1070
|
+
if (cfg.stats) {
|
|
1071
|
+
process.stderr.write(
|
|
1072
|
+
`[ultra-lean-mcp-proxy] stats: tools/list=${metrics.toolsListRequests} saved=${metrics.toolsListSavedBytes}B `
|
|
1073
|
+
+ `tools_hash_sync hit=${metrics.toolsHashSyncHits} miss=${metrics.toolsHashSyncMisses} `
|
|
1074
|
+
+ `not_modified=${metrics.toolsHashSyncNotModified} saved=${metrics.toolsHashSyncSavedBytes}B/`
|
|
1075
|
+
+ `${metrics.toolsHashSyncSavedTokens}tok cache hit=${metrics.cacheHits} miss=${metrics.cacheMisses} `
|
|
1076
|
+
+ `result_compression=${metrics.resultCompressions} saved=${metrics.resultSavedBytes}B `
|
|
1077
|
+
+ `delta=${metrics.deltaResponses} saved=${metrics.deltaSavedBytes}B `
|
|
1078
|
+
+ `search_calls=${metrics.searchCalls} upstream req=${metrics.upstreamRequests}/`
|
|
1079
|
+
+ `${metrics.upstreamRequestTokens}tok/${metrics.upstreamRequestBytes}B rsp=${metrics.upstreamResponses}/`
|
|
1080
|
+
+ `${metrics.upstreamResponseTokens}tok/${metrics.upstreamResponseBytes}B\n`
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
process.exit(code ?? 0);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
process.stdin.on('end', () => {
|
|
1087
|
+
try {
|
|
1088
|
+
upstream.stdin.end();
|
|
1089
|
+
} catch {
|
|
1090
|
+
// ignore
|
|
1091
|
+
}
|
|
1092
|
+
setTimeout(() => {
|
|
1093
|
+
try {
|
|
1094
|
+
upstream.kill('SIGTERM');
|
|
1095
|
+
} catch {
|
|
1096
|
+
// ignore
|
|
1097
|
+
}
|
|
1098
|
+
}, 2000);
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
upstream.on('exit', (code) => {
|
|
1102
|
+
shutdown(code);
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
upstream.on('error', (err) => {
|
|
1106
|
+
process.stderr.write(`[ultra-lean-mcp-proxy] upstream error: ${err.message}\n`);
|
|
1107
|
+
process.exit(1);
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
for (const sig of ['SIGINT', 'SIGTERM']) {
|
|
1111
|
+
process.on(sig, () => {
|
|
1112
|
+
try {
|
|
1113
|
+
upstream.kill(sig);
|
|
1114
|
+
} catch {
|
|
1115
|
+
// ignore
|
|
1116
|
+
}
|
|
1117
|
+
setTimeout(() => process.exit(1), 3000);
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
return new Promise(() => {});
|
|
1122
|
+
}
|