@hashgraphonline/conversational-agent 0.1.205 → 0.1.206

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.
Files changed (87) hide show
  1. package/dist/cjs/base-agent.d.ts +6 -0
  2. package/dist/cjs/context/ReferenceContextManager.d.ts +84 -0
  3. package/dist/cjs/context/ReferenceResponseProcessor.d.ts +76 -0
  4. package/dist/cjs/conversational-agent.d.ts +34 -0
  5. package/dist/cjs/index.cjs +1 -1
  6. package/dist/cjs/index.cjs.map +1 -1
  7. package/dist/cjs/langchain-agent.d.ts +1 -0
  8. package/dist/cjs/mcp/ContentProcessor.d.ts +37 -0
  9. package/dist/cjs/mcp/MCPClientManager.d.ts +19 -1
  10. package/dist/cjs/memory/ContentStorage.d.ts +205 -0
  11. package/dist/cjs/memory/MemoryWindow.d.ts +114 -0
  12. package/dist/cjs/memory/ReferenceIdGenerator.d.ts +45 -0
  13. package/dist/cjs/memory/SmartMemoryManager.d.ts +201 -0
  14. package/dist/cjs/memory/TokenCounter.d.ts +61 -0
  15. package/dist/cjs/memory/index.d.ts +7 -0
  16. package/dist/cjs/plugins/hbar-transfer/TransferHbarTool.d.ts +18 -0
  17. package/dist/cjs/services/ContentStoreManager.d.ts +54 -0
  18. package/dist/cjs/types/content-reference.d.ts +213 -0
  19. package/dist/cjs/types/index.d.ts +4 -0
  20. package/dist/esm/index12.js +15 -2
  21. package/dist/esm/index12.js.map +1 -1
  22. package/dist/esm/index14.js +119 -95
  23. package/dist/esm/index14.js.map +1 -1
  24. package/dist/esm/index15.js +159 -114
  25. package/dist/esm/index15.js.map +1 -1
  26. package/dist/esm/index16.js +122 -81
  27. package/dist/esm/index16.js.map +1 -1
  28. package/dist/esm/index17.js +236 -0
  29. package/dist/esm/index17.js.map +1 -0
  30. package/dist/esm/index18.js +95 -0
  31. package/dist/esm/index18.js.map +1 -0
  32. package/dist/esm/index19.js +663 -0
  33. package/dist/esm/index19.js.map +1 -0
  34. package/dist/esm/index2.js +3 -1
  35. package/dist/esm/index2.js.map +1 -1
  36. package/dist/esm/index20.js +233 -0
  37. package/dist/esm/index20.js.map +1 -0
  38. package/dist/esm/index21.js +182 -0
  39. package/dist/esm/index21.js.map +1 -0
  40. package/dist/esm/index22.js +126 -0
  41. package/dist/esm/index22.js.map +1 -0
  42. package/dist/esm/index23.js +68 -0
  43. package/dist/esm/index23.js.map +1 -0
  44. package/dist/esm/index24.js +38 -0
  45. package/dist/esm/index24.js.map +1 -0
  46. package/dist/esm/index6.js +143 -84
  47. package/dist/esm/index6.js.map +1 -1
  48. package/dist/esm/index7.js.map +1 -1
  49. package/dist/esm/index8.js +69 -5
  50. package/dist/esm/index8.js.map +1 -1
  51. package/dist/types/base-agent.d.ts +6 -0
  52. package/dist/types/context/ReferenceContextManager.d.ts +84 -0
  53. package/dist/types/context/ReferenceResponseProcessor.d.ts +76 -0
  54. package/dist/types/conversational-agent.d.ts +34 -0
  55. package/dist/types/langchain-agent.d.ts +1 -0
  56. package/dist/types/mcp/ContentProcessor.d.ts +37 -0
  57. package/dist/types/mcp/MCPClientManager.d.ts +19 -1
  58. package/dist/types/memory/ContentStorage.d.ts +205 -0
  59. package/dist/types/memory/MemoryWindow.d.ts +114 -0
  60. package/dist/types/memory/ReferenceIdGenerator.d.ts +45 -0
  61. package/dist/types/memory/SmartMemoryManager.d.ts +201 -0
  62. package/dist/types/memory/TokenCounter.d.ts +61 -0
  63. package/dist/types/memory/index.d.ts +7 -0
  64. package/dist/types/plugins/hbar-transfer/TransferHbarTool.d.ts +18 -0
  65. package/dist/types/services/ContentStoreManager.d.ts +54 -0
  66. package/dist/types/types/content-reference.d.ts +213 -0
  67. package/dist/types/types/index.d.ts +4 -0
  68. package/package.json +5 -3
  69. package/src/base-agent.ts +6 -0
  70. package/src/context/ReferenceContextManager.ts +345 -0
  71. package/src/context/ReferenceResponseProcessor.ts +296 -0
  72. package/src/conversational-agent.ts +166 -92
  73. package/src/langchain-agent.ts +89 -2
  74. package/src/mcp/ContentProcessor.ts +317 -0
  75. package/src/mcp/MCPClientManager.ts +61 -1
  76. package/src/mcp/adapters/langchain.ts +9 -4
  77. package/src/memory/ContentStorage.ts +954 -0
  78. package/src/memory/MemoryWindow.ts +247 -0
  79. package/src/memory/ReferenceIdGenerator.ts +84 -0
  80. package/src/memory/SmartMemoryManager.ts +323 -0
  81. package/src/memory/TokenCounter.ts +152 -0
  82. package/src/memory/index.ts +8 -0
  83. package/src/plugins/hbar-transfer/TransferHbarTool.ts +19 -1
  84. package/src/plugins/hcs-10/HCS10Plugin.ts +5 -4
  85. package/src/services/ContentStoreManager.ts +199 -0
  86. package/src/types/content-reference.ts +281 -0
  87. package/src/types/index.ts +6 -0
