mcp-use 0.1.7 ā 0.1.9
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/README.md +196 -0
- package/dist/examples/ai_sdk_example.d.ts +23 -0
- package/dist/examples/ai_sdk_example.d.ts.map +1 -0
- package/dist/examples/ai_sdk_example.js +213 -0
- package/dist/examples/stream_example.d.ts +12 -0
- package/dist/examples/stream_example.d.ts.map +1 -0
- package/dist/examples/stream_example.js +198 -0
- package/dist/examples/structured_output.d.ts +9 -0
- package/dist/examples/structured_output.d.ts.map +1 -0
- package/dist/examples/structured_output.js +95 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/src/agents/mcp_agent.d.ts +24 -4
- package/dist/src/agents/mcp_agent.d.ts.map +1 -1
- package/dist/src/agents/mcp_agent.js +355 -50
- package/dist/src/agents/utils/ai_sdk.d.ts +22 -0
- package/dist/src/agents/utils/ai_sdk.d.ts.map +1 -0
- package/dist/src/agents/utils/ai_sdk.js +62 -0
- package/dist/src/agents/utils/index.d.ts +2 -0
- package/dist/src/agents/utils/index.d.ts.map +1 -0
- package/dist/src/agents/utils/index.js +1 -0
- package/dist/tests/ai_sdk_compatibility.test.d.ts +13 -0
- package/dist/tests/ai_sdk_compatibility.test.d.ts.map +1 -0
- package/dist/tests/ai_sdk_compatibility.test.js +214 -0
- package/dist/tests/stream_events.test.d.ts +2 -0
- package/dist/tests/stream_events.test.d.ts.map +1 -0
- package/dist/tests/stream_events.test.js +306 -0
- package/dist/tests/stream_events_simple.test.d.ts +7 -0
- package/dist/tests/stream_events_simple.test.d.ts.map +1 -0
- package/dist/tests/stream_events_simple.test.js +179 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +21 -0
- package/package.json +16 -5
@@ -22,12 +22,12 @@ export class MCPAgent {
|
|
22
22
|
systemPrompt;
|
23
23
|
systemPromptTemplateOverride;
|
24
24
|
additionalInstructions;
|
25
|
-
|
25
|
+
_initialized = false;
|
26
26
|
conversationHistory = [];
|
27
|
-
|
27
|
+
_agentExecutor = null;
|
28
28
|
sessions = {};
|
29
29
|
systemMessage = null;
|
30
|
-
|
30
|
+
_tools = [];
|
31
31
|
adapter;
|
32
32
|
serverManager = null;
|
33
33
|
telemetry;
|
@@ -67,6 +67,19 @@ export class MCPAgent {
|
|
67
67
|
const [provider, name] = extractModelInfo(this.llm);
|
68
68
|
this.modelProvider = provider;
|
69
69
|
this.modelName = name;
|
70
|
+
// Make getters configurable for test mocking
|
71
|
+
Object.defineProperty(this, 'agentExecutor', {
|
72
|
+
get: () => this._agentExecutor,
|
73
|
+
configurable: true,
|
74
|
+
});
|
75
|
+
Object.defineProperty(this, 'tools', {
|
76
|
+
get: () => this._tools,
|
77
|
+
configurable: true,
|
78
|
+
});
|
79
|
+
Object.defineProperty(this, 'initialized', {
|
80
|
+
get: () => this._initialized,
|
81
|
+
configurable: true,
|
82
|
+
});
|
70
83
|
}
|
71
84
|
async initialize() {
|
72
85
|
logger.info('š Initializing MCP agent and connecting to services...');
|
@@ -75,11 +88,11 @@ export class MCPAgent {
|
|
75
88
|
await this.serverManager.initialize();
|
76
89
|
// Get server management tools
|
77
90
|
const managementTools = this.serverManager.tools;
|
78
|
-
this.
|
79
|
-
this.
|
91
|
+
this._tools = managementTools;
|
92
|
+
this._tools.push(...this.additionalTools);
|
80
93
|
logger.info(`š§ Server manager mode active with ${managementTools.length} management tools`);
|
81
94
|
// Create the system message based on available tools
|
82
|
-
await this.createSystemMessageFromTools(this.
|
95
|
+
await this.createSystemMessageFromTools(this._tools);
|
83
96
|
}
|
84
97
|
else {
|
85
98
|
// Standard initialization - if using client, get or create sessions
|
@@ -94,9 +107,9 @@ export class MCPAgent {
|
|
94
107
|
logger.info(`ā
Created ${Object.keys(this.sessions).length} new sessions`);
|
95
108
|
}
|
96
109
|
// Create LangChain tools directly from the client using the adapter
|
97
|
-
this.
|
98
|
-
this.
|
99
|
-
logger.info(`š ļø Created ${this.
|
110
|
+
this._tools = await LangChainAdapter.createTools(this.client);
|
111
|
+
this._tools.push(...this.additionalTools);
|
112
|
+
logger.info(`š ļø Created ${this._tools.length} LangChain tools from client`);
|
100
113
|
}
|
101
114
|
else {
|
102
115
|
// Using direct connector - only establish connection
|
@@ -107,18 +120,18 @@ export class MCPAgent {
|
|
107
120
|
}
|
108
121
|
}
|
109
122
|
// Create LangChain tools using the adapter with connectors
|
110
|
-
this.
|
111
|
-
this.
|
112
|
-
logger.info(`š ļø Created ${this.
|
123
|
+
this._tools = await this.adapter.createToolsFromConnectors(this.connectors);
|
124
|
+
this._tools.push(...this.additionalTools);
|
125
|
+
logger.info(`š ļø Created ${this._tools.length} LangChain tools from connectors`);
|
113
126
|
}
|
114
127
|
// Get all tools for system message generation
|
115
|
-
logger.info(`š§° Found ${this.
|
128
|
+
logger.info(`š§° Found ${this._tools.length} tools across all connectors`);
|
116
129
|
// Create the system message based on available tools
|
117
|
-
await this.createSystemMessageFromTools(this.
|
130
|
+
await this.createSystemMessageFromTools(this._tools);
|
118
131
|
}
|
119
132
|
// Create the agent executor and mark initialized
|
120
|
-
this.
|
121
|
-
this.
|
133
|
+
this._agentExecutor = this.createAgent();
|
134
|
+
this._initialized = true;
|
122
135
|
logger.info('⨠Agent initialization complete');
|
123
136
|
}
|
124
137
|
async createSystemMessageFromTools(tools) {
|
@@ -142,12 +155,12 @@ export class MCPAgent {
|
|
142
155
|
]);
|
143
156
|
const agent = createToolCallingAgent({
|
144
157
|
llm: this.llm,
|
145
|
-
tools: this.
|
158
|
+
tools: this._tools,
|
146
159
|
prompt,
|
147
160
|
});
|
148
161
|
return new AgentExecutor({
|
149
162
|
agent,
|
150
|
-
tools: this.
|
163
|
+
tools: this._tools,
|
151
164
|
maxIterations: this.maxSteps,
|
152
165
|
verbose: this.verbose,
|
153
166
|
returnIntermediateSteps: true,
|
@@ -172,15 +185,15 @@ export class MCPAgent {
|
|
172
185
|
this.conversationHistory = this.conversationHistory.filter(m => !(m instanceof SystemMessage));
|
173
186
|
this.conversationHistory.unshift(this.systemMessage);
|
174
187
|
}
|
175
|
-
if (this.
|
176
|
-
this.
|
188
|
+
if (this._initialized && this._tools.length) {
|
189
|
+
this._agentExecutor = this.createAgent();
|
177
190
|
logger.debug('Agent recreated with new system message');
|
178
191
|
}
|
179
192
|
}
|
180
193
|
setDisallowedTools(disallowedTools) {
|
181
194
|
this.disallowedTools = disallowedTools;
|
182
195
|
this.adapter = new LangChainAdapter(this.disallowedTools);
|
183
|
-
if (this.
|
196
|
+
if (this._initialized) {
|
184
197
|
logger.debug('Agent already initialized. Changes will take effect on next initialization.');
|
185
198
|
}
|
186
199
|
}
|
@@ -198,37 +211,68 @@ export class MCPAgent {
|
|
198
211
|
}
|
199
212
|
}
|
200
213
|
}
|
201
|
-
|
202
|
-
|
203
|
-
*/
|
204
|
-
async run(query, maxSteps, manageConnector, externalHistory) {
|
205
|
-
const generator = this.stream(query, maxSteps, manageConnector, externalHistory);
|
214
|
+
async run(query, maxSteps, manageConnector, externalHistory, outputSchema) {
|
215
|
+
const generator = this.stream(query, maxSteps, manageConnector, externalHistory, outputSchema);
|
206
216
|
return this._consumeAndReturn(generator);
|
207
217
|
}
|
208
218
|
/**
|
209
219
|
* Runs the agent and yields intermediate steps as an async generator.
|
220
|
+
* If outputSchema is provided, returns structured output of type T.
|
210
221
|
*/
|
211
|
-
async *stream(query, maxSteps, manageConnector = true, externalHistory) {
|
222
|
+
async *stream(query, maxSteps, manageConnector = true, externalHistory, outputSchema) {
|
212
223
|
let result = '';
|
213
224
|
let initializedHere = false;
|
214
225
|
const startTime = Date.now();
|
215
226
|
const toolsUsedNames = [];
|
216
227
|
let stepsTaken = 0;
|
217
228
|
let success = false;
|
229
|
+
// Schema-aware setup for structured output
|
230
|
+
let structuredLlm = null;
|
231
|
+
let schemaDescription = '';
|
232
|
+
if (outputSchema) {
|
233
|
+
query = this._enhanceQueryWithSchema(query, outputSchema);
|
234
|
+
// Check if withStructuredOutput method exists
|
235
|
+
if ('withStructuredOutput' in this.llm && typeof this.llm.withStructuredOutput === 'function') {
|
236
|
+
structuredLlm = this.llm.withStructuredOutput(outputSchema);
|
237
|
+
}
|
238
|
+
else {
|
239
|
+
// Fallback: use the same LLM but we'll handle structure in our helper method
|
240
|
+
structuredLlm = this.llm;
|
241
|
+
}
|
242
|
+
// Get schema description for feedback
|
243
|
+
try {
|
244
|
+
const schemaType = outputSchema;
|
245
|
+
if (schemaType._def && schemaType._def.shape) {
|
246
|
+
const fields = [];
|
247
|
+
for (const [key, fieldSchema] of Object.entries(schemaType._def.shape)) {
|
248
|
+
const field = fieldSchema;
|
249
|
+
const isOptional = field.isOptional?.() ?? field._def?.typeName === 'ZodOptional';
|
250
|
+
const isNullable = field.isNullable?.() ?? field._def?.typeName === 'ZodNullable';
|
251
|
+
const description = field._def?.description || field.description || key;
|
252
|
+
fields.push(`- ${key}: ${description} ${(isOptional || isNullable) ? '(optional)' : '(required)'}`);
|
253
|
+
}
|
254
|
+
schemaDescription = fields.join('\n');
|
255
|
+
}
|
256
|
+
}
|
257
|
+
catch (e) {
|
258
|
+
logger.warn(`Could not extract schema details: ${e}`);
|
259
|
+
schemaDescription = `Schema: ${outputSchema.constructor.name}`;
|
260
|
+
}
|
261
|
+
}
|
218
262
|
try {
|
219
|
-
if (manageConnector && !this.
|
263
|
+
if (manageConnector && !this._initialized) {
|
220
264
|
await this.initialize();
|
221
265
|
initializedHere = true;
|
222
266
|
}
|
223
|
-
else if (!this.
|
267
|
+
else if (!this._initialized && this.autoInitialize) {
|
224
268
|
await this.initialize();
|
225
269
|
initializedHere = true;
|
226
270
|
}
|
227
|
-
if (!this.
|
271
|
+
if (!this._agentExecutor) {
|
228
272
|
throw new Error('MCP agent failed to initialize');
|
229
273
|
}
|
230
274
|
const steps = maxSteps ?? this.maxSteps;
|
231
|
-
this.
|
275
|
+
this._agentExecutor.maxIterations = steps;
|
232
276
|
const display_query = query.length > 50 ? `${query.slice(0, 50).replace(/\n/g, ' ')}...` : query.replace(/\n/g, ' ');
|
233
277
|
logger.info(`š¬ Received query: '${display_query}'`);
|
234
278
|
// āāā Record user message
|
@@ -244,34 +288,73 @@ export class MCPAgent {
|
|
244
288
|
}
|
245
289
|
const intermediateSteps = [];
|
246
290
|
const inputs = { input: query, chat_history: langchainHistory };
|
247
|
-
let nameToToolMap = Object.fromEntries(this.
|
291
|
+
let nameToToolMap = Object.fromEntries(this._tools.map(t => [t.name, t]));
|
248
292
|
logger.info(`š Starting agent execution with max_steps=${steps}`);
|
249
293
|
for (let stepNum = 0; stepNum < steps; stepNum++) {
|
250
294
|
stepsTaken = stepNum + 1;
|
251
295
|
if (this.useServerManager && this.serverManager) {
|
252
296
|
const currentTools = this.serverManager.tools;
|
253
297
|
const currentToolNames = new Set(currentTools.map(t => t.name));
|
254
|
-
const existingToolNames = new Set(this.
|
255
|
-
const changed = currentTools.length !== this.
|
298
|
+
const existingToolNames = new Set(this._tools.map(t => t.name));
|
299
|
+
const changed = currentTools.length !== this._tools.length
|
256
300
|
|| [...currentToolNames].some(n => !existingToolNames.has(n));
|
257
301
|
if (changed) {
|
258
302
|
logger.info(`š Tools changed before step ${stepNum + 1}, updating agent. New tools: ${[...currentToolNames].join(', ')}`);
|
259
|
-
this.
|
260
|
-
this.
|
261
|
-
await this.createSystemMessageFromTools(this.
|
262
|
-
this.
|
263
|
-
this.
|
264
|
-
nameToToolMap = Object.fromEntries(this.
|
303
|
+
this._tools = currentTools;
|
304
|
+
this._tools.push(...this.additionalTools);
|
305
|
+
await this.createSystemMessageFromTools(this._tools);
|
306
|
+
this._agentExecutor = this.createAgent();
|
307
|
+
this._agentExecutor.maxIterations = steps;
|
308
|
+
nameToToolMap = Object.fromEntries(this._tools.map(t => [t.name, t]));
|
265
309
|
}
|
266
310
|
}
|
267
311
|
logger.info(`š£ Step ${stepNum + 1}/${steps}`);
|
268
312
|
try {
|
269
313
|
logger.debug('Starting agent step execution');
|
270
|
-
const nextStepOutput = await this.
|
314
|
+
const nextStepOutput = await this._agentExecutor._takeNextStep(nameToToolMap, inputs, intermediateSteps);
|
271
315
|
if (nextStepOutput.returnValues) {
|
272
316
|
logger.info(`ā
Agent finished at step ${stepNum + 1}`);
|
273
317
|
result = nextStepOutput.returnValues?.output ?? 'No output generated';
|
274
|
-
|
318
|
+
// If structured output is requested, attempt to create it
|
319
|
+
if (outputSchema && structuredLlm) {
|
320
|
+
try {
|
321
|
+
logger.info('š§ Attempting structured output...');
|
322
|
+
const structuredResult = await this._attemptStructuredOutput(result, structuredLlm, outputSchema, schemaDescription);
|
323
|
+
// Add the final response to conversation history if memory is enabled
|
324
|
+
if (this.memoryEnabled) {
|
325
|
+
this.addToHistory(new AIMessage(`Structured result: ${JSON.stringify(structuredResult)}`));
|
326
|
+
}
|
327
|
+
logger.info('ā
Structured output successful');
|
328
|
+
success = true;
|
329
|
+
return structuredResult;
|
330
|
+
}
|
331
|
+
catch (e) {
|
332
|
+
logger.warn(`ā ļø Structured output failed: ${e}`);
|
333
|
+
// Continue execution to gather missing information
|
334
|
+
const missingInfoPrompt = `
|
335
|
+
The current result cannot be formatted into the required structure.
|
336
|
+
Error: ${String(e)}
|
337
|
+
|
338
|
+
Current information: ${result}
|
339
|
+
|
340
|
+
Please continue working to gather the missing information needed for:
|
341
|
+
${schemaDescription}
|
342
|
+
|
343
|
+
Focus on finding the specific missing details.
|
344
|
+
`;
|
345
|
+
// Add this as feedback and continue the loop
|
346
|
+
inputs.input = missingInfoPrompt;
|
347
|
+
if (this.memoryEnabled) {
|
348
|
+
this.addToHistory(new HumanMessage(missingInfoPrompt));
|
349
|
+
}
|
350
|
+
logger.info('š Continuing execution to gather missing information...');
|
351
|
+
continue;
|
352
|
+
}
|
353
|
+
}
|
354
|
+
else {
|
355
|
+
// Regular execution without structured output
|
356
|
+
break;
|
357
|
+
}
|
275
358
|
}
|
276
359
|
const stepArray = nextStepOutput;
|
277
360
|
intermediateSteps.push(...stepArray);
|
@@ -280,7 +363,9 @@ export class MCPAgent {
|
|
280
363
|
const { action, observation } = step;
|
281
364
|
const toolName = action.tool;
|
282
365
|
toolsUsedNames.push(toolName);
|
283
|
-
let toolInputStr =
|
366
|
+
let toolInputStr = typeof action.toolInput === 'string'
|
367
|
+
? action.toolInput
|
368
|
+
: JSON.stringify(action.toolInput, null, 2);
|
284
369
|
if (toolInputStr.length > 100)
|
285
370
|
toolInputStr = `${toolInputStr.slice(0, 97)}...`;
|
286
371
|
logger.info(`š§ Tool call: ${toolName} with input: ${toolInputStr}`);
|
@@ -293,7 +378,7 @@ export class MCPAgent {
|
|
293
378
|
// Detect direct return
|
294
379
|
if (stepArray.length) {
|
295
380
|
const lastStep = stepArray[stepArray.length - 1];
|
296
|
-
const toolReturn = await this.
|
381
|
+
const toolReturn = await this._agentExecutor._getToolReturn(lastStep);
|
297
382
|
if (toolReturn) {
|
298
383
|
logger.info(`š Tool returned directly at step ${stepNum + 1}`);
|
299
384
|
result = toolReturn.returnValues?.output ?? 'No output generated';
|
@@ -318,11 +403,31 @@ export class MCPAgent {
|
|
318
403
|
logger.warn(`ā ļø Agent stopped after reaching max iterations (${steps})`);
|
319
404
|
result = `Agent stopped after reaching the maximum number of steps (${steps}).`;
|
320
405
|
}
|
321
|
-
|
406
|
+
// If structured output was requested but not achieved, attempt one final time
|
407
|
+
if (outputSchema && structuredLlm && !success) {
|
408
|
+
try {
|
409
|
+
logger.info('š§ Final attempt at structured output...');
|
410
|
+
const structuredResult = await this._attemptStructuredOutput(result, structuredLlm, outputSchema, schemaDescription);
|
411
|
+
// Add the final response to conversation history if memory is enabled
|
412
|
+
if (this.memoryEnabled) {
|
413
|
+
this.addToHistory(new AIMessage(`Structured result: ${JSON.stringify(structuredResult)}`));
|
414
|
+
}
|
415
|
+
logger.info('ā
Final structured output successful');
|
416
|
+
success = true;
|
417
|
+
return structuredResult;
|
418
|
+
}
|
419
|
+
catch (e) {
|
420
|
+
logger.error(`ā Final structured output attempt failed: ${e}`);
|
421
|
+
throw new Error(`Failed to generate structured output after ${steps} steps: ${e}`);
|
422
|
+
}
|
423
|
+
}
|
424
|
+
// Add the final response to conversation history if memory is enabled (regular case)
|
425
|
+
if (this.memoryEnabled && !outputSchema) {
|
322
426
|
this.addToHistory(new AIMessage(result));
|
323
427
|
}
|
324
428
|
logger.info('š Agent execution complete');
|
325
429
|
success = true;
|
430
|
+
// Return regular result
|
326
431
|
return result;
|
327
432
|
}
|
328
433
|
catch (e) {
|
@@ -352,8 +457,8 @@ export class MCPAgent {
|
|
352
457
|
modelName: this.modelName,
|
353
458
|
serverCount,
|
354
459
|
serverIdentifiers: this.connectors.map(connector => connector.publicIdentifier),
|
355
|
-
totalToolsAvailable: this.
|
356
|
-
toolsAvailableNames: this.
|
460
|
+
totalToolsAvailable: this._tools.length,
|
461
|
+
toolsAvailableNames: this._tools.map(t => t.name),
|
357
462
|
maxStepsConfigured: this.maxSteps,
|
358
463
|
memoryEnabled: this.memoryEnabled,
|
359
464
|
useServerManager: this.useServerManager,
|
@@ -377,8 +482,8 @@ export class MCPAgent {
|
|
377
482
|
async close() {
|
378
483
|
logger.info('š Closing MCPAgent resourcesā¦');
|
379
484
|
try {
|
380
|
-
this.
|
381
|
-
this.
|
485
|
+
this._agentExecutor = null;
|
486
|
+
this._tools = [];
|
382
487
|
if (this.client) {
|
383
488
|
logger.info('š Closing sessions through client');
|
384
489
|
await this.client.closeAllSessions();
|
@@ -395,8 +500,208 @@ export class MCPAgent {
|
|
395
500
|
}
|
396
501
|
}
|
397
502
|
finally {
|
398
|
-
this.
|
503
|
+
this._initialized = false;
|
399
504
|
logger.info('š Agent closed successfully');
|
400
505
|
}
|
401
506
|
}
|
507
|
+
/**
|
508
|
+
* Yields LangChain StreamEvent objects from the underlying streamEvents() method.
|
509
|
+
* This provides token-level streaming and fine-grained event updates.
|
510
|
+
*/
|
511
|
+
async *streamEvents(query, maxSteps, manageConnector = true, externalHistory) {
|
512
|
+
let initializedHere = false;
|
513
|
+
const startTime = Date.now();
|
514
|
+
let success = false;
|
515
|
+
let eventCount = 0;
|
516
|
+
let totalResponseLength = 0;
|
517
|
+
try {
|
518
|
+
// Initialize if needed
|
519
|
+
if (manageConnector && !this._initialized) {
|
520
|
+
await this.initialize();
|
521
|
+
initializedHere = true;
|
522
|
+
}
|
523
|
+
else if (!this._initialized && this.autoInitialize) {
|
524
|
+
await this.initialize();
|
525
|
+
initializedHere = true;
|
526
|
+
}
|
527
|
+
const agentExecutor = this.agentExecutor;
|
528
|
+
if (!agentExecutor) {
|
529
|
+
throw new Error('MCP agent failed to initialize');
|
530
|
+
}
|
531
|
+
// Set max iterations
|
532
|
+
const steps = maxSteps ?? this.maxSteps;
|
533
|
+
agentExecutor.maxIterations = steps;
|
534
|
+
const display_query = query.length > 50 ? `${query.slice(0, 50).replace(/\n/g, ' ')}...` : query.replace(/\n/g, ' ');
|
535
|
+
logger.info(`š¬ Received query for streamEvents: '${display_query}'`);
|
536
|
+
// Add user message to history if memory enabled
|
537
|
+
if (this.memoryEnabled) {
|
538
|
+
this.addToHistory(new HumanMessage(query));
|
539
|
+
}
|
540
|
+
// Prepare history
|
541
|
+
const historyToUse = externalHistory ?? this.conversationHistory;
|
542
|
+
const langchainHistory = [];
|
543
|
+
for (const msg of historyToUse) {
|
544
|
+
if (msg instanceof HumanMessage || msg instanceof AIMessage) {
|
545
|
+
langchainHistory.push(msg);
|
546
|
+
}
|
547
|
+
}
|
548
|
+
// Prepare inputs
|
549
|
+
const inputs = { input: query, chat_history: langchainHistory };
|
550
|
+
// Stream events from the agent executor
|
551
|
+
const eventStream = agentExecutor.streamEvents(inputs, { version: 'v2' });
|
552
|
+
// Yield each event
|
553
|
+
for await (const event of eventStream) {
|
554
|
+
eventCount++;
|
555
|
+
// Skip null or invalid events
|
556
|
+
if (!event || typeof event !== 'object') {
|
557
|
+
continue;
|
558
|
+
}
|
559
|
+
// Track response length for telemetry
|
560
|
+
if (event.event === 'on_chat_model_stream' && event.data?.chunk?.content) {
|
561
|
+
totalResponseLength += event.data.chunk.content.length;
|
562
|
+
}
|
563
|
+
yield event;
|
564
|
+
// Handle final message for history
|
565
|
+
if (event.event === 'on_chain_end' && event.data?.output) {
|
566
|
+
const output = event.data.output;
|
567
|
+
if (typeof output === 'string' && this.memoryEnabled) {
|
568
|
+
this.addToHistory(new AIMessage(output));
|
569
|
+
}
|
570
|
+
else if (output?.output && typeof output.output === 'string' && this.memoryEnabled) {
|
571
|
+
this.addToHistory(new AIMessage(output.output));
|
572
|
+
}
|
573
|
+
}
|
574
|
+
}
|
575
|
+
logger.info(`š StreamEvents complete - ${eventCount} events emitted`);
|
576
|
+
success = true;
|
577
|
+
}
|
578
|
+
catch (e) {
|
579
|
+
logger.error(`ā Error during streamEvents: ${e}`);
|
580
|
+
if (initializedHere && manageConnector) {
|
581
|
+
logger.info('š§¹ Cleaning up resources after initialization error in streamEvents');
|
582
|
+
await this.close();
|
583
|
+
}
|
584
|
+
throw e;
|
585
|
+
}
|
586
|
+
finally {
|
587
|
+
// Track telemetry
|
588
|
+
const executionTimeMs = Date.now() - startTime;
|
589
|
+
let serverCount = 0;
|
590
|
+
if (this.client) {
|
591
|
+
serverCount = Object.keys(await this.client.getAllActiveSessions()).length;
|
592
|
+
}
|
593
|
+
else if (this.connectors) {
|
594
|
+
serverCount = this.connectors.length;
|
595
|
+
}
|
596
|
+
const conversationHistoryLength = this.memoryEnabled ? this.conversationHistory.length : 0;
|
597
|
+
await this.telemetry.trackAgentExecution({
|
598
|
+
executionMethod: 'streamEvents',
|
599
|
+
query,
|
600
|
+
success,
|
601
|
+
modelProvider: this.modelProvider,
|
602
|
+
modelName: this.modelName,
|
603
|
+
serverCount,
|
604
|
+
serverIdentifiers: this.connectors.map(connector => connector.publicIdentifier),
|
605
|
+
totalToolsAvailable: this._tools.length,
|
606
|
+
toolsAvailableNames: this._tools.map(t => t.name),
|
607
|
+
maxStepsConfigured: this.maxSteps,
|
608
|
+
memoryEnabled: this.memoryEnabled,
|
609
|
+
useServerManager: this.useServerManager,
|
610
|
+
maxStepsUsed: maxSteps ?? null,
|
611
|
+
manageConnector,
|
612
|
+
externalHistoryUsed: externalHistory !== undefined,
|
613
|
+
response: `[STREAMED RESPONSE - ${totalResponseLength} chars]`,
|
614
|
+
executionTimeMs,
|
615
|
+
errorType: success ? null : 'streaming_error',
|
616
|
+
conversationHistoryLength,
|
617
|
+
});
|
618
|
+
// Clean up if needed
|
619
|
+
if (manageConnector && !this.client && initializedHere) {
|
620
|
+
logger.info('š§¹ Closing agent after streamEvents completion');
|
621
|
+
await this.close();
|
622
|
+
}
|
623
|
+
}
|
624
|
+
}
|
625
|
+
/**
|
626
|
+
* Attempt to create structured output from raw result with validation.
|
627
|
+
*/
|
628
|
+
async _attemptStructuredOutput(rawResult, structuredLlm, outputSchema, schemaDescription) {
|
629
|
+
const formatPrompt = `
|
630
|
+
Please format the following information according to the specified schema.
|
631
|
+
Extract and structure the relevant information from the content below.
|
632
|
+
|
633
|
+
Required schema fields:
|
634
|
+
${schemaDescription}
|
635
|
+
|
636
|
+
Content to format:
|
637
|
+
${rawResult}
|
638
|
+
|
639
|
+
Please provide the information in the requested structured format.
|
640
|
+
If any required information is missing, you must indicate this clearly.
|
641
|
+
`;
|
642
|
+
const structuredResult = await structuredLlm.invoke(formatPrompt);
|
643
|
+
// Validate that the result is complete (basic check)
|
644
|
+
try {
|
645
|
+
// Use Zod to validate the structured result
|
646
|
+
const validatedResult = outputSchema.parse(structuredResult);
|
647
|
+
// Additional validation for required fields
|
648
|
+
const schemaType = outputSchema;
|
649
|
+
if (schemaType._def && schemaType._def.shape) {
|
650
|
+
for (const [fieldName, fieldSchema] of Object.entries(schemaType._def.shape)) {
|
651
|
+
const field = fieldSchema;
|
652
|
+
const isOptional = field.isOptional?.() ?? field._def?.typeName === 'ZodOptional';
|
653
|
+
const isNullable = field.isNullable?.() ?? field._def?.typeName === 'ZodNullable';
|
654
|
+
if (!isOptional && !isNullable) {
|
655
|
+
const value = validatedResult[fieldName];
|
656
|
+
if (value === null || value === undefined
|
657
|
+
|| (typeof value === 'string' && !value.trim())
|
658
|
+
|| (Array.isArray(value) && value.length === 0)) {
|
659
|
+
throw new Error(`Required field '${fieldName}' is missing or empty`);
|
660
|
+
}
|
661
|
+
}
|
662
|
+
}
|
663
|
+
}
|
664
|
+
return validatedResult;
|
665
|
+
}
|
666
|
+
catch (e) {
|
667
|
+
logger.debug(`Validation details: ${e}`);
|
668
|
+
throw e; // Re-raise to trigger retry logic
|
669
|
+
}
|
670
|
+
}
|
671
|
+
/**
|
672
|
+
* Enhance the query with schema information to make the agent aware of required fields.
|
673
|
+
*/
|
674
|
+
_enhanceQueryWithSchema(query, outputSchema) {
|
675
|
+
const schemaFields = [];
|
676
|
+
try {
|
677
|
+
// Get field information from the schema
|
678
|
+
const schemaType = outputSchema;
|
679
|
+
if (schemaType._def && schemaType._def.shape) {
|
680
|
+
for (const [fieldName, fieldSchema] of Object.entries(schemaType._def.shape)) {
|
681
|
+
const field = fieldSchema;
|
682
|
+
const description = field._def?.description || field.description || fieldName;
|
683
|
+
const isOptional = field.isOptional?.() ?? field._def?.typeName === 'ZodOptional';
|
684
|
+
const isNullable = field.isNullable?.() ?? field._def?.typeName === 'ZodNullable';
|
685
|
+
schemaFields.push(`- ${fieldName}: ${description} ${(isOptional || isNullable) ? '(optional)' : '(required)'}`);
|
686
|
+
}
|
687
|
+
}
|
688
|
+
const schemaDescription = schemaFields.join('\n');
|
689
|
+
// Enhance the query with schema awareness
|
690
|
+
const enhancedQuery = `
|
691
|
+
${query}
|
692
|
+
|
693
|
+
IMPORTANT: Your response must include sufficient information to populate the following structured output:
|
694
|
+
|
695
|
+
${schemaDescription}
|
696
|
+
|
697
|
+
Make sure you gather ALL the required information during your task execution.
|
698
|
+
If any required information is missing, continue working to find it.
|
699
|
+
`;
|
700
|
+
return enhancedQuery;
|
701
|
+
}
|
702
|
+
catch (e) {
|
703
|
+
logger.warn(`Could not extract schema details: ${e}`);
|
704
|
+
return query;
|
705
|
+
}
|
706
|
+
}
|
402
707
|
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/**
|
2
|
+
* AI SDK Integration Utilities
|
3
|
+
*
|
4
|
+
* Utility functions for integrating MCPAgent's streamEvents with Vercel AI SDK.
|
5
|
+
* These utilities help convert stream events to AI SDK compatible formats.
|
6
|
+
*/
|
7
|
+
import type { StreamEvent } from '@langchain/core/tracers/log_stream';
|
8
|
+
/**
|
9
|
+
* Converts streamEvents to AI SDK compatible stream (basic version)
|
10
|
+
* Only yields the actual content tokens from chat model streams
|
11
|
+
*/
|
12
|
+
export declare function streamEventsToAISDK(streamEvents: AsyncGenerator<StreamEvent, void, void>): AsyncGenerator<string, void, void>;
|
13
|
+
/**
|
14
|
+
* Converts async generator to ReadableStream for AI SDK compatibility
|
15
|
+
*/
|
16
|
+
export declare function createReadableStreamFromGenerator(generator: AsyncGenerator<string, void, void>): ReadableStream<string>;
|
17
|
+
/**
|
18
|
+
* Enhanced adapter that includes tool information along with chat content
|
19
|
+
* Yields both content tokens and tool usage notifications
|
20
|
+
*/
|
21
|
+
export declare function streamEventsToAISDKWithTools(streamEvents: AsyncGenerator<StreamEvent, void, void>): AsyncGenerator<string, void, void>;
|
22
|
+
//# sourceMappingURL=ai_sdk.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"ai_sdk.d.ts","sourceRoot":"","sources":["../../../../src/agents/utils/ai_sdk.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAA;AAErE;;;GAGG;AACH,wBAAuB,mBAAmB,CACxC,YAAY,EAAE,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,GACpD,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CASpC;AAED;;GAEG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAC5C,cAAc,CAAC,MAAM,CAAC,CAcxB;AAED;;;GAGG;AACH,wBAAuB,4BAA4B,CACjD,YAAY,EAAE,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,GACpD,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAqBpC"}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
/**
|
2
|
+
* AI SDK Integration Utilities
|
3
|
+
*
|
4
|
+
* Utility functions for integrating MCPAgent's streamEvents with Vercel AI SDK.
|
5
|
+
* These utilities help convert stream events to AI SDK compatible formats.
|
6
|
+
*/
|
7
|
+
/**
|
8
|
+
* Converts streamEvents to AI SDK compatible stream (basic version)
|
9
|
+
* Only yields the actual content tokens from chat model streams
|
10
|
+
*/
|
11
|
+
export async function* streamEventsToAISDK(streamEvents) {
|
12
|
+
for await (const event of streamEvents) {
|
13
|
+
if (event.event === 'on_chat_model_stream' && event.data?.chunk?.text) {
|
14
|
+
const textContent = event.data.chunk.text;
|
15
|
+
if (typeof textContent === 'string' && textContent.length > 0) {
|
16
|
+
yield textContent;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
/**
|
22
|
+
* Converts async generator to ReadableStream for AI SDK compatibility
|
23
|
+
*/
|
24
|
+
export function createReadableStreamFromGenerator(generator) {
|
25
|
+
return new ReadableStream({
|
26
|
+
async start(controller) {
|
27
|
+
try {
|
28
|
+
for await (const chunk of generator) {
|
29
|
+
controller.enqueue(chunk);
|
30
|
+
}
|
31
|
+
controller.close();
|
32
|
+
}
|
33
|
+
catch (error) {
|
34
|
+
controller.error(error);
|
35
|
+
}
|
36
|
+
},
|
37
|
+
});
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* Enhanced adapter that includes tool information along with chat content
|
41
|
+
* Yields both content tokens and tool usage notifications
|
42
|
+
*/
|
43
|
+
export async function* streamEventsToAISDKWithTools(streamEvents) {
|
44
|
+
for await (const event of streamEvents) {
|
45
|
+
switch (event.event) {
|
46
|
+
case 'on_chat_model_stream':
|
47
|
+
if (event.data?.chunk?.text) {
|
48
|
+
const textContent = event.data.chunk.text;
|
49
|
+
if (typeof textContent === 'string' && textContent.length > 0) {
|
50
|
+
yield textContent;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
break;
|
54
|
+
case 'on_tool_start':
|
55
|
+
yield `\nš§ Using tool: ${event.name}\n`;
|
56
|
+
break;
|
57
|
+
case 'on_tool_end':
|
58
|
+
yield `\nā
Tool completed: ${event.name}\n`;
|
59
|
+
break;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agents/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,aAAa,CAAA"}
|
@@ -0,0 +1 @@
|
|
1
|
+
export { createReadableStreamFromGenerator, streamEventsToAISDK, streamEventsToAISDKWithTools, } from './ai_sdk.js';
|