@juspay/neurolink 9.26.2 → 9.28.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 +12 -0
- package/README.md +59 -9
- package/dist/cli/commands/config.d.ts +4 -4
- package/dist/cli/commands/mcp.d.ts +87 -0
- package/dist/cli/commands/mcp.js +1524 -0
- package/dist/cli/loop/optionsSchema.js +4 -0
- package/dist/core/modules/ToolsManager.js +29 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +27 -1
- package/dist/lib/core/modules/ToolsManager.js +29 -2
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +27 -1
- package/dist/lib/mcp/agentExposure.d.ts +228 -0
- package/dist/lib/mcp/agentExposure.js +357 -0
- package/dist/lib/mcp/batching/index.d.ts +11 -0
- package/dist/lib/mcp/batching/index.js +11 -0
- package/dist/lib/mcp/batching/requestBatcher.d.ts +202 -0
- package/dist/lib/mcp/batching/requestBatcher.js +442 -0
- package/dist/lib/mcp/caching/index.d.ts +11 -0
- package/dist/lib/mcp/caching/index.js +11 -0
- package/dist/lib/mcp/caching/toolCache.d.ts +221 -0
- package/dist/lib/mcp/caching/toolCache.js +434 -0
- package/dist/lib/mcp/elicitation/elicitationManager.d.ts +169 -0
- package/dist/lib/mcp/elicitation/elicitationManager.js +377 -0
- package/dist/lib/mcp/elicitation/index.d.ts +11 -0
- package/dist/lib/mcp/elicitation/index.js +12 -0
- package/dist/lib/mcp/elicitation/types.d.ts +278 -0
- package/dist/lib/mcp/elicitation/types.js +11 -0
- package/dist/lib/mcp/elicitationProtocol.d.ts +228 -0
- package/dist/lib/mcp/elicitationProtocol.js +376 -0
- package/dist/lib/mcp/enhancedToolDiscovery.d.ts +205 -0
- package/dist/lib/mcp/enhancedToolDiscovery.js +482 -0
- package/dist/lib/mcp/index.d.ts +38 -1
- package/dist/lib/mcp/index.js +36 -3
- package/dist/lib/mcp/mcpRegistryClient.d.ts +332 -0
- package/dist/lib/mcp/mcpRegistryClient.js +489 -0
- package/dist/lib/mcp/mcpServerBase.d.ts +227 -0
- package/dist/lib/mcp/mcpServerBase.js +374 -0
- package/dist/lib/mcp/multiServerManager.d.ts +310 -0
- package/dist/lib/mcp/multiServerManager.js +580 -0
- package/dist/lib/mcp/routing/index.d.ts +11 -0
- package/dist/lib/mcp/routing/index.js +11 -0
- package/dist/lib/mcp/routing/toolRouter.d.ts +219 -0
- package/dist/lib/mcp/routing/toolRouter.js +417 -0
- package/dist/lib/mcp/serverCapabilities.d.ts +341 -0
- package/dist/lib/mcp/serverCapabilities.js +503 -0
- package/dist/lib/mcp/toolAnnotations.d.ts +154 -0
- package/dist/lib/mcp/toolAnnotations.js +240 -0
- package/dist/lib/mcp/toolConverter.d.ts +178 -0
- package/dist/lib/mcp/toolConverter.js +259 -0
- package/dist/lib/mcp/toolIntegration.d.ts +136 -0
- package/dist/lib/mcp/toolIntegration.js +335 -0
- package/dist/lib/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/lib/memory/hippocampusInitializer.js +1 -1
- package/dist/lib/neurolink.d.ts +275 -2
- package/dist/lib/neurolink.js +596 -56
- package/dist/lib/providers/litellm.d.ts +10 -0
- package/dist/lib/providers/litellm.js +104 -2
- package/dist/lib/types/configTypes.d.ts +56 -0
- package/dist/lib/types/conversation.d.ts +2 -2
- package/dist/lib/types/generateTypes.d.ts +4 -0
- package/dist/lib/types/index.d.ts +2 -1
- package/dist/lib/types/modelTypes.d.ts +6 -6
- package/dist/lib/types/streamTypes.d.ts +2 -0
- package/dist/lib/types/tools.d.ts +2 -0
- package/dist/lib/utils/pricing.js +177 -17
- package/dist/lib/utils/schemaConversion.d.ts +6 -1
- package/dist/lib/utils/schemaConversion.js +50 -28
- package/dist/lib/workflow/config.d.ts +16 -16
- package/dist/mcp/agentExposure.d.ts +228 -0
- package/dist/mcp/agentExposure.js +356 -0
- package/dist/mcp/batching/index.d.ts +11 -0
- package/dist/mcp/batching/index.js +10 -0
- package/dist/mcp/batching/requestBatcher.d.ts +202 -0
- package/dist/mcp/batching/requestBatcher.js +441 -0
- package/dist/mcp/caching/index.d.ts +11 -0
- package/dist/mcp/caching/index.js +10 -0
- package/dist/mcp/caching/toolCache.d.ts +221 -0
- package/dist/mcp/caching/toolCache.js +433 -0
- package/dist/mcp/elicitation/elicitationManager.d.ts +169 -0
- package/dist/mcp/elicitation/elicitationManager.js +376 -0
- package/dist/mcp/elicitation/index.d.ts +11 -0
- package/dist/mcp/elicitation/index.js +11 -0
- package/dist/mcp/elicitation/types.d.ts +278 -0
- package/dist/mcp/elicitation/types.js +10 -0
- package/dist/mcp/elicitationProtocol.d.ts +228 -0
- package/dist/mcp/elicitationProtocol.js +375 -0
- package/dist/mcp/enhancedToolDiscovery.d.ts +205 -0
- package/dist/mcp/enhancedToolDiscovery.js +481 -0
- package/dist/mcp/index.d.ts +38 -1
- package/dist/mcp/index.js +36 -3
- package/dist/mcp/mcpRegistryClient.d.ts +332 -0
- package/dist/mcp/mcpRegistryClient.js +488 -0
- package/dist/mcp/mcpServerBase.d.ts +227 -0
- package/dist/mcp/mcpServerBase.js +373 -0
- package/dist/mcp/multiServerManager.d.ts +310 -0
- package/dist/mcp/multiServerManager.js +579 -0
- package/dist/mcp/routing/index.d.ts +11 -0
- package/dist/mcp/routing/index.js +10 -0
- package/dist/mcp/routing/toolRouter.d.ts +219 -0
- package/dist/mcp/routing/toolRouter.js +416 -0
- package/dist/mcp/serverCapabilities.d.ts +341 -0
- package/dist/mcp/serverCapabilities.js +502 -0
- package/dist/mcp/toolAnnotations.d.ts +154 -0
- package/dist/mcp/toolAnnotations.js +239 -0
- package/dist/mcp/toolConverter.d.ts +178 -0
- package/dist/mcp/toolConverter.js +258 -0
- package/dist/mcp/toolIntegration.d.ts +136 -0
- package/dist/mcp/toolIntegration.js +334 -0
- package/dist/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/memory/hippocampusInitializer.js +1 -1
- package/dist/neurolink.d.ts +275 -2
- package/dist/neurolink.js +596 -56
- package/dist/providers/litellm.d.ts +10 -0
- package/dist/providers/litellm.js +104 -2
- package/dist/types/configTypes.d.ts +56 -0
- package/dist/types/conversation.d.ts +2 -2
- package/dist/types/generateTypes.d.ts +4 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/streamTypes.d.ts +2 -0
- package/dist/types/tools.d.ts +2 -0
- package/dist/utils/pricing.js +177 -17
- package/dist/utils/schemaConversion.d.ts +6 -1
- package/dist/utils/schemaConversion.js +50 -28
- package/package.json +2 -2
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Cache - Caches tool results and server responses
|
|
3
|
+
*
|
|
4
|
+
* Provides intelligent caching for MCP tool calls to improve performance
|
|
5
|
+
* and reduce redundant operations. Supports multiple eviction strategies:
|
|
6
|
+
* - LRU (Least Recently Used)
|
|
7
|
+
* - FIFO (First In, First Out)
|
|
8
|
+
* - LFU (Least Frequently Used)
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from "crypto";
|
|
11
|
+
import { EventEmitter } from "events";
|
|
12
|
+
import { withTimeout } from "../../utils/async/withTimeout.js";
|
|
13
|
+
/**
|
|
14
|
+
* Tool Cache - High-performance caching for MCP tool results
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const cache = new ToolCache({
|
|
19
|
+
* ttl: 60000, // 1 minute
|
|
20
|
+
* maxSize: 500,
|
|
21
|
+
* strategy: 'lru',
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Cache a tool result
|
|
25
|
+
* cache.set('getUserById:123', { id: 123, name: 'John' });
|
|
26
|
+
*
|
|
27
|
+
* // Retrieve from cache
|
|
28
|
+
* const user = cache.get('getUserById:123');
|
|
29
|
+
*
|
|
30
|
+
* // Invalidate by pattern
|
|
31
|
+
* cache.invalidate('getUserById:*');
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class ToolCache extends EventEmitter {
|
|
35
|
+
cache = new Map();
|
|
36
|
+
config;
|
|
37
|
+
stats;
|
|
38
|
+
cleanupTimer;
|
|
39
|
+
constructor(config) {
|
|
40
|
+
super();
|
|
41
|
+
this.config = {
|
|
42
|
+
ttl: config.ttl,
|
|
43
|
+
maxSize: config.maxSize,
|
|
44
|
+
strategy: config.strategy,
|
|
45
|
+
enableAutoCleanup: config.enableAutoCleanup ?? true,
|
|
46
|
+
cleanupInterval: config.cleanupInterval ?? 60000,
|
|
47
|
+
namespace: config.namespace ?? "",
|
|
48
|
+
};
|
|
49
|
+
this.stats = {
|
|
50
|
+
hits: 0,
|
|
51
|
+
misses: 0,
|
|
52
|
+
evictions: 0,
|
|
53
|
+
size: 0,
|
|
54
|
+
maxSize: this.config.maxSize,
|
|
55
|
+
hitRate: 0,
|
|
56
|
+
};
|
|
57
|
+
if (this.config.enableAutoCleanup) {
|
|
58
|
+
this.startAutoCleanup();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a value from the cache
|
|
63
|
+
*/
|
|
64
|
+
get(key) {
|
|
65
|
+
const fullKey = this.getFullKey(key);
|
|
66
|
+
const entry = this.cache.get(fullKey);
|
|
67
|
+
if (!entry) {
|
|
68
|
+
this.stats.misses++;
|
|
69
|
+
this.updateHitRate();
|
|
70
|
+
this.emit("miss", { key: fullKey });
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
// Check expiration
|
|
74
|
+
if (this.isExpired(entry)) {
|
|
75
|
+
this.deleteWithReason(fullKey, "expired");
|
|
76
|
+
this.stats.misses++;
|
|
77
|
+
this.updateHitRate();
|
|
78
|
+
this.emit("miss", { key: fullKey });
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
// Update access metadata
|
|
82
|
+
entry.accessedAt = Date.now();
|
|
83
|
+
entry.accessCount++;
|
|
84
|
+
this.stats.hits++;
|
|
85
|
+
this.updateHitRate();
|
|
86
|
+
this.emit("hit", { key: fullKey, value: entry.value });
|
|
87
|
+
return entry.value;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Set a value in the cache
|
|
91
|
+
*/
|
|
92
|
+
set(key, value, ttl) {
|
|
93
|
+
const fullKey = this.getFullKey(key);
|
|
94
|
+
const effectiveTtl = ttl ?? this.config.ttl;
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
// Evict if at capacity
|
|
97
|
+
if (this.cache.size >= this.config.maxSize && !this.cache.has(fullKey)) {
|
|
98
|
+
this.evictOne();
|
|
99
|
+
}
|
|
100
|
+
const entry = {
|
|
101
|
+
value,
|
|
102
|
+
expires: now + effectiveTtl,
|
|
103
|
+
createdAt: now,
|
|
104
|
+
accessedAt: now,
|
|
105
|
+
accessCount: 1,
|
|
106
|
+
key: fullKey,
|
|
107
|
+
};
|
|
108
|
+
this.cache.set(fullKey, entry);
|
|
109
|
+
this.stats.size = this.cache.size;
|
|
110
|
+
this.emit("set", { key: fullKey, value, ttl: effectiveTtl });
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if a key exists and is not expired
|
|
114
|
+
*/
|
|
115
|
+
has(key) {
|
|
116
|
+
const fullKey = this.getFullKey(key);
|
|
117
|
+
const entry = this.cache.get(fullKey);
|
|
118
|
+
if (!entry) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (this.isExpired(entry)) {
|
|
122
|
+
this.deleteWithReason(fullKey, "expired");
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Delete a specific key from the cache
|
|
129
|
+
*/
|
|
130
|
+
delete(key) {
|
|
131
|
+
const fullKey = this.getFullKey(key);
|
|
132
|
+
const deleted = this.cache.delete(fullKey);
|
|
133
|
+
if (deleted) {
|
|
134
|
+
this.stats.size = this.cache.size;
|
|
135
|
+
this.emit("evict", { key: fullKey, reason: "manual" });
|
|
136
|
+
}
|
|
137
|
+
return deleted;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Invalidate entries matching a pattern
|
|
141
|
+
* Supports glob-style patterns with * wildcard
|
|
142
|
+
*/
|
|
143
|
+
invalidate(pattern) {
|
|
144
|
+
const fullPattern = this.getFullKey(pattern);
|
|
145
|
+
const regex = this.patternToRegex(fullPattern);
|
|
146
|
+
let invalidated = 0;
|
|
147
|
+
for (const key of this.cache.keys()) {
|
|
148
|
+
if (regex.test(key)) {
|
|
149
|
+
this.cache.delete(key);
|
|
150
|
+
invalidated++;
|
|
151
|
+
this.emit("evict", { key, reason: "manual" });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
this.stats.size = this.cache.size;
|
|
155
|
+
return invalidated;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Clear all entries from the cache
|
|
159
|
+
*/
|
|
160
|
+
clear() {
|
|
161
|
+
const entriesRemoved = this.cache.size;
|
|
162
|
+
this.cache.clear();
|
|
163
|
+
this.stats.size = 0;
|
|
164
|
+
this.emit("clear", { entriesRemoved });
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get or set a value (cache-aside pattern)
|
|
168
|
+
*/
|
|
169
|
+
async getOrSet(key, factory, ttl) {
|
|
170
|
+
const existing = this.get(key);
|
|
171
|
+
if (existing !== undefined) {
|
|
172
|
+
return existing;
|
|
173
|
+
}
|
|
174
|
+
const factoryTimeoutMs = 30_000;
|
|
175
|
+
const value = await withTimeout(Promise.resolve(factory()), factoryTimeoutMs, `ToolCache getOrSet factory timed out after ${factoryTimeoutMs}ms for key "${key}"`);
|
|
176
|
+
if (value === undefined) {
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
this.set(key, value, ttl);
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get cache statistics
|
|
184
|
+
*/
|
|
185
|
+
getStats() {
|
|
186
|
+
return { ...this.stats };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Reset statistics
|
|
190
|
+
*/
|
|
191
|
+
resetStats() {
|
|
192
|
+
this.stats.hits = 0;
|
|
193
|
+
this.stats.misses = 0;
|
|
194
|
+
this.stats.evictions = 0;
|
|
195
|
+
this.updateHitRate();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get all keys in the cache
|
|
199
|
+
*/
|
|
200
|
+
keys() {
|
|
201
|
+
return Array.from(this.cache.keys());
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get the number of entries in the cache
|
|
205
|
+
*/
|
|
206
|
+
get size() {
|
|
207
|
+
return this.cache.size;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Generate a cache key from tool name and arguments
|
|
211
|
+
*/
|
|
212
|
+
static generateKey(toolName, args) {
|
|
213
|
+
const stableStringify = (value, seen = new WeakSet()) => {
|
|
214
|
+
if (value === null || typeof value !== "object") {
|
|
215
|
+
return JSON.stringify(value);
|
|
216
|
+
}
|
|
217
|
+
if (value instanceof Date) {
|
|
218
|
+
return `{"$date":${JSON.stringify(value.toISOString())}}`;
|
|
219
|
+
}
|
|
220
|
+
if (seen.has(value)) {
|
|
221
|
+
throw new TypeError("Circular structures are not supported in cache keys");
|
|
222
|
+
}
|
|
223
|
+
seen.add(value);
|
|
224
|
+
if (Array.isArray(value)) {
|
|
225
|
+
const result = "[" + value.map((v) => stableStringify(v, seen)).join(",") + "]";
|
|
226
|
+
seen.delete(value);
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
const sortedKeys = Object.keys(value).sort();
|
|
230
|
+
const entries = sortedKeys.map((k) => JSON.stringify(k) +
|
|
231
|
+
":" +
|
|
232
|
+
stableStringify(value[k], seen));
|
|
233
|
+
seen.delete(value);
|
|
234
|
+
return "{" + entries.join(",") + "}";
|
|
235
|
+
};
|
|
236
|
+
const argsHash = createHash("sha256")
|
|
237
|
+
.update(stableStringify(args))
|
|
238
|
+
.digest("hex")
|
|
239
|
+
.substring(0, 16);
|
|
240
|
+
return `${toolName}:${argsHash}`;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Stop the auto-cleanup timer
|
|
244
|
+
*/
|
|
245
|
+
destroy() {
|
|
246
|
+
if (this.cleanupTimer) {
|
|
247
|
+
clearInterval(this.cleanupTimer);
|
|
248
|
+
this.cleanupTimer = undefined;
|
|
249
|
+
}
|
|
250
|
+
this.clear();
|
|
251
|
+
}
|
|
252
|
+
// ==================== Private Methods ====================
|
|
253
|
+
getFullKey(key) {
|
|
254
|
+
return this.config.namespace ? `${this.config.namespace}:${key}` : key;
|
|
255
|
+
}
|
|
256
|
+
isExpired(entry) {
|
|
257
|
+
return Date.now() > entry.expires;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Delete a cache entry by its full key with a specific eviction reason.
|
|
261
|
+
*/
|
|
262
|
+
deleteWithReason(fullKey, reason) {
|
|
263
|
+
const deleted = this.cache.delete(fullKey);
|
|
264
|
+
if (deleted) {
|
|
265
|
+
this.stats.evictions++;
|
|
266
|
+
this.stats.size = this.cache.size;
|
|
267
|
+
this.emit("evict", { key: fullKey, reason });
|
|
268
|
+
}
|
|
269
|
+
return deleted;
|
|
270
|
+
}
|
|
271
|
+
evictOne() {
|
|
272
|
+
const entryToEvict = this.selectEvictionCandidate();
|
|
273
|
+
if (entryToEvict) {
|
|
274
|
+
this.cache.delete(entryToEvict.key);
|
|
275
|
+
this.stats.evictions++;
|
|
276
|
+
this.stats.size = this.cache.size;
|
|
277
|
+
this.emit("evict", { key: entryToEvict.key, reason: "capacity" });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
selectEvictionCandidate() {
|
|
281
|
+
if (this.cache.size === 0) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
switch (this.config.strategy) {
|
|
285
|
+
case "lru":
|
|
286
|
+
return this.findLRU();
|
|
287
|
+
case "fifo":
|
|
288
|
+
return this.findFIFO();
|
|
289
|
+
case "lfu":
|
|
290
|
+
return this.findLFU();
|
|
291
|
+
default:
|
|
292
|
+
return this.findLRU();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
findLRU() {
|
|
296
|
+
let oldest;
|
|
297
|
+
let oldestTime = Infinity;
|
|
298
|
+
for (const entry of this.cache.values()) {
|
|
299
|
+
if (entry.accessedAt < oldestTime) {
|
|
300
|
+
oldestTime = entry.accessedAt;
|
|
301
|
+
oldest = entry;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return oldest;
|
|
305
|
+
}
|
|
306
|
+
findFIFO() {
|
|
307
|
+
let oldest;
|
|
308
|
+
let oldestTime = Infinity;
|
|
309
|
+
for (const entry of this.cache.values()) {
|
|
310
|
+
if (entry.createdAt < oldestTime) {
|
|
311
|
+
oldestTime = entry.createdAt;
|
|
312
|
+
oldest = entry;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return oldest;
|
|
316
|
+
}
|
|
317
|
+
findLFU() {
|
|
318
|
+
let leastFrequent;
|
|
319
|
+
let lowestCount = Infinity;
|
|
320
|
+
for (const entry of this.cache.values()) {
|
|
321
|
+
if (entry.accessCount < lowestCount) {
|
|
322
|
+
lowestCount = entry.accessCount;
|
|
323
|
+
leastFrequent = entry;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return leastFrequent;
|
|
327
|
+
}
|
|
328
|
+
patternToRegex(pattern) {
|
|
329
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
330
|
+
const regexPattern = escaped.replace(/\*/g, "[^:]*");
|
|
331
|
+
return new RegExp(`^${regexPattern}$`);
|
|
332
|
+
}
|
|
333
|
+
updateHitRate() {
|
|
334
|
+
const total = this.stats.hits + this.stats.misses;
|
|
335
|
+
this.stats.hitRate = total > 0 ? this.stats.hits / total : 0;
|
|
336
|
+
}
|
|
337
|
+
startAutoCleanup() {
|
|
338
|
+
this.cleanupTimer = setInterval(() => {
|
|
339
|
+
this.cleanupExpired();
|
|
340
|
+
}, this.config.cleanupInterval);
|
|
341
|
+
// Don't keep the process alive just for cleanup
|
|
342
|
+
if (this.cleanupTimer.unref) {
|
|
343
|
+
this.cleanupTimer.unref();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
cleanupExpired() {
|
|
347
|
+
const now = Date.now();
|
|
348
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
349
|
+
if (now > entry.expires) {
|
|
350
|
+
this.cache.delete(key);
|
|
351
|
+
this.stats.evictions++;
|
|
352
|
+
this.emit("evict", { key, reason: "expired" });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
this.stats.size = this.cache.size;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Factory function to create a ToolCache instance
|
|
360
|
+
*/
|
|
361
|
+
export const createToolCache = (config) => new ToolCache(config);
|
|
362
|
+
/**
|
|
363
|
+
* Default cache configuration
|
|
364
|
+
*/
|
|
365
|
+
export const DEFAULT_CACHE_CONFIG = {
|
|
366
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
367
|
+
maxSize: 500,
|
|
368
|
+
strategy: "lru",
|
|
369
|
+
enableAutoCleanup: true,
|
|
370
|
+
cleanupInterval: 60000, // 1 minute
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* Tool-specific cache wrapper with automatic key generation
|
|
374
|
+
*/
|
|
375
|
+
export class ToolResultCache {
|
|
376
|
+
cache;
|
|
377
|
+
constructor(config) {
|
|
378
|
+
this.cache = new ToolCache({
|
|
379
|
+
...DEFAULT_CACHE_CONFIG,
|
|
380
|
+
...config,
|
|
381
|
+
namespace: config?.namespace ?? "tool-results",
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Cache a tool result
|
|
386
|
+
*/
|
|
387
|
+
cacheResult(toolName, args, result, ttl) {
|
|
388
|
+
const key = ToolCache.generateKey(toolName, args);
|
|
389
|
+
this.cache.set(key, result, ttl);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get a cached tool result
|
|
393
|
+
*/
|
|
394
|
+
getCachedResult(toolName, args) {
|
|
395
|
+
const key = ToolCache.generateKey(toolName, args);
|
|
396
|
+
return this.cache.get(key);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Check if a result is cached
|
|
400
|
+
*/
|
|
401
|
+
hasCachedResult(toolName, args) {
|
|
402
|
+
const key = ToolCache.generateKey(toolName, args);
|
|
403
|
+
return this.cache.has(key);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Invalidate all cached results for a tool
|
|
407
|
+
*/
|
|
408
|
+
invalidateTool(toolName) {
|
|
409
|
+
return this.cache.invalidate(`${toolName}:*`);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Get cache statistics
|
|
413
|
+
*/
|
|
414
|
+
getStats() {
|
|
415
|
+
return this.cache.getStats();
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Clear all cached results
|
|
419
|
+
*/
|
|
420
|
+
clear() {
|
|
421
|
+
this.cache.clear();
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Destroy the cache
|
|
425
|
+
*/
|
|
426
|
+
destroy() {
|
|
427
|
+
this.cache.destroy();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Create a tool result cache instance
|
|
432
|
+
*/
|
|
433
|
+
export const createToolResultCache = (config) => new ToolResultCache(config);
|
|
434
|
+
//# sourceMappingURL=toolCache.js.map
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elicitation Manager
|
|
3
|
+
*
|
|
4
|
+
* Manager for handling elicitation requests during tool execution.
|
|
5
|
+
* Enables MCP tools to request interactive user input mid-execution.
|
|
6
|
+
*
|
|
7
|
+
* @module mcp/elicitation/elicitationManager
|
|
8
|
+
* @since 8.39.0
|
|
9
|
+
*/
|
|
10
|
+
import { EventEmitter } from "events";
|
|
11
|
+
import type { Elicitation, ElicitationResponse, ElicitationHandler, ElicitationManagerConfig, FormField } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Manager for handling elicitation requests during tool execution
|
|
14
|
+
*
|
|
15
|
+
* The elicitation protocol allows MCP tools to request interactive user input
|
|
16
|
+
* mid-execution. This is useful for:
|
|
17
|
+
* - Confirming destructive operations
|
|
18
|
+
* - Requesting missing information
|
|
19
|
+
* - Getting user preferences
|
|
20
|
+
* - Handling authentication challenges
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const elicitationManager = new ElicitationManager({
|
|
25
|
+
* defaultTimeout: 60000,
|
|
26
|
+
* handler: async (request) => {
|
|
27
|
+
* // Implement UI prompt based on request type
|
|
28
|
+
* if (request.type === "confirmation") {
|
|
29
|
+
* const confirmed = await showConfirmDialog(request.message);
|
|
30
|
+
* return {
|
|
31
|
+
* requestId: request.id,
|
|
32
|
+
* responded: true,
|
|
33
|
+
* value: confirmed,
|
|
34
|
+
* timestamp: Date.now(),
|
|
35
|
+
* };
|
|
36
|
+
* }
|
|
37
|
+
* // Handle other types...
|
|
38
|
+
* },
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Use in a tool
|
|
42
|
+
* const response = await elicitationManager.request({
|
|
43
|
+
* type: "confirmation",
|
|
44
|
+
* message: "Are you sure you want to delete this file?",
|
|
45
|
+
* toolName: "deleteFile",
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* if (response.value === true) {
|
|
49
|
+
* // Proceed with deletion
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class ElicitationManager extends EventEmitter {
|
|
54
|
+
private config;
|
|
55
|
+
private pendingRequests;
|
|
56
|
+
constructor(config?: ElicitationManagerConfig);
|
|
57
|
+
/**
|
|
58
|
+
* Set the elicitation handler
|
|
59
|
+
*/
|
|
60
|
+
setHandler(handler: ElicitationHandler): void;
|
|
61
|
+
/**
|
|
62
|
+
* Enable or disable elicitation
|
|
63
|
+
*/
|
|
64
|
+
setEnabled(enabled: boolean): void;
|
|
65
|
+
/**
|
|
66
|
+
* Check if elicitation is enabled
|
|
67
|
+
*/
|
|
68
|
+
isEnabled(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Request user input
|
|
71
|
+
*/
|
|
72
|
+
request(elicitation: Omit<Elicitation, "id"> & {
|
|
73
|
+
id?: string;
|
|
74
|
+
}): Promise<ElicitationResponse>;
|
|
75
|
+
/**
|
|
76
|
+
* Convenience method for confirmation requests
|
|
77
|
+
*/
|
|
78
|
+
confirm(message: string, options?: {
|
|
79
|
+
toolName?: string;
|
|
80
|
+
serverId?: string;
|
|
81
|
+
confirmLabel?: string;
|
|
82
|
+
cancelLabel?: string;
|
|
83
|
+
timeout?: number;
|
|
84
|
+
}): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Convenience method for text input
|
|
87
|
+
*/
|
|
88
|
+
getText(message: string, options?: {
|
|
89
|
+
toolName?: string;
|
|
90
|
+
placeholder?: string;
|
|
91
|
+
defaultValue?: string;
|
|
92
|
+
timeout?: number;
|
|
93
|
+
}): Promise<string | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* Convenience method for selection
|
|
96
|
+
*/
|
|
97
|
+
select<T extends string>(message: string, options: Array<{
|
|
98
|
+
value: T;
|
|
99
|
+
label: string;
|
|
100
|
+
}>, config?: {
|
|
101
|
+
toolName?: string;
|
|
102
|
+
timeout?: number;
|
|
103
|
+
}): Promise<T | undefined>;
|
|
104
|
+
/**
|
|
105
|
+
* Convenience method for multiple selection
|
|
106
|
+
*/
|
|
107
|
+
multiSelect<T extends string>(message: string, options: Array<{
|
|
108
|
+
value: T;
|
|
109
|
+
label: string;
|
|
110
|
+
}>, config?: {
|
|
111
|
+
toolName?: string;
|
|
112
|
+
timeout?: number;
|
|
113
|
+
minSelections?: number;
|
|
114
|
+
maxSelections?: number;
|
|
115
|
+
}): Promise<T[] | undefined>;
|
|
116
|
+
/**
|
|
117
|
+
* Convenience method for form input
|
|
118
|
+
*/
|
|
119
|
+
form<T extends Record<string, unknown>>(message: string, fields: FormField[], config?: {
|
|
120
|
+
toolName?: string;
|
|
121
|
+
serverId?: string;
|
|
122
|
+
timeout?: number;
|
|
123
|
+
submitLabel?: string;
|
|
124
|
+
}): Promise<T | undefined>;
|
|
125
|
+
/**
|
|
126
|
+
* Convenience method for secret input
|
|
127
|
+
*/
|
|
128
|
+
getSecret(message: string, options?: {
|
|
129
|
+
toolName?: string;
|
|
130
|
+
hint?: string;
|
|
131
|
+
timeout?: number;
|
|
132
|
+
}): Promise<string | undefined>;
|
|
133
|
+
/**
|
|
134
|
+
* Cancel a pending request
|
|
135
|
+
*/
|
|
136
|
+
cancel(requestId: string, reason?: string): void;
|
|
137
|
+
/**
|
|
138
|
+
* Default handler when none is provided
|
|
139
|
+
*/
|
|
140
|
+
private defaultHandler;
|
|
141
|
+
/**
|
|
142
|
+
* Handle timeout
|
|
143
|
+
*/
|
|
144
|
+
private handleTimeout;
|
|
145
|
+
/**
|
|
146
|
+
* Handle disabled elicitation
|
|
147
|
+
*/
|
|
148
|
+
private handleDisabled;
|
|
149
|
+
/**
|
|
150
|
+
* Handle disabled request based on fallback behavior
|
|
151
|
+
*/
|
|
152
|
+
private handleDisabledRequest;
|
|
153
|
+
/**
|
|
154
|
+
* Get pending request count
|
|
155
|
+
*/
|
|
156
|
+
getPendingCount(): number;
|
|
157
|
+
/**
|
|
158
|
+
* Get all pending requests
|
|
159
|
+
*/
|
|
160
|
+
getPendingRequests(): Elicitation[];
|
|
161
|
+
/**
|
|
162
|
+
* Clear all pending requests
|
|
163
|
+
*/
|
|
164
|
+
clearPending(reason?: string): void;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Global elicitation manager instance
|
|
168
|
+
*/
|
|
169
|
+
export declare const globalElicitationManager: ElicitationManager;
|