@@ -22,11 +22,13 @@ import {
22
22
  } from './base-agent';
23
23
  import { MCPClientManager } from './mcp/MCPClientManager';
24
24
  import { convertMCPToolToLangChain } from './mcp/adapters/langchain';
25
+ import { SmartMemoryManager } from './memory/SmartMemoryManager';
25
26
 
26
27
  export class LangChainAgent extends BaseAgent {
27
28
  private executor: AgentExecutor | undefined;
28
29
  private systemMessage = '';
29
30
  private mcpManager?: MCPClientManager;
31
+ private smartMemory: SmartMemoryManager | undefined;
30
32
 
31
33
  async boot(): Promise<void> {
32
34
  if (this.initialized) {
@@ -51,7 +53,24 @@ export class LangChainAgent extends BaseAgent {
51
53
  await this.initializeMCP();
52
54
  }
53
55
 
56
+ this.smartMemory = new SmartMemoryManager({
57
+ modelName,
58
+ maxTokens: 90000,
59
+ reserveTokens: 10000,
60
+ storageLimit: 1000
61
+ });
62
+
63
+ this.logger.info('SmartMemoryManager initialized:', {
64
+ modelName,
65
+ toolsCount: this.tools.length,
66
+ maxTokens: 90000,
67
+ reserveTokens: 10000
68
+ });
69
+
54
70
  this.systemMessage = this.buildSystemPrompt();
71
+
72
+ // Set the system prompt in smart memory
73
+ this.smartMemory.setSystemPrompt(this.systemMessage);
55
74
 
56
75
  await this.createExecutor();
57
76
 
@@ -67,22 +86,64 @@ export class LangChainAgent extends BaseAgent {
67
86
  message: string,
68
87
  context?: ConversationContext
69
88
  ): Promise<ChatResponse> {
70
- if (!this.initialized || !this.executor) {
89
+ if (!this.initialized || !this.executor || !this.smartMemory) {
71
90
  throw new Error('Agent not initialized. Call boot() first.');
72
91
  }
73
92
 
74
93
  try {
94
+ this.logger.info('LangChainAgent.chat called with:', { message, contextLength: context?.messages?.length || 0 });
95
+
96
+ // If context is provided, populate smart memory with previous messages
97
+ if (context?.messages && context.messages.length > 0) {
98
+ // Clear existing memory and add messages from context
99
+ this.smartMemory.clear();
100
+
101
+ for (const msg of context.messages) {
102
+ this.smartMemory.addMessage(msg);
103
+ }
104
+ }
105
+
106
+ // Add the current user message to memory
107
+ const { HumanMessage } = await import('@langchain/core/messages');
108
+ this.smartMemory.addMessage(new HumanMessage(message));
109
+
110
+ const memoryStats = this.smartMemory.getMemoryStats();
111
+ this.logger.info('Memory stats before execution:', {
112
+ totalMessages: memoryStats.totalActiveMessages,
113
+ currentTokens: memoryStats.currentTokenCount,
114
+ maxTokens: memoryStats.maxTokens,
115
+ usagePercentage: memoryStats.usagePercentage,
116
+ toolsCount: this.tools.length
117
+ });
118
+
75
119
  const result = await this.executor.invoke({
76
120
  input: message,
77
- chat_history: context?.messages || [],
121
+ chat_history: this.smartMemory.getMessages(),
78
122
  });
79
123
 
124
+ this.logger.info('LangChainAgent executor result:', result);
125
+
80
126
  let response: ChatResponse = {
81
127
  output: result.output || '',
82
128
  message: result.output || '',
83
129
  notes: [],
130
+ intermediateSteps: result.intermediateSteps,
84
131
  };
85
132
 
133
+ // Extract tool calls from intermediate steps
134
+ if (result.intermediateSteps && Array.isArray(result.intermediateSteps)) {
135
+ const toolCalls = result.intermediateSteps.map((step: any, index: number) => ({
136
+ id: `call_${index}`,
137
+ name: step.action?.tool || 'unknown',
138
+ args: step.action?.toolInput || {},
139
+ output: typeof step.observation === 'string' ? step.observation : JSON.stringify(step.observation)
140
+ }));
141
+
142
+ if (toolCalls.length > 0) {
143
+ response.tool_calls = toolCalls;
144
+ }
145
+ }
146
+
86
147
  const parsedSteps = result?.intermediateSteps?.[0]?.observation;
87
148
  if (
88
149
  parsedSteps &&
@@ -101,6 +162,12 @@ export class LangChainAgent extends BaseAgent {
101
162
  response.output = 'Agent action complete.';
102
163
  }
103
164
 
165
+ // Add the AI response to memory
166
+ if (response.output) {
167
+ const { AIMessage } = await import('@langchain/core/messages');
168
+ this.smartMemory.addMessage(new AIMessage(response.output));
169
+ }
170
+
104
171
  if (this.tokenTracker) {
105
172
  const tokenUsage = this.tokenTracker.getLatestTokenUsage();
106
173
  if (tokenUsage) {
@@ -109,8 +176,21 @@ export class LangChainAgent extends BaseAgent {
109
176
  }
110
177
  }
111
178
 
179
+ const finalMemoryStats = this.smartMemory.getMemoryStats();
180
+ response.metadata = {
181
+ ...response.metadata,
182
+ memoryStats: {
183
+ activeMessages: finalMemoryStats.totalActiveMessages,
184
+ tokenUsage: finalMemoryStats.currentTokenCount,
185
+ maxTokens: finalMemoryStats.maxTokens,
186
+ usagePercentage: finalMemoryStats.usagePercentage
187
+ }
188
+ };
189
+
190
+ this.logger.info('LangChainAgent.chat returning response:', response);
112
191
  return response;
113
192
  } catch (error) {
193
+ this.logger.error('LangChainAgent.chat error:', error);
114
194
  return this.handleError(error);
115
195
  }
116
196
  }
@@ -120,6 +200,11 @@ export class LangChainAgent extends BaseAgent {
120
200
  await this.mcpManager.disconnectAll();
121
201
  }
122
202
 
203
+ if (this.smartMemory) {
204
+ this.smartMemory.dispose();
205
+ this.smartMemory = undefined;
206
+ }
207
+
123
208
  this.executor = undefined;
124
209
  this.agentKit = undefined;
125
210
  this.tools = [];
@@ -175,6 +260,7 @@ export class LangChainAgent extends BaseAgent {
175
260
  }
176
261
  }
177
262
 
263
+
178
264
  private async createAgentKit(): Promise<HederaAgentKit> {
179
265
  const corePlugins = getAllHederaCorePlugins();
180
266
  const extensionPlugins = this.config.extensions?.plugins || [];
@@ -232,6 +318,7 @@ export class LangChainAgent extends BaseAgent {
232
318
  prompt,
233
319
  });
234
320
 
321
+ // Create executor without memory - we handle memory manually with SmartMemoryManager
235
322
  this.executor = new AgentExecutor({
236
323
  agent,
237
324
  tools: langchainTools,
@@ -0,0 +1,317 @@
1
+ import type { ContentType, ContentSource } from '../types/content-reference';
2
+ import type { ContentStorage } from '../memory/ContentStorage';
3
+ import { Logger } from '@hashgraphonline/standards-sdk';
4
+
5
+ export interface MCPResponseContent {
6
+ content: unknown;
7
+ type: 'text' | 'image' | 'resource' | 'text[]' | 'image[]';
8
+ sizeBytes: number;
9
+ mimeType?: string;
10
+ }
11
+
12
+ export interface ProcessedResponse {
13
+ content: unknown;
14
+ wasProcessed: boolean;
15
+ referenceCreated?: boolean;
16
+ referenceId?: string;
17
+ originalSize?: number;
18
+ errors?: string[];
19
+ }
20
+
21
+ export interface ContentAnalysis {
22
+ shouldProcess: boolean;
23
+ contents: MCPResponseContent[];
24
+ totalSize: number;
25
+ largestContentSize: number;
26
+ }
27
+
28
+ export class MCPContentProcessor {
29
+ private contentStorage: ContentStorage;
30
+ private logger: Logger;
31
+
32
+ constructor(contentStorage: ContentStorage, logger: Logger) {
33
+ this.contentStorage = contentStorage;
34
+ this.logger = logger;
35
+ }
36
+
37
+ analyzeResponse(response: unknown): ContentAnalysis {
38
+ const contents: MCPResponseContent[] = [];
39
+ let totalSize = 0;
40
+
41
+ this.extractContentFromResponse(response, contents);
42
+
43
+ totalSize = contents.reduce((sum, content) => sum + content.sizeBytes, 0);
44
+ const largestContentSize = contents.reduce((max, content) =>
45
+ Math.max(max, content.sizeBytes), 0);
46
+
47
+ const shouldProcess = contents.some(content =>
48
+ this.contentStorage.shouldUseReference(
49
+ typeof content.content === 'string'
50
+ ? content.content
51
+ : JSON.stringify(content.content)
52
+ )
53
+ );
54
+
55
+ return {
56
+ shouldProcess,
57
+ contents,
58
+ totalSize,
59
+ largestContentSize
60
+ };
61
+ }
62
+
63
+ async processResponse(
64
+ response: unknown,
65
+ serverName: string,
66
+ toolName: string
67
+ ): Promise<ProcessedResponse> {
68
+ try {
69
+ const analysis = this.analyzeResponse(response);
70
+
71
+ if (!analysis.shouldProcess) {
72
+ return {
73
+ content: response,
74
+ wasProcessed: false
75
+ };
76
+ }
77
+
78
+ const processedResponse = await this.createReferencedResponse(
79
+ response,
80
+ analysis,
81
+ serverName,
82
+ toolName
83
+ );
84
+
85
+ return processedResponse;
86
+ } catch (error) {
87
+ this.logger.error('Error processing MCP response:', error);
88
+ return {
89
+ content: response,
90
+ wasProcessed: false,
91
+ errors: [error instanceof Error ? error.message : 'Unknown processing error']
92
+ };
93
+ }
94
+ }
95
+
96
+ private extractContentFromResponse(obj: unknown, contents: MCPResponseContent[]): void {
97
+ if (obj === null || obj === undefined) {
98
+ return;
99
+ }
100
+
101
+ if (Array.isArray(obj)) {
102
+ obj.forEach(item => this.extractContentFromResponse(item, contents));
103
+ return;
104
+ }
105
+
106
+ if (typeof obj === 'object') {
107
+ const record = obj as Record<string, unknown>;
108
+
109
+ if (record.type === 'text' && typeof record.text === 'string') {
110
+ contents.push({
111
+ content: record.text,
112
+ type: 'text',
113
+ sizeBytes: Buffer.byteLength(record.text, 'utf8'),
114
+ mimeType: 'text/plain'
115
+ });
116
+ return;
117
+ }
118
+
119
+ if (record.type === 'image' && typeof record.data === 'string') {
120
+ contents.push({
121
+ content: record.data,
122
+ type: 'image',
123
+ sizeBytes: Math.ceil(record.data.length * 0.75),
124
+ mimeType: record.mimeType as string || 'image/jpeg'
125
+ });
126
+ return;
127
+ }
128
+
129
+ if (record.type === 'resource' && record.resource) {
130
+ const resourceStr = JSON.stringify(record.resource);
131
+ contents.push({
132
+ content: resourceStr,
133
+ type: 'resource',
134
+ sizeBytes: Buffer.byteLength(resourceStr, 'utf8'),
135
+ mimeType: 'application/json'
136
+ });
137
+ return;
138
+ }
139
+
140
+ Object.values(record).forEach(value =>
141
+ this.extractContentFromResponse(value, contents));
142
+ return;
143
+ }
144
+
145
+ if (typeof obj === 'string') {
146
+ if (obj.length > 1000) {
147
+ contents.push({
148
+ content: obj,
149
+ type: 'text',
150
+ sizeBytes: Buffer.byteLength(obj, 'utf8'),
151
+ mimeType: this.detectMimeType(obj)
152
+ });
153
+ }
154
+ }
155
+ }
156
+
157
+ private async createReferencedResponse(
158
+ originalResponse: unknown,
159
+ analysis: ContentAnalysis,
160
+ serverName: string,
161
+ toolName: string
162
+ ): Promise<ProcessedResponse> {
163
+ const processedResponse = this.deepClone(originalResponse);
164
+ const errors: string[] = [];
165
+ let referenceCreated = false;
166
+ let totalReferenceSize = 0;
167
+
168
+ for (const contentInfo of analysis.contents) {
169
+ if (this.contentStorage.shouldUseReference(
170
+ typeof contentInfo.content === 'string'
171
+ ? contentInfo.content
172
+ : JSON.stringify(contentInfo.content)
173
+ )) {
174
+ try {
175
+ const contentBuffer = Buffer.from(
176
+ typeof contentInfo.content === 'string'
177
+ ? contentInfo.content
178
+ : JSON.stringify(contentInfo.content),
179
+ 'utf8'
180
+ );
181
+
182
+ const contentType = this.mapMimeTypeToContentType(contentInfo.mimeType);
183
+
184
+ const metadata: Parameters<typeof this.contentStorage.storeContentIfLarge>[1] = {
185
+ contentType,
186
+ source: 'mcp_tool' as ContentSource,
187
+ mcpToolName: `${serverName}::${toolName}`,
188
+ tags: ['mcp_response', serverName, toolName]
189
+ };
190
+
191
+ if (contentInfo.mimeType !== undefined) {
192
+ metadata.mimeType = contentInfo.mimeType;
193
+ }
194
+
195
+ const reference = await this.contentStorage.storeContentIfLarge(
196
+ contentBuffer,
197
+ metadata
198
+ );
199
+
200
+ if (reference) {
201
+ this.replaceContentInResponse(
202
+ processedResponse,
203
+ contentInfo.content,
204
+ this.createLightweightReference(reference)
205
+ );
206
+ referenceCreated = true;
207
+ totalReferenceSize += contentBuffer.length;
208
+ }
209
+ } catch (error) {
210
+ errors.push(`Failed to create reference: ${error instanceof Error ? error.message : 'Unknown error'}`);
211
+ }
212
+ }
213
+ }
214
+
215
+ const result: ProcessedResponse = {
216
+ content: processedResponse,
217
+ wasProcessed: true,
218
+ referenceCreated,
219
+ originalSize: totalReferenceSize
220
+ };
221
+
222
+ if (errors.length > 0) {
223
+ result.errors = errors;
224
+ }
225
+
226
+ return result;
227
+ }
228
+
229
+ private createLightweightReference(reference: any): Record<string, unknown> {
230
+ return {
231
+ type: 'content_reference',
232
+ referenceId: reference.referenceId,
233
+ preview: reference.preview,
234
+ size: reference.metadata.sizeBytes,
235
+ contentType: reference.metadata.contentType,
236
+ format: 'ref://{id}',
237
+ _isReference: true
238
+ };
239
+ }
240
+
241
+ private replaceContentInResponse(obj: unknown, oldContent: unknown, newContent: unknown): void {
242
+ if (obj === null || obj === undefined) {
243
+ return;
244
+ }
245
+
246
+ if (Array.isArray(obj)) {
247
+ for (let i = 0; i < obj.length; i++) {
248
+ if (obj[i] === oldContent) {
249
+ obj[i] = newContent;
250
+ } else {
251
+ this.replaceContentInResponse(obj[i], oldContent, newContent);
252
+ }
253
+ }
254
+ return;
255
+ }
256
+
257
+ if (typeof obj === 'object') {
258
+ const record = obj as Record<string, unknown>;
259
+
260
+ for (const key in record) {
261
+ if (record[key] === oldContent) {
262
+ record[key] = newContent;
263
+ } else {
264
+ this.replaceContentInResponse(record[key], oldContent, newContent);
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ private detectMimeType(content: string): string {
271
+ if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
272
+ return 'application/json';
273
+ }
274
+ if (content.includes('<html>') || content.includes('<!DOCTYPE')) {
275
+ return 'text/html';
276
+ }
277
+ if (content.includes('# ') || content.includes('## ')) {
278
+ return 'text/markdown';
279
+ }
280
+ return 'text/plain';
281
+ }
282
+
283
+ private mapMimeTypeToContentType(mimeType?: string): ContentType {
284
+ if (!mimeType) return 'text';
285
+
286
+ if (mimeType.startsWith('text/plain')) return 'text';
287
+ if (mimeType === 'application/json') return 'json';
288
+ if (mimeType === 'text/html') return 'html';
289
+ if (mimeType === 'text/markdown') return 'markdown';
290
+ if (mimeType.startsWith('text/')) return 'text';
291
+
292
+ return 'binary';
293
+ }
294
+
295
+ private deepClone<T>(obj: T): T {
296
+ if (obj === null || typeof obj !== 'object') {
297
+ return obj;
298
+ }
299
+
300
+ if (obj instanceof Date) {
301
+ return new Date(obj.getTime()) as T;
302
+ }
303
+
304
+ if (Array.isArray(obj)) {
305
+ return obj.map(item => this.deepClone(item)) as T;
306
+ }
307
+
308
+ const cloned = {} as T;
309
+ for (const key in obj) {
310
+ if (obj.hasOwnProperty(key)) {
311
+ cloned[key] = this.deepClone(obj[key]);
312
+ }
313
+ }
314
+
315
+ return cloned;
316
+ }
317
+ }
@@ -2,6 +2,8 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
3
  import type { MCPServerConfig, MCPToolInfo, MCPConnectionStatus } from './types';
4
4
  import { Logger } from '@hashgraphonline/standards-sdk';
5
+ import type { ContentStorage } from '../memory/ContentStorage';
6
+ import { MCPContentProcessor, ProcessedResponse } from './ContentProcessor';
5
7
 
6
8
  /**
7
9
  * Manages connections to MCP servers and tool discovery
@@ -10,9 +12,13 @@ export class MCPClientManager {
10
12
  private clients: Map<string, Client> = new Map();
11
13
  private tools: Map<string, MCPToolInfo[]> = new Map();
12
14
  private logger: Logger;
15
+ private contentProcessor?: MCPContentProcessor;
13
16
 
14
- constructor(logger: Logger) {
17
+ constructor(logger: Logger, contentStorage?: ContentStorage) {
15
18
  this.logger = logger;
19
+ if (contentStorage) {
20
+ this.contentProcessor = new MCPContentProcessor(contentStorage, logger);
21
+ }
16
22
  }
17
23
 
18
24
  /**
@@ -91,6 +97,27 @@ export class MCPClientManager {
91
97
  arguments: args,
92
98
  });
93
99
 
100
+ if (this.contentProcessor) {
101
+ const processed = await this.contentProcessor.processResponse(result, serverName, toolName);
102
+
103
+ if (processed.wasProcessed) {
104
+ this.logger.debug(
105
+ `Processed MCP response from ${serverName}::${toolName}`,
106
+ {
107
+ referenceCreated: processed.referenceCreated,
108
+ originalSize: processed.originalSize,
109
+ errors: processed.errors
110
+ }
111
+ );
112
+
113
+ if (processed.errors && processed.errors.length > 0) {
114
+ this.logger.warn(`Content processing warnings for ${serverName}::${toolName}:`, processed.errors);
115
+ }
116
+ }
117
+
118
+ return processed.content;
119
+ }
120
+
94
121
  return result;
95
122
  } catch (error) {
96
123
  this.logger.error(`Error executing MCP tool ${toolName}:`, error);
@@ -145,4 +172,37 @@ export class MCPClientManager {
145
172
  getConnectedServers(): string[] {
146
173
  return Array.from(this.clients.keys());
147
174
  }
175
+
176
+ /**
177
+ * Enable content processing with content storage
178
+ */
179
+ enableContentProcessing(contentStorage: ContentStorage): void {
180
+ this.contentProcessor = new MCPContentProcessor(contentStorage, this.logger);
181
+ this.logger.info('Content processing enabled for MCP responses');
182
+ }
183
+
184
+ /**
185
+ * Disable content processing
186
+ */
187
+ disableContentProcessing(): void {
188
+ delete this.contentProcessor;
189
+ this.logger.info('Content processing disabled for MCP responses');
190
+ }
191
+
192
+ /**
193
+ * Check if content processing is enabled
194
+ */
195
+ isContentProcessingEnabled(): boolean {
196
+ return this.contentProcessor !== undefined;
197
+ }
198
+
199
+ /**
200
+ * Analyze a response without processing it (for testing/debugging)
201
+ */
202
+ analyzeResponseContent(response: unknown): unknown {
203
+ if (!this.contentProcessor) {
204
+ throw new Error('Content processing is not enabled');
205
+ }
206
+ return this.contentProcessor.analyzeResponse(response);
207
+ }
148
208
  }
@@ -40,8 +40,10 @@ export function convertMCPToolToLangChain(
40
40
  input
41
41
  );
42
42
 
43
+ let responseText = '';
44
+
43
45
  if (typeof result === 'string') {
44
- return result;
46
+ responseText = result;
45
47
  } else if (
46
48
  result &&
47
49
  typeof result === 'object' &&
@@ -59,12 +61,15 @@ export function convertMCPToolToLangChain(
59
61
  'text' in item
60
62
  )
61
63
  .map((item) => item.text);
62
- return textParts.join('\n');
64
+ responseText = textParts.join('\n');
65
+ } else {
66
+ responseText = JSON.stringify(content);
63
67
  }
64
- return JSON.stringify(content);
68
+ } else {
69
+ responseText = JSON.stringify(result);
65
70
  }
66
71
 
67
- return JSON.stringify(result);
72
+ return responseText;
68
73
  } catch (error) {
69
74
  const errorMessage =
70
75
  error instanceof Error ? error.message : 'Unknown error';