@hashgraphonline/conversational-agent 0.1.207 → 0.1.209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/conversational-agent.d.ts +67 -8
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/langchain/ContentAwareAgentExecutor.d.ts +4 -6
- package/dist/cjs/langchain-agent.d.ts +8 -0
- package/dist/cjs/memory/SmartMemoryManager.d.ts +58 -21
- package/dist/cjs/memory/index.d.ts +1 -1
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index12.js +124 -46
- package/dist/esm/index12.js.map +1 -1
- package/dist/esm/index13.js +178 -13
- package/dist/esm/index13.js.map +1 -1
- package/dist/esm/index14.js +604 -100
- package/dist/esm/index14.js.map +1 -1
- package/dist/esm/index15.js +453 -59
- package/dist/esm/index15.js.map +1 -1
- package/dist/esm/index16.js +44 -172
- package/dist/esm/index16.js.map +1 -1
- package/dist/esm/index17.js +11 -156
- package/dist/esm/index17.js.map +1 -1
- package/dist/esm/index18.js +106 -191
- package/dist/esm/index18.js.map +1 -1
- package/dist/esm/index19.js +7 -90
- package/dist/esm/index19.js.map +1 -1
- package/dist/esm/index2.js +22 -13
- package/dist/esm/index2.js.map +1 -1
- package/dist/esm/index20.js +130 -616
- package/dist/esm/index20.js.map +1 -1
- package/dist/esm/index21.js +138 -215
- package/dist/esm/index21.js.map +1 -1
- package/dist/esm/index22.js +45 -159
- package/dist/esm/index22.js.map +1 -1
- package/dist/esm/index23.js +25 -121
- package/dist/esm/index23.js.map +1 -1
- package/dist/esm/index24.js +83 -56
- package/dist/esm/index24.js.map +1 -1
- package/dist/esm/index25.js +236 -32
- package/dist/esm/index25.js.map +1 -1
- package/dist/esm/index5.js +1 -1
- package/dist/esm/index6.js +295 -17
- package/dist/esm/index6.js.map +1 -1
- package/dist/esm/index8.js +82 -8
- package/dist/esm/index8.js.map +1 -1
- package/dist/types/conversational-agent.d.ts +67 -8
- package/dist/types/index.d.ts +1 -0
- package/dist/types/langchain/ContentAwareAgentExecutor.d.ts +4 -6
- package/dist/types/langchain-agent.d.ts +8 -0
- package/dist/types/memory/SmartMemoryManager.d.ts +58 -21
- package/dist/types/memory/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/context/ReferenceContextManager.ts +9 -4
- package/src/context/ReferenceResponseProcessor.ts +3 -4
- package/src/conversational-agent.ts +379 -31
- package/src/index.ts +2 -0
- package/src/langchain/ContentAwareAgentExecutor.ts +4 -97
- package/src/langchain-agent.ts +94 -11
- package/src/mcp/ContentProcessor.ts +13 -3
- package/src/mcp/adapters/langchain.ts +1 -9
- package/src/memory/ContentStorage.ts +3 -51
- package/src/memory/MemoryWindow.ts +4 -16
- package/src/memory/ReferenceIdGenerator.ts +0 -4
- package/src/memory/SmartMemoryManager.ts +400 -33
- package/src/memory/TokenCounter.ts +12 -16
- package/src/memory/index.ts +1 -1
- package/src/plugins/hcs-10/HCS10Plugin.ts +44 -14
- package/src/services/ContentStoreManager.ts +0 -3
- package/src/types/content-reference.ts +8 -8
- package/src/types/index.ts +0 -1
package/dist/esm/index20.js
CHANGED
|
@@ -1,663 +1,177 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
activeReferences: 0,
|
|
12
|
-
totalStorageBytes: 0,
|
|
13
|
-
recentlyCleanedUp: 0,
|
|
14
|
-
totalResolutions: 0,
|
|
15
|
-
failedResolutions: 0,
|
|
16
|
-
averageContentSize: 0,
|
|
17
|
-
storageUtilization: 0,
|
|
18
|
-
performanceMetrics: {
|
|
19
|
-
averageCreationTimeMs: 0,
|
|
20
|
-
averageResolutionTimeMs: 0,
|
|
21
|
-
averageCleanupTimeMs: 0,
|
|
22
|
-
creationTimes: [],
|
|
23
|
-
resolutionTimes: [],
|
|
24
|
-
cleanupTimes: []
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
if (this.referenceConfig.enableAutoCleanup) {
|
|
28
|
-
this.startReferenceCleanupTimer();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Store messages in the content storage
|
|
33
|
-
* Automatically drops oldest messages if storage limit is exceeded
|
|
34
|
-
* @param messages - Messages to store
|
|
35
|
-
* @returns Result indicating how many messages were stored and dropped
|
|
36
|
-
*/
|
|
37
|
-
storeMessages(messages) {
|
|
38
|
-
if (messages.length === 0) {
|
|
39
|
-
return { stored: 0, dropped: 0 };
|
|
40
|
-
}
|
|
41
|
-
const now = /* @__PURE__ */ new Date();
|
|
42
|
-
let dropped = 0;
|
|
43
|
-
const storedMessages = messages.map((message) => ({
|
|
44
|
-
message,
|
|
45
|
-
storedAt: now,
|
|
46
|
-
id: this.generateId()
|
|
47
|
-
}));
|
|
48
|
-
this.messages.push(...storedMessages);
|
|
49
|
-
while (this.messages.length > this.maxStorage) {
|
|
50
|
-
this.messages.shift();
|
|
51
|
-
dropped++;
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
stored: storedMessages.length,
|
|
55
|
-
dropped
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get the most recent messages from storage
|
|
60
|
-
* @param count - Number of recent messages to retrieve
|
|
61
|
-
* @returns Array of recent messages in chronological order
|
|
62
|
-
*/
|
|
63
|
-
getRecentMessages(count) {
|
|
64
|
-
if (count <= 0 || this.messages.length === 0) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
const startIndex = Math.max(0, this.messages.length - count);
|
|
68
|
-
return this.messages.slice(startIndex).map((stored) => stored.message);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Search for messages containing specific text or patterns
|
|
72
|
-
* @param query - Search term or regex pattern
|
|
73
|
-
* @param options - Search configuration options
|
|
74
|
-
* @returns Array of matching messages
|
|
75
|
-
*/
|
|
76
|
-
searchMessages(query, options = {}) {
|
|
77
|
-
if (!query || this.messages.length === 0) {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
const {
|
|
81
|
-
caseSensitive = false,
|
|
82
|
-
limit,
|
|
83
|
-
useRegex = false
|
|
84
|
-
} = options;
|
|
85
|
-
let matches = [];
|
|
86
|
-
if (useRegex) {
|
|
87
|
-
try {
|
|
88
|
-
const regex = new RegExp(query, caseSensitive ? "g" : "gi");
|
|
89
|
-
matches = this.messages.filter((stored) => regex.test(stored.message.content)).map((stored) => stored.message);
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.warn("Invalid regex pattern:", query, error);
|
|
92
|
-
return [];
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
const searchTerm = caseSensitive ? query : query.toLowerCase();
|
|
96
|
-
matches = this.messages.filter((stored) => {
|
|
97
|
-
const content = stored.message.content;
|
|
98
|
-
const searchContent = caseSensitive ? content : content.toLowerCase();
|
|
99
|
-
return searchContent.includes(searchTerm);
|
|
100
|
-
}).map((stored) => stored.message);
|
|
101
|
-
}
|
|
102
|
-
return limit ? matches.slice(0, limit) : matches;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Get messages from a specific time range
|
|
106
|
-
* @param startTime - Start of time range (inclusive)
|
|
107
|
-
* @param endTime - End of time range (inclusive)
|
|
108
|
-
* @returns Array of messages within the time range
|
|
109
|
-
*/
|
|
110
|
-
getMessagesFromTimeRange(startTime, endTime) {
|
|
111
|
-
if (startTime > endTime || this.messages.length === 0) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
return this.messages.filter(
|
|
115
|
-
(stored) => stored.storedAt >= startTime && stored.storedAt <= endTime
|
|
116
|
-
).map((stored) => stored.message);
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Get storage statistics and usage information
|
|
120
|
-
* @returns Current storage statistics
|
|
121
|
-
*/
|
|
122
|
-
getStorageStats() {
|
|
123
|
-
const totalMessages = this.messages.length;
|
|
124
|
-
const usagePercentage = totalMessages > 0 ? Math.round(totalMessages / this.maxStorage * 100) : 0;
|
|
125
|
-
let oldestMessageTime;
|
|
126
|
-
let newestMessageTime;
|
|
127
|
-
if (totalMessages > 0) {
|
|
128
|
-
oldestMessageTime = this.messages[0].storedAt;
|
|
129
|
-
newestMessageTime = this.messages[totalMessages - 1].storedAt;
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
totalMessages,
|
|
133
|
-
maxStorageLimit: this.maxStorage,
|
|
134
|
-
usagePercentage,
|
|
135
|
-
oldestMessageTime,
|
|
136
|
-
newestMessageTime
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Clear all stored messages
|
|
141
|
-
*/
|
|
142
|
-
clear() {
|
|
143
|
-
this.messages = [];
|
|
144
|
-
this.idCounter = 0;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Get total number of stored messages
|
|
148
|
-
* @returns Number of messages currently in storage
|
|
149
|
-
*/
|
|
150
|
-
getTotalStoredMessages() {
|
|
151
|
-
return this.messages.length;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Update the maximum storage limit
|
|
155
|
-
* @param newLimit - New maximum storage limit
|
|
156
|
-
*/
|
|
157
|
-
updateStorageLimit(newLimit) {
|
|
158
|
-
if (newLimit <= 0) {
|
|
159
|
-
throw new Error("Storage limit must be greater than 0");
|
|
160
|
-
}
|
|
161
|
-
this.maxStorage = newLimit;
|
|
162
|
-
while (this.messages.length > this.maxStorage) {
|
|
163
|
-
this.messages.shift();
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Get messages by message type
|
|
168
|
-
* @param messageType - Type of messages to retrieve ('human', 'ai', 'system', etc.)
|
|
169
|
-
* @param limit - Maximum number of messages to return
|
|
170
|
-
* @returns Array of messages of the specified type
|
|
171
|
-
*/
|
|
172
|
-
getMessagesByType(messageType, limit) {
|
|
173
|
-
const filtered = this.messages.filter((stored) => stored.message._getType() === messageType).map((stored) => stored.message);
|
|
174
|
-
return limit ? filtered.slice(0, limit) : filtered;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Get the current storage configuration
|
|
178
|
-
* @returns Storage configuration object
|
|
179
|
-
*/
|
|
180
|
-
getConfig() {
|
|
181
|
-
return {
|
|
182
|
-
maxStorage: this.maxStorage,
|
|
183
|
-
currentUsage: this.messages.length,
|
|
184
|
-
utilizationPercentage: this.messages.length / this.maxStorage * 100
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Generate a unique ID for stored messages
|
|
189
|
-
* @returns Unique string identifier
|
|
190
|
-
*/
|
|
191
|
-
generateId() {
|
|
192
|
-
return `msg_${++this.idCounter}_${Date.now()}`;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Get messages stored within the last N minutes
|
|
196
|
-
* @param minutes - Number of minutes to look back
|
|
197
|
-
* @returns Array of messages from the last N minutes
|
|
198
|
-
*/
|
|
199
|
-
getRecentMessagesByTime(minutes) {
|
|
200
|
-
if (minutes <= 0 || this.messages.length === 0) {
|
|
201
|
-
return [];
|
|
202
|
-
}
|
|
203
|
-
const cutoffTime = new Date(Date.now() - minutes * 60 * 1e3);
|
|
204
|
-
return this.messages.filter((stored) => stored.storedAt >= cutoffTime).map((stored) => stored.message);
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Export messages to a JSON-serializable format
|
|
208
|
-
* @returns Serializable representation of stored messages
|
|
209
|
-
*/
|
|
210
|
-
exportMessages() {
|
|
211
|
-
return this.messages.map((stored) => ({
|
|
212
|
-
content: stored.message.content,
|
|
213
|
-
type: stored.message._getType(),
|
|
214
|
-
storedAt: stored.storedAt.toISOString(),
|
|
215
|
-
id: stored.id
|
|
216
|
-
}));
|
|
217
|
-
}
|
|
218
|
-
// ========== Reference-Based Content Storage Methods ==========
|
|
219
|
-
/**
|
|
220
|
-
* Determine if content should be stored as a reference based on size
|
|
221
|
-
*/
|
|
222
|
-
shouldUseReference(content) {
|
|
223
|
-
const size = Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content, "utf8");
|
|
224
|
-
return size > this.referenceConfig.sizeThresholdBytes;
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Store content and return a reference if it exceeds the size threshold
|
|
228
|
-
* Otherwise returns null to indicate direct content should be used
|
|
229
|
-
*/
|
|
230
|
-
async storeContentIfLarge(content, metadata) {
|
|
231
|
-
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content, "utf8");
|
|
232
|
-
if (!this.shouldUseReference(buffer)) {
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
const storeMetadata = {
|
|
236
|
-
contentType: metadata.contentType || this.detectContentType(buffer, metadata.mimeType),
|
|
237
|
-
sizeBytes: buffer.length,
|
|
238
|
-
source: metadata.source,
|
|
239
|
-
tags: []
|
|
240
|
-
};
|
|
241
|
-
if (metadata.mimeType !== void 0) {
|
|
242
|
-
storeMetadata.mimeType = metadata.mimeType;
|
|
243
|
-
}
|
|
244
|
-
if (metadata.mcpToolName !== void 0) {
|
|
245
|
-
storeMetadata.mcpToolName = metadata.mcpToolName;
|
|
246
|
-
}
|
|
247
|
-
if (metadata.fileName !== void 0) {
|
|
248
|
-
storeMetadata.fileName = metadata.fileName;
|
|
249
|
-
}
|
|
250
|
-
if (metadata.tags !== void 0) {
|
|
251
|
-
storeMetadata.tags = metadata.tags;
|
|
252
|
-
}
|
|
253
|
-
if (metadata.customMetadata !== void 0) {
|
|
254
|
-
storeMetadata.customMetadata = metadata.customMetadata;
|
|
255
|
-
}
|
|
256
|
-
return await this.storeContent(buffer, storeMetadata);
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Store content and return a reference (implements ContentReferenceStore)
|
|
260
|
-
*/
|
|
261
|
-
async storeContent(content, metadata) {
|
|
262
|
-
const startTime = Date.now();
|
|
263
|
-
try {
|
|
264
|
-
const now = /* @__PURE__ */ new Date();
|
|
265
|
-
const referenceId = ReferenceIdGenerator.generateId(content);
|
|
266
|
-
const fullMetadata = {
|
|
267
|
-
...metadata,
|
|
268
|
-
createdAt: now,
|
|
269
|
-
lastAccessedAt: now,
|
|
270
|
-
accessCount: 0
|
|
271
|
-
};
|
|
272
|
-
const storedContent = {
|
|
273
|
-
content,
|
|
274
|
-
metadata: fullMetadata,
|
|
275
|
-
state: "active"
|
|
276
|
-
};
|
|
277
|
-
const expirationTime = this.calculateExpirationTime(metadata.source);
|
|
278
|
-
if (expirationTime !== void 0) {
|
|
279
|
-
storedContent.expiresAt = expirationTime;
|
|
280
|
-
}
|
|
281
|
-
this.contentStore.set(referenceId, storedContent);
|
|
282
|
-
this.updateStatsAfterStore(content.length);
|
|
283
|
-
await this.enforceReferenceStorageLimits();
|
|
284
|
-
const preview = this.createContentPreview(content, fullMetadata.contentType);
|
|
285
|
-
const referenceMetadata = {
|
|
286
|
-
contentType: fullMetadata.contentType,
|
|
287
|
-
sizeBytes: fullMetadata.sizeBytes,
|
|
288
|
-
source: fullMetadata.source
|
|
289
|
-
};
|
|
290
|
-
if (fullMetadata.fileName !== void 0) {
|
|
291
|
-
referenceMetadata.fileName = fullMetadata.fileName;
|
|
292
|
-
}
|
|
293
|
-
if (fullMetadata.mimeType !== void 0) {
|
|
294
|
-
referenceMetadata.mimeType = fullMetadata.mimeType;
|
|
295
|
-
}
|
|
296
|
-
const reference = {
|
|
297
|
-
referenceId,
|
|
298
|
-
state: "active",
|
|
299
|
-
preview,
|
|
300
|
-
metadata: referenceMetadata,
|
|
301
|
-
createdAt: now,
|
|
302
|
-
format: "ref://{id}"
|
|
303
|
-
};
|
|
304
|
-
const duration = Date.now() - startTime;
|
|
305
|
-
this.recordPerformanceMetric("creation", duration);
|
|
306
|
-
console.log(`[ContentStorage] Stored content with reference ID: ${referenceId} (${content.length} bytes)`);
|
|
307
|
-
return reference;
|
|
308
|
-
} catch (error) {
|
|
309
|
-
const duration = Date.now() - startTime;
|
|
310
|
-
this.recordPerformanceMetric("creation", duration);
|
|
311
|
-
console.error("[ContentStorage] Failed to store content:", error);
|
|
312
|
-
throw new ContentReferenceError(
|
|
313
|
-
`Failed to store content: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
314
|
-
"system_error",
|
|
315
|
-
void 0,
|
|
316
|
-
["Try again", "Check storage limits", "Contact administrator"]
|
|
317
|
-
);
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
import { MCPContentProcessor } from "./index25.js";
|
|
4
|
+
class MCPClientManager {
|
|
5
|
+
constructor(logger, contentStorage) {
|
|
6
|
+
this.clients = /* @__PURE__ */ new Map();
|
|
7
|
+
this.tools = /* @__PURE__ */ new Map();
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
if (contentStorage) {
|
|
10
|
+
this.contentProcessor = new MCPContentProcessor(contentStorage, logger);
|
|
318
11
|
}
|
|
319
12
|
}
|
|
320
13
|
/**
|
|
321
|
-
*
|
|
14
|
+
* Connect to an MCP server and discover its tools
|
|
322
15
|
*/
|
|
323
|
-
async
|
|
324
|
-
const startTime = Date.now();
|
|
16
|
+
async connectServer(config) {
|
|
325
17
|
try {
|
|
326
|
-
if (
|
|
327
|
-
this.referenceStats.failedResolutions++;
|
|
328
|
-
return {
|
|
329
|
-
success: false,
|
|
330
|
-
error: "Invalid reference ID format",
|
|
331
|
-
errorType: "not_found",
|
|
332
|
-
suggestedActions: ["Check the reference ID format", "Ensure the reference ID is complete"]
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
const storedContent = this.contentStore.get(referenceId);
|
|
336
|
-
if (!storedContent) {
|
|
337
|
-
this.referenceStats.failedResolutions++;
|
|
18
|
+
if (this.isServerConnected(config.name)) {
|
|
338
19
|
return {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
20
|
+
serverName: config.name,
|
|
21
|
+
connected: false,
|
|
22
|
+
error: `Server ${config.name} is already connected`,
|
|
23
|
+
tools: []
|
|
343
24
|
};
|
|
344
25
|
}
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
this.referenceStats.failedResolutions++;
|
|
348
|
-
return {
|
|
349
|
-
success: false,
|
|
350
|
-
error: "Reference has expired",
|
|
351
|
-
errorType: "expired",
|
|
352
|
-
suggestedActions: ["Request fresh content", "Use alternative content source"]
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
if (storedContent.state !== "active") {
|
|
356
|
-
this.referenceStats.failedResolutions++;
|
|
357
|
-
return {
|
|
358
|
-
success: false,
|
|
359
|
-
error: `Reference is ${storedContent.state}`,
|
|
360
|
-
errorType: storedContent.state === "expired" ? "expired" : "corrupted",
|
|
361
|
-
suggestedActions: ["Request fresh content", "Check reference validity"]
|
|
362
|
-
};
|
|
26
|
+
if (config.transport && config.transport !== "stdio") {
|
|
27
|
+
throw new Error(`Transport ${config.transport} not yet supported`);
|
|
363
28
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
29
|
+
const transport = new StdioClientTransport({
|
|
30
|
+
command: config.command,
|
|
31
|
+
args: config.args,
|
|
32
|
+
...config.env && { env: config.env }
|
|
33
|
+
});
|
|
34
|
+
const client = new Client({
|
|
35
|
+
name: `conversational-agent-${config.name}`,
|
|
36
|
+
version: "1.0.0"
|
|
37
|
+
}, {
|
|
38
|
+
capabilities: {}
|
|
39
|
+
});
|
|
40
|
+
await client.connect(transport);
|
|
41
|
+
this.clients.set(config.name, client);
|
|
42
|
+
const toolsResponse = await client.listTools();
|
|
43
|
+
const toolsWithServer = toolsResponse.tools.map((tool) => ({
|
|
44
|
+
...tool,
|
|
45
|
+
serverName: config.name
|
|
46
|
+
}));
|
|
47
|
+
this.tools.set(config.name, toolsWithServer);
|
|
48
|
+
this.logger.info(`Connected to MCP server ${config.name} with ${toolsWithServer.length} tools`);
|
|
370
49
|
return {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
50
|
+
serverName: config.name,
|
|
51
|
+
connected: true,
|
|
52
|
+
tools: toolsWithServer
|
|
374
53
|
};
|
|
375
54
|
} catch (error) {
|
|
376
|
-
|
|
377
|
-
this.recordPerformanceMetric("resolution", duration);
|
|
378
|
-
this.referenceStats.failedResolutions++;
|
|
379
|
-
console.error(`[ContentStorage] Error resolving reference ${referenceId}:`, error);
|
|
55
|
+
this.logger.error(`Failed to connect to MCP server ${config.name}:`, error);
|
|
380
56
|
return {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
57
|
+
serverName: config.name,
|
|
58
|
+
connected: false,
|
|
59
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
60
|
+
tools: []
|
|
385
61
|
};
|
|
386
62
|
}
|
|
387
63
|
}
|
|
388
64
|
/**
|
|
389
|
-
*
|
|
390
|
-
*/
|
|
391
|
-
async hasReference(referenceId) {
|
|
392
|
-
if (!ReferenceIdGenerator.isValidReferenceId(referenceId)) {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
const storedContent = this.contentStore.get(referenceId);
|
|
396
|
-
if (!storedContent) {
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
if (storedContent.expiresAt && storedContent.expiresAt < /* @__PURE__ */ new Date()) {
|
|
400
|
-
storedContent.state = "expired";
|
|
401
|
-
return false;
|
|
402
|
-
}
|
|
403
|
-
return storedContent.state === "active";
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Mark a reference for cleanup
|
|
407
|
-
*/
|
|
408
|
-
async cleanupReference(referenceId) {
|
|
409
|
-
const storedContent = this.contentStore.get(referenceId);
|
|
410
|
-
if (!storedContent) {
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
this.referenceStats.totalStorageBytes -= storedContent.content.length;
|
|
414
|
-
this.referenceStats.activeReferences--;
|
|
415
|
-
this.referenceStats.recentlyCleanedUp++;
|
|
416
|
-
this.contentStore.delete(referenceId);
|
|
417
|
-
console.log(`[ContentStorage] Cleaned up reference ${referenceId} (${storedContent.content.length} bytes)`);
|
|
418
|
-
return true;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Get current reference storage statistics (implements ContentReferenceStore)
|
|
422
|
-
*/
|
|
423
|
-
async getStats() {
|
|
424
|
-
this.updateReferenceStorageStats();
|
|
425
|
-
return {
|
|
426
|
-
...this.referenceStats,
|
|
427
|
-
performanceMetrics: {
|
|
428
|
-
averageCreationTimeMs: this.calculateAverage(this.referenceStats.performanceMetrics.creationTimes),
|
|
429
|
-
averageResolutionTimeMs: this.calculateAverage(this.referenceStats.performanceMetrics.resolutionTimes),
|
|
430
|
-
averageCleanupTimeMs: this.calculateAverage(this.referenceStats.performanceMetrics.cleanupTimes)
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Update reference configuration
|
|
65
|
+
* Execute a tool on a specific MCP server
|
|
436
66
|
*/
|
|
437
|
-
async
|
|
438
|
-
|
|
439
|
-
if (
|
|
440
|
-
|
|
441
|
-
delete this.cleanupTimer;
|
|
442
|
-
}
|
|
443
|
-
if (this.referenceConfig.enableAutoCleanup) {
|
|
444
|
-
this.startReferenceCleanupTimer();
|
|
67
|
+
async executeTool(serverName, toolName, args) {
|
|
68
|
+
const client = this.clients.get(serverName);
|
|
69
|
+
if (!client) {
|
|
70
|
+
throw new Error(`MCP server ${serverName} not connected`);
|
|
445
71
|
}
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Perform cleanup based on current policies (implements ContentReferenceStore)
|
|
450
|
-
*/
|
|
451
|
-
async performCleanup() {
|
|
452
|
-
const startTime = Date.now();
|
|
453
|
-
const errors = [];
|
|
454
|
-
let cleanedUp = 0;
|
|
72
|
+
this.logger.debug(`Executing MCP tool ${toolName} on server ${serverName}`, args);
|
|
455
73
|
try {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
for (const [referenceId, storedContent] of this.contentStore.entries()) {
|
|
460
|
-
let shouldCleanup = false;
|
|
461
|
-
if (storedContent.expiresAt && storedContent.expiresAt < now) {
|
|
462
|
-
shouldCleanup = true;
|
|
463
|
-
storedContent.state = "expired";
|
|
464
|
-
}
|
|
465
|
-
const ageMs = now.getTime() - storedContent.metadata.createdAt.getTime();
|
|
466
|
-
const policy = this.getCleanupPolicy(storedContent.metadata.source);
|
|
467
|
-
if (ageMs > policy.maxAgeMs) {
|
|
468
|
-
shouldCleanup = true;
|
|
469
|
-
}
|
|
470
|
-
if (storedContent.state === "cleanup_pending") {
|
|
471
|
-
shouldCleanup = true;
|
|
472
|
-
}
|
|
473
|
-
if (shouldCleanup) {
|
|
474
|
-
toCleanup.push(referenceId);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
toCleanup.sort((a, b) => {
|
|
478
|
-
const aContent = this.contentStore.get(a);
|
|
479
|
-
const bContent = this.contentStore.get(b);
|
|
480
|
-
const aPriority = this.getCleanupPolicy(aContent.metadata.source).priority;
|
|
481
|
-
const bPriority = this.getCleanupPolicy(bContent.metadata.source).priority;
|
|
482
|
-
return bPriority - aPriority;
|
|
74
|
+
const result = await client.callTool({
|
|
75
|
+
name: toolName,
|
|
76
|
+
arguments: args
|
|
483
77
|
});
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
if (this.contentStore.size > this.referenceConfig.maxReferences) {
|
|
495
|
-
const sortedByAge = Array.from(this.contentStore.entries()).sort(([, a], [, b]) => a.metadata.lastAccessedAt.getTime() - b.metadata.lastAccessedAt.getTime());
|
|
496
|
-
const excessCount = this.contentStore.size - this.referenceConfig.maxReferences;
|
|
497
|
-
for (let i = 0; i < excessCount && i < sortedByAge.length; i++) {
|
|
498
|
-
const [referenceId] = sortedByAge[i];
|
|
499
|
-
try {
|
|
500
|
-
const success = await this.cleanupReference(referenceId);
|
|
501
|
-
if (success) {
|
|
502
|
-
cleanedUp++;
|
|
78
|
+
if (this.contentProcessor) {
|
|
79
|
+
const processed = await this.contentProcessor.processResponse(result, serverName, toolName);
|
|
80
|
+
if (processed.wasProcessed) {
|
|
81
|
+
this.logger.debug(
|
|
82
|
+
`Processed MCP response from ${serverName}::${toolName}`,
|
|
83
|
+
{
|
|
84
|
+
referenceCreated: processed.referenceCreated,
|
|
85
|
+
originalSize: processed.originalSize,
|
|
86
|
+
errors: processed.errors
|
|
503
87
|
}
|
|
504
|
-
|
|
505
|
-
|
|
88
|
+
);
|
|
89
|
+
if (processed.errors && processed.errors.length > 0) {
|
|
90
|
+
this.logger.warn(`Content processing warnings for ${serverName}::${toolName}:`, processed.errors);
|
|
506
91
|
}
|
|
507
92
|
}
|
|
93
|
+
return processed.content;
|
|
508
94
|
}
|
|
509
|
-
|
|
510
|
-
this.recordPerformanceMetric("cleanup", duration);
|
|
511
|
-
console.log(`[ContentStorage] Reference cleanup completed: ${cleanedUp} references cleaned up, ${errors.length} errors`);
|
|
512
|
-
return { cleanedUp, errors };
|
|
95
|
+
return result;
|
|
513
96
|
} catch (error) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const errorMessage = `Cleanup process failed: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
517
|
-
console.error("[ContentStorage]", errorMessage);
|
|
518
|
-
errors.push(errorMessage);
|
|
519
|
-
return { cleanedUp, errors };
|
|
97
|
+
this.logger.error(`Error executing MCP tool ${toolName}:`, error);
|
|
98
|
+
throw error;
|
|
520
99
|
}
|
|
521
100
|
}
|
|
522
101
|
/**
|
|
523
|
-
*
|
|
102
|
+
* Disconnect all MCP servers
|
|
524
103
|
*/
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
// ========== Private Reference Storage Helper Methods ==========
|
|
529
|
-
async enforceReferenceStorageLimits() {
|
|
530
|
-
if (this.contentStore.size >= this.referenceConfig.maxReferences) {
|
|
531
|
-
await this.performCleanup();
|
|
532
|
-
}
|
|
533
|
-
if (this.referenceStats.totalStorageBytes >= this.referenceConfig.maxTotalStorageBytes) {
|
|
534
|
-
await this.performCleanup();
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
calculateExpirationTime(source) {
|
|
538
|
-
const policy = this.getCleanupPolicy(source);
|
|
539
|
-
return new Date(Date.now() + policy.maxAgeMs);
|
|
540
|
-
}
|
|
541
|
-
getCleanupPolicy(source) {
|
|
542
|
-
switch (source) {
|
|
543
|
-
case "mcp_tool":
|
|
544
|
-
return this.referenceConfig.cleanupPolicies.recent;
|
|
545
|
-
case "user_upload":
|
|
546
|
-
return this.referenceConfig.cleanupPolicies.userContent;
|
|
547
|
-
case "agent_generated":
|
|
548
|
-
return this.referenceConfig.cleanupPolicies.agentGenerated;
|
|
549
|
-
default:
|
|
550
|
-
return this.referenceConfig.cleanupPolicies.default;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
detectContentType(content, mimeType) {
|
|
554
|
-
if (mimeType) {
|
|
555
|
-
if (mimeType === "text/html") return "html";
|
|
556
|
-
if (mimeType === "text/markdown") return "markdown";
|
|
557
|
-
if (mimeType === "application/json") return "json";
|
|
558
|
-
if (mimeType.startsWith("text/")) return "text";
|
|
559
|
-
return "binary";
|
|
560
|
-
}
|
|
561
|
-
const contentStr = content.toString("utf8", 0, Math.min(content.length, 1e3));
|
|
562
|
-
if (contentStr.startsWith("{") || contentStr.startsWith("[")) return "json";
|
|
563
|
-
if (contentStr.includes("<html>") || contentStr.includes("<!DOCTYPE")) return "html";
|
|
564
|
-
if (contentStr.includes("#") && contentStr.includes("\n")) return "markdown";
|
|
565
|
-
return "text";
|
|
566
|
-
}
|
|
567
|
-
createContentPreview(content, contentType) {
|
|
568
|
-
const maxLength = 200;
|
|
569
|
-
let preview = content.toString("utf8", 0, Math.min(content.length, maxLength * 2));
|
|
570
|
-
if (contentType === "html") {
|
|
571
|
-
preview = preview.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
|
|
572
|
-
} else if (contentType === "json") {
|
|
104
|
+
async disconnectAll() {
|
|
105
|
+
for (const [name, client] of this.clients) {
|
|
573
106
|
try {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
} catch {
|
|
107
|
+
await client.close();
|
|
108
|
+
this.logger.info(`Disconnected from MCP server ${name}`);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this.logger.error(`Error disconnecting MCP server ${name}:`, error);
|
|
577
111
|
}
|
|
578
112
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
113
|
+
this.clients.clear();
|
|
114
|
+
this.tools.clear();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get all discovered tools from all connected servers
|
|
118
|
+
*/
|
|
119
|
+
getAllTools() {
|
|
120
|
+
const allTools = [];
|
|
121
|
+
for (const tools of this.tools.values()) {
|
|
122
|
+
allTools.push(...tools);
|
|
582
123
|
}
|
|
583
|
-
return
|
|
124
|
+
return allTools;
|
|
584
125
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Get tools from a specific server
|
|
128
|
+
*/
|
|
129
|
+
getServerTools(serverName) {
|
|
130
|
+
return this.tools.get(serverName) || [];
|
|
589
131
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
let mostAccessedId;
|
|
596
|
-
let maxAccess = 0;
|
|
597
|
-
for (const [referenceId, storedContent] of this.contentStore.entries()) {
|
|
598
|
-
if (storedContent.metadata.accessCount > maxAccess) {
|
|
599
|
-
maxAccess = storedContent.metadata.accessCount;
|
|
600
|
-
mostAccessedId = referenceId;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
if (mostAccessedId !== void 0) {
|
|
604
|
-
this.referenceStats.mostAccessedReferenceId = mostAccessedId;
|
|
605
|
-
} else {
|
|
606
|
-
delete this.referenceStats.mostAccessedReferenceId;
|
|
607
|
-
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if a server is connected
|
|
134
|
+
*/
|
|
135
|
+
isServerConnected(serverName) {
|
|
136
|
+
return this.clients.has(serverName);
|
|
608
137
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
metrics.creationTimes.push(timeMs);
|
|
615
|
-
if (metrics.creationTimes.length > maxRecords) {
|
|
616
|
-
metrics.creationTimes.shift();
|
|
617
|
-
}
|
|
618
|
-
break;
|
|
619
|
-
case "resolution":
|
|
620
|
-
metrics.resolutionTimes.push(timeMs);
|
|
621
|
-
if (metrics.resolutionTimes.length > maxRecords) {
|
|
622
|
-
metrics.resolutionTimes.shift();
|
|
623
|
-
}
|
|
624
|
-
break;
|
|
625
|
-
case "cleanup":
|
|
626
|
-
metrics.cleanupTimes.push(timeMs);
|
|
627
|
-
if (metrics.cleanupTimes.length > maxRecords) {
|
|
628
|
-
metrics.cleanupTimes.shift();
|
|
629
|
-
}
|
|
630
|
-
break;
|
|
631
|
-
}
|
|
138
|
+
/**
|
|
139
|
+
* Get list of connected server names
|
|
140
|
+
*/
|
|
141
|
+
getConnectedServers() {
|
|
142
|
+
return Array.from(this.clients.keys());
|
|
632
143
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Enable content processing with content storage
|
|
146
|
+
*/
|
|
147
|
+
enableContentProcessing(contentStorage) {
|
|
148
|
+
this.contentProcessor = new MCPContentProcessor(contentStorage, this.logger);
|
|
149
|
+
this.logger.info("Content processing enabled for MCP responses");
|
|
636
150
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
}, this.referenceConfig.cleanupIntervalMs);
|
|
151
|
+
/**
|
|
152
|
+
* Disable content processing
|
|
153
|
+
*/
|
|
154
|
+
disableContentProcessing() {
|
|
155
|
+
delete this.contentProcessor;
|
|
156
|
+
this.logger.info("Content processing disabled for MCP responses");
|
|
645
157
|
}
|
|
646
158
|
/**
|
|
647
|
-
*
|
|
159
|
+
* Check if content processing is enabled
|
|
648
160
|
*/
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
161
|
+
isContentProcessingEnabled() {
|
|
162
|
+
return this.contentProcessor !== void 0;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Analyze a response without processing it (for testing/debugging)
|
|
166
|
+
*/
|
|
167
|
+
analyzeResponseContent(response) {
|
|
168
|
+
if (!this.contentProcessor) {
|
|
169
|
+
throw new Error("Content processing is not enabled");
|
|
653
170
|
}
|
|
654
|
-
this.
|
|
655
|
-
this.clear();
|
|
171
|
+
return this.contentProcessor.analyzeResponse(response);
|
|
656
172
|
}
|
|
657
|
-
}
|
|
658
|
-
_ContentStorage.DEFAULT_MAX_STORAGE = 1e3;
|
|
659
|
-
let ContentStorage = _ContentStorage;
|
|
173
|
+
}
|
|
660
174
|
export {
|
|
661
|
-
|
|
175
|
+
MCPClientManager
|
|
662
176
|
};
|
|
663
177
|
//# sourceMappingURL=index20.js.map
|