@juspay/neurolink 7.13.0 → 7.14.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 +6 -0
- package/README.md +89 -25
- package/dist/config/conversationMemoryConfig.js +2 -1
- package/dist/context/ContextManager.js +15 -4
- package/dist/context/config.js +5 -1
- package/dist/context/utils.js +1 -1
- package/dist/core/baseProvider.d.ts +16 -1
- package/dist/core/baseProvider.js +208 -9
- package/dist/core/conversationMemoryManager.js +3 -2
- package/dist/core/factory.js +13 -2
- package/dist/factories/providerFactory.js +5 -11
- package/dist/factories/providerRegistry.js +2 -2
- package/dist/lib/config/conversationMemoryConfig.js +2 -1
- package/dist/lib/context/ContextManager.js +15 -4
- package/dist/lib/context/config.js +5 -1
- package/dist/lib/context/utils.js +1 -1
- package/dist/lib/core/baseProvider.d.ts +16 -1
- package/dist/lib/core/baseProvider.js +208 -9
- package/dist/lib/core/conversationMemoryManager.js +3 -2
- package/dist/lib/core/factory.js +13 -2
- package/dist/lib/factories/providerFactory.js +5 -11
- package/dist/lib/factories/providerRegistry.js +2 -2
- package/dist/lib/mcp/externalServerManager.d.ts +115 -0
- package/dist/lib/mcp/externalServerManager.js +677 -0
- package/dist/lib/mcp/mcpCircuitBreaker.d.ts +184 -0
- package/dist/lib/mcp/mcpCircuitBreaker.js +338 -0
- package/dist/lib/mcp/mcpClientFactory.d.ts +104 -0
- package/dist/lib/mcp/mcpClientFactory.js +416 -0
- package/dist/lib/mcp/toolDiscoveryService.d.ts +192 -0
- package/dist/lib/mcp/toolDiscoveryService.js +578 -0
- package/dist/lib/neurolink.d.ts +111 -16
- package/dist/lib/neurolink.js +517 -50
- package/dist/lib/providers/googleVertex.d.ts +1 -1
- package/dist/lib/providers/googleVertex.js +23 -7
- package/dist/lib/types/externalMcp.d.ts +282 -0
- package/dist/lib/types/externalMcp.js +6 -0
- package/dist/lib/types/generateTypes.d.ts +0 -1
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/mcp/externalServerManager.d.ts +115 -0
- package/dist/mcp/externalServerManager.js +677 -0
- package/dist/mcp/mcpCircuitBreaker.d.ts +184 -0
- package/dist/mcp/mcpCircuitBreaker.js +338 -0
- package/dist/mcp/mcpClientFactory.d.ts +104 -0
- package/dist/mcp/mcpClientFactory.js +416 -0
- package/dist/mcp/toolDiscoveryService.d.ts +192 -0
- package/dist/mcp/toolDiscoveryService.js +578 -0
- package/dist/neurolink.d.ts +111 -16
- package/dist/neurolink.js +517 -50
- package/dist/providers/googleVertex.d.ts +1 -1
- package/dist/providers/googleVertex.js +23 -7
- package/dist/types/externalMcp.d.ts +282 -0
- package/dist/types/externalMcp.js +6 -0
- package/dist/types/generateTypes.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [7.14.0](https://github.com/juspay/neurolink/compare/v7.13.0...v7.14.0) (2025-08-14)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- **(external-mcp):** add external MCP server integration support ([c03dee8](https://github.com/juspay/neurolink/commit/c03dee8dd7a2e06e78bc743d7b3a5cff858395de))
|
|
6
|
+
|
|
1
7
|
## [7.13.0](https://github.com/juspay/neurolink/compare/v7.12.0...v7.13.0) (2025-08-14)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ npx @juspay/neurolink sagemaker benchmark my-endpoint # Performance testing
|
|
|
69
69
|
- **🛡️ Error Recovery** - Graceful failures with provider fallback and retry logic
|
|
70
70
|
- **📊 Analytics & Evaluation** - Built-in usage tracking and AI-powered quality assessment
|
|
71
71
|
- **🎯 Real-time Event Monitoring** - EventEmitter integration for progress tracking and debugging
|
|
72
|
-
- **🔧 MCP Integration** - Model Context Protocol with 6 built-in tools
|
|
72
|
+
- **🔧 External MCP Integration** - Model Context Protocol with 6 built-in tools + full external MCP server support
|
|
73
73
|
- **🚀 Lighthouse Integration** - Unified tool registration API supporting both object and array formats for seamless Lighthouse tool import
|
|
74
74
|
|
|
75
75
|
---
|
|
@@ -118,6 +118,28 @@ npx @juspay/neurolink status # Check all providers
|
|
|
118
118
|
```bash
|
|
119
119
|
# SDK Installation for using in your typescript projects
|
|
120
120
|
npm install @juspay/neurolink
|
|
121
|
+
|
|
122
|
+
# 🆕 NEW: External MCP Server Integration Quick Test
|
|
123
|
+
node -e "
|
|
124
|
+
const { NeuroLink } = require('@juspay/neurolink');
|
|
125
|
+
(async () => {
|
|
126
|
+
const neurolink = new NeuroLink();
|
|
127
|
+
|
|
128
|
+
// Add external filesystem MCP server
|
|
129
|
+
await neurolink.addExternalMCPServer('filesystem', {
|
|
130
|
+
command: 'npx',
|
|
131
|
+
args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
|
|
132
|
+
transport: 'stdio'
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// External tools automatically available in generate()
|
|
136
|
+
const result = await neurolink.generate({
|
|
137
|
+
input: { text: 'List files in the current directory' }
|
|
138
|
+
});
|
|
139
|
+
console.log('🎉 External MCP integration working!');
|
|
140
|
+
console.log(result.content);
|
|
141
|
+
})();
|
|
142
|
+
"
|
|
121
143
|
```
|
|
122
144
|
|
|
123
145
|
### Basic Usage
|
|
@@ -327,41 +349,56 @@ console.log(productData.name, productData.price, productData.features);
|
|
|
327
349
|
- ⚡ **Automatic Fallback** - Never fail when providers are down, intelligent provider switching
|
|
328
350
|
- 🖥️ **CLI + SDK** - Use from command line or integrate programmatically with TypeScript support
|
|
329
351
|
- 🛡️ **Production Ready** - Enterprise-grade error handling, performance optimization, extracted from production
|
|
330
|
-
- ✅ **MCP Integration** - Model Context Protocol with
|
|
352
|
+
- ✅ **External MCP Integration** - Model Context Protocol with built-in tools + full external MCP server support
|
|
331
353
|
- 🔍 **Smart Model Resolution** - Fuzzy matching, aliases, and capability-based search across all providers
|
|
332
354
|
- 🏠 **Local AI Support** - Run completely offline with Ollama or through LiteLLM proxy
|
|
333
355
|
- 🌍 **Universal Model Access** - Direct providers + 100,000+ models via Hugging Face + 100+ models via LiteLLM
|
|
334
356
|
- 🧠 **Automatic Context Summarization** - Stateful, long-running conversations with automatic history summarization.
|
|
335
357
|
- 📊 **Analytics & Evaluation** - Built-in usage tracking and AI-powered quality assessment
|
|
336
358
|
|
|
337
|
-
## 🛠️ MCP Integration Status ✅ **
|
|
359
|
+
## 🛠️ External MCP Integration Status ✅ **PRODUCTION READY**
|
|
338
360
|
|
|
339
|
-
| Component
|
|
340
|
-
|
|
|
341
|
-
| Built-in Tools
|
|
342
|
-
| SDK Custom Tools
|
|
343
|
-
| External
|
|
344
|
-
| Tool Execution
|
|
345
|
-
| **
|
|
346
|
-
| **
|
|
347
|
-
|
|
|
361
|
+
| Component | Status | Description |
|
|
362
|
+
| ---------------------- | -------------- | ---------------------------------------------------------------- |
|
|
363
|
+
| Built-in Tools | ✅ **Working** | 6 core tools fully functional across all providers |
|
|
364
|
+
| SDK Custom Tools | ✅ **Working** | Register custom tools programmatically |
|
|
365
|
+
| **External MCP Tools** | ✅ **Working** | **Full external MCP server support with dynamic tool discovery** |
|
|
366
|
+
| Tool Execution | ✅ **Working** | Real-time AI tool calling with all tool types |
|
|
367
|
+
| **Streaming Support** | ✅ **Working** | **External MCP tools work with streaming generation** |
|
|
368
|
+
| **Multi-Provider** | ✅ **Working** | **External tools work across all AI providers** |
|
|
369
|
+
| **CLI Integration** | ✅ **READY** | **Production-ready with external MCP support** |
|
|
348
370
|
|
|
349
|
-
### ✅
|
|
371
|
+
### ✅ External MCP Integration Demo
|
|
350
372
|
|
|
351
373
|
```bash
|
|
352
374
|
# Test built-in tools (works immediately)
|
|
353
375
|
npx @juspay/neurolink generate "What time is it?" --debug
|
|
354
376
|
|
|
355
|
-
#
|
|
356
|
-
|
|
377
|
+
# 🆕 NEW: External MCP server integration (SDK)
|
|
378
|
+
import { NeuroLink } from '@juspay/neurolink';
|
|
379
|
+
|
|
380
|
+
const neurolink = new NeuroLink();
|
|
381
|
+
|
|
382
|
+
// Add external MCP server (e.g., Bitbucket)
|
|
383
|
+
await neurolink.addExternalMCPServer('bitbucket', {
|
|
384
|
+
command: 'npx',
|
|
385
|
+
args: ['-y', '@nexus2520/bitbucket-mcp-server'],
|
|
386
|
+
transport: 'stdio',
|
|
387
|
+
env: {
|
|
388
|
+
BITBUCKET_USERNAME: process.env.BITBUCKET_USERNAME,
|
|
389
|
+
BITBUCKET_TOKEN: process.env.BITBUCKET_TOKEN,
|
|
390
|
+
BITBUCKET_BASE_URL: 'https://bitbucket.example.com'
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Use external MCP tools in generation
|
|
395
|
+
const result = await neurolink.generate({
|
|
396
|
+
input: { text: 'Get pull request #123 details from the main repository' },
|
|
397
|
+
disableTools: false // External MCP tools automatically available
|
|
398
|
+
});
|
|
357
399
|
|
|
358
400
|
# Discover available MCP servers
|
|
359
401
|
npx @juspay/neurolink mcp discover --format table
|
|
360
|
-
|
|
361
|
-
# Install popular MCP servers (NEW: Bitbucket support added!)
|
|
362
|
-
npx @juspay/neurolink mcp install filesystem
|
|
363
|
-
npx @juspay/neurolink mcp install github
|
|
364
|
-
npx @juspay/neurolink mcp install bitbucket # 🆕 NEW
|
|
365
402
|
```
|
|
366
403
|
|
|
367
404
|
### 🔧 SDK Custom Tool Registration (NEW!)
|
|
@@ -663,16 +700,43 @@ npx @juspay/neurolink generate "Hello!" --provider openai-compatible
|
|
|
663
700
|
- **Extensibility**: Connect external tools and services via MCP protocol
|
|
664
701
|
- **🆕 Dynamic Server Management**: Programmatically add MCP servers at runtime
|
|
665
702
|
|
|
666
|
-
### 🔧
|
|
703
|
+
### 🔧 External MCP Server Management ✅ **AVAILABLE NOW**
|
|
667
704
|
|
|
668
|
-
**
|
|
705
|
+
**External MCP integration is now production-ready:**
|
|
669
706
|
|
|
670
707
|
- ✅ 6 built-in tools working across all providers
|
|
671
708
|
- ✅ SDK custom tool registration
|
|
672
|
-
-
|
|
673
|
-
-
|
|
709
|
+
- ✅ **External MCP server management** (add, remove, list, test servers)
|
|
710
|
+
- ✅ **Dynamic tool discovery** (automatic tool registration from external servers)
|
|
711
|
+
- ✅ **Multi-provider support** (external tools work with all AI providers)
|
|
712
|
+
- ✅ **Streaming integration** (external tools work with real-time streaming)
|
|
713
|
+
- ✅ **Enhanced tool tracking** (proper parameter extraction and execution logging)
|
|
674
714
|
|
|
675
|
-
|
|
715
|
+
```typescript
|
|
716
|
+
// Complete external MCP server API
|
|
717
|
+
const neurolink = new NeuroLink();
|
|
718
|
+
|
|
719
|
+
// Server management
|
|
720
|
+
await neurolink.addExternalMCPServer(serverId, config);
|
|
721
|
+
await neurolink.removeExternalMCPServer(serverId);
|
|
722
|
+
const servers = neurolink.listExternalMCPServers();
|
|
723
|
+
const server = neurolink.getExternalMCPServer(serverId);
|
|
724
|
+
|
|
725
|
+
// Tool management
|
|
726
|
+
const tools = neurolink.getExternalMCPTools();
|
|
727
|
+
const serverTools = neurolink.getExternalMCPServerTools(serverId);
|
|
728
|
+
|
|
729
|
+
// Direct tool execution
|
|
730
|
+
const result = await neurolink.executeExternalMCPTool(
|
|
731
|
+
serverId,
|
|
732
|
+
toolName,
|
|
733
|
+
params,
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
// Statistics and monitoring
|
|
737
|
+
const stats = neurolink.getExternalMCPStatistics();
|
|
738
|
+
await neurolink.shutdownExternalMCPServers();
|
|
739
|
+
```
|
|
676
740
|
|
|
677
741
|
## 🤝 Contributing
|
|
678
742
|
|
|
@@ -34,6 +34,7 @@ export function getConversationMemoryDefaults() {
|
|
|
34
34
|
return {
|
|
35
35
|
enabled: process.env.NEUROLINK_MEMORY_ENABLED === "true",
|
|
36
36
|
maxSessions: Number(process.env.NEUROLINK_MEMORY_MAX_SESSIONS) || DEFAULT_MAX_SESSIONS,
|
|
37
|
-
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
37
|
+
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
38
|
+
DEFAULT_MAX_TURNS_PER_SESSION,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
@@ -14,7 +14,10 @@ export class ContextManager {
|
|
|
14
14
|
constructor(generatorFunction, config, initialContext = "This is the start of the conversation.") {
|
|
15
15
|
this.internalGenerator = generatorFunction;
|
|
16
16
|
this.config = config;
|
|
17
|
-
const initialMessage = {
|
|
17
|
+
const initialMessage = {
|
|
18
|
+
role: "system",
|
|
19
|
+
content: initialContext,
|
|
20
|
+
};
|
|
18
21
|
initialMessage.wordCount = this.config.estimateWordCount([initialMessage]);
|
|
19
22
|
this.history = [initialMessage];
|
|
20
23
|
this.wordCount = initialMessage.wordCount;
|
|
@@ -55,7 +58,9 @@ export class ContextManager {
|
|
|
55
58
|
const result = await this.internalGenerator(textOptions);
|
|
56
59
|
if (typeof result.content === "string" && result.content.length > 0) {
|
|
57
60
|
// Replace the history with a single system message containing the summary
|
|
58
|
-
const newHistory = [
|
|
61
|
+
const newHistory = [
|
|
62
|
+
{ role: "system", content: result.content },
|
|
63
|
+
];
|
|
59
64
|
this.history = newHistory;
|
|
60
65
|
this.wordCount = this.config.estimateWordCount(this.history);
|
|
61
66
|
logger.info(`[ContextManager] Summarization complete. New history length: ${this.wordCount} words.`);
|
|
@@ -63,7 +68,10 @@ export class ContextManager {
|
|
|
63
68
|
else {
|
|
64
69
|
logger.warn("[ContextManager] Summarization returned empty or non-string content; truncating history as a fallback.");
|
|
65
70
|
this._truncateHistory(this.config.lowWaterMarkWords);
|
|
66
|
-
this.history.unshift({
|
|
71
|
+
this.history.unshift({
|
|
72
|
+
role: "system",
|
|
73
|
+
content: ContextManager.SUMMARIZATION_EMPTY_WARNING,
|
|
74
|
+
});
|
|
67
75
|
this.wordCount = this.config.estimateWordCount(this.history);
|
|
68
76
|
}
|
|
69
77
|
logger.debug(`[ContextManager] New history: ${JSON.stringify(this.history)}`);
|
|
@@ -72,7 +80,10 @@ export class ContextManager {
|
|
|
72
80
|
logger.error("Context summarization failed:", { error });
|
|
73
81
|
// Fallback strategy: truncate the history to the target word count.
|
|
74
82
|
this._truncateHistory(this.config.lowWaterMarkWords);
|
|
75
|
-
this.history.unshift({
|
|
83
|
+
this.history.unshift({
|
|
84
|
+
role: "system",
|
|
85
|
+
content: ContextManager.SUMMARIZATION_FAILED_WARNING,
|
|
86
|
+
});
|
|
76
87
|
this.wordCount = this.config.estimateWordCount(this.history);
|
|
77
88
|
}
|
|
78
89
|
}
|
package/dist/context/config.js
CHANGED
|
@@ -6,7 +6,11 @@ function estimateWordCount(history) {
|
|
|
6
6
|
if (!history || history.length === 0) {
|
|
7
7
|
return 0;
|
|
8
8
|
}
|
|
9
|
-
return history.reduce((acc, msg) => acc +
|
|
9
|
+
return history.reduce((acc, msg) => acc +
|
|
10
|
+
(msg.content
|
|
11
|
+
.trim()
|
|
12
|
+
.split(/\s+/)
|
|
13
|
+
.filter((word) => word.length > 0).length || 0), 0);
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* Generates the default prompt for summarization.
|
package/dist/context/utils.js
CHANGED
|
@@ -6,7 +6,7 @@ import type { StreamOptions, StreamResult } from "../types/streamTypes.js";
|
|
|
6
6
|
import type { JsonValue, UnknownRecord } from "../types/common.js";
|
|
7
7
|
import type { ToolResult } from "../types/tools.js";
|
|
8
8
|
/**
|
|
9
|
-
* Interface for SDK with in-memory MCP servers
|
|
9
|
+
* Interface for SDK with in-memory MCP servers and external MCP support
|
|
10
10
|
*/
|
|
11
11
|
export interface NeuroLinkSDK {
|
|
12
12
|
getInMemoryServers?: () => Map<string, {
|
|
@@ -18,6 +18,16 @@ export interface NeuroLinkSDK {
|
|
|
18
18
|
category?: string;
|
|
19
19
|
metadata?: UnknownRecord;
|
|
20
20
|
}>;
|
|
21
|
+
externalServerManager?: {
|
|
22
|
+
getAllTools: () => Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
serverId: string;
|
|
26
|
+
isAvailable: boolean;
|
|
27
|
+
inputSchema?: Record<string, unknown>;
|
|
28
|
+
}>;
|
|
29
|
+
executeTool: (serverId: string, toolName: string, params: any) => Promise<any>;
|
|
30
|
+
};
|
|
21
31
|
}
|
|
22
32
|
/**
|
|
23
33
|
* Interface for tool information in MCP servers
|
|
@@ -86,6 +96,11 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
86
96
|
* MCP tools are added when available (without blocking)
|
|
87
97
|
*/
|
|
88
98
|
protected getAllTools(): Promise<Record<string, Tool>>;
|
|
99
|
+
/**
|
|
100
|
+
* Convert MCP JSON Schema to Zod schema for AI SDK tools
|
|
101
|
+
* Handles common MCP schema patterns safely
|
|
102
|
+
*/
|
|
103
|
+
private convertMCPSchemaToZod;
|
|
89
104
|
/**
|
|
90
105
|
* Set session context for MCP tools
|
|
91
106
|
*/
|
|
@@ -157,10 +157,20 @@ export class BaseProvider {
|
|
|
157
157
|
try {
|
|
158
158
|
// Import generateText dynamically to avoid circular dependencies
|
|
159
159
|
const { generateText } = await import("ai");
|
|
160
|
-
// Get ALL available tools (direct + MCP
|
|
160
|
+
// Get ALL available tools (direct + MCP + external from options)
|
|
161
161
|
const shouldUseTools = !options.disableTools && this.supportsTools();
|
|
162
|
-
const
|
|
163
|
-
|
|
162
|
+
const baseTools = shouldUseTools ? await this.getAllTools() : {};
|
|
163
|
+
const tools = shouldUseTools
|
|
164
|
+
? {
|
|
165
|
+
...baseTools,
|
|
166
|
+
...(options.tools || {}), // Include external tools passed from NeuroLink
|
|
167
|
+
}
|
|
168
|
+
: {};
|
|
169
|
+
logger.debug(`[BaseProvider.generate] Tools for ${this.providerName}:`, {
|
|
170
|
+
directTools: Object.keys(baseTools),
|
|
171
|
+
externalTools: Object.keys(options.tools || {}),
|
|
172
|
+
totalTools: Object.keys(tools),
|
|
173
|
+
});
|
|
164
174
|
// EVERY provider uses Vercel AI SDK - no exceptions
|
|
165
175
|
const model = await this.getAISDKModel(); // This method is now REQUIRED
|
|
166
176
|
// Build proper message array with conversation history
|
|
@@ -201,22 +211,66 @@ export class BaseProvider {
|
|
|
201
211
|
const uniqueToolsUsed = [...new Set(toolsUsed)];
|
|
202
212
|
// ✅ Extract tool executions from AI SDK result
|
|
203
213
|
const toolExecutions = [];
|
|
214
|
+
// Create a map of tool calls to their arguments for matching with results
|
|
215
|
+
const toolCallArgsMap = new Map();
|
|
204
216
|
// Extract tool executions from AI SDK result steps
|
|
205
|
-
// Extract tool executions from steps (where tool results are stored)
|
|
206
217
|
if (result.steps &&
|
|
207
218
|
Array.isArray(result.steps)) {
|
|
208
219
|
for (const step of result.steps ||
|
|
209
220
|
[]) {
|
|
210
|
-
//
|
|
211
|
-
|
|
221
|
+
// First, collect tool calls and their arguments
|
|
222
|
+
if (step?.toolCalls && Array.isArray(step.toolCalls)) {
|
|
223
|
+
for (const toolCall of step.toolCalls) {
|
|
224
|
+
const tcRecord = toolCall;
|
|
225
|
+
const toolName = tcRecord.toolName ||
|
|
226
|
+
tcRecord.name ||
|
|
227
|
+
"unknown";
|
|
228
|
+
const toolId = tcRecord.toolCallId ||
|
|
229
|
+
tcRecord.id ||
|
|
230
|
+
toolName;
|
|
231
|
+
// Extract arguments from tool call
|
|
232
|
+
let callArgs = {};
|
|
233
|
+
if (tcRecord.args) {
|
|
234
|
+
callArgs = tcRecord.args;
|
|
235
|
+
}
|
|
236
|
+
else if (tcRecord.arguments) {
|
|
237
|
+
callArgs = tcRecord.arguments;
|
|
238
|
+
}
|
|
239
|
+
else if (tcRecord.parameters) {
|
|
240
|
+
callArgs = tcRecord.parameters;
|
|
241
|
+
}
|
|
242
|
+
toolCallArgsMap.set(toolId, callArgs);
|
|
243
|
+
toolCallArgsMap.set(toolName, callArgs); // Also map by name as fallback
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Then, process tool results and match with call arguments
|
|
212
247
|
if (step?.toolResults && Array.isArray(step.toolResults)) {
|
|
213
248
|
for (const toolResult of step.toolResults) {
|
|
214
249
|
const trRecord = toolResult;
|
|
250
|
+
const toolName = trRecord.toolName || "unknown";
|
|
251
|
+
const toolId = trRecord.toolCallId || trRecord.id;
|
|
252
|
+
// Try to get arguments from the tool result first
|
|
253
|
+
let toolArgs = {};
|
|
254
|
+
if (trRecord.args) {
|
|
255
|
+
toolArgs = trRecord.args;
|
|
256
|
+
}
|
|
257
|
+
else if (trRecord.arguments) {
|
|
258
|
+
toolArgs = trRecord.arguments;
|
|
259
|
+
}
|
|
260
|
+
else if (trRecord.parameters) {
|
|
261
|
+
toolArgs = trRecord.parameters;
|
|
262
|
+
}
|
|
263
|
+
else if (trRecord.input) {
|
|
264
|
+
toolArgs = trRecord.input;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// Fallback: get arguments from the corresponding tool call
|
|
268
|
+
toolArgs = toolCallArgsMap.get(toolId || toolName) || {};
|
|
269
|
+
}
|
|
215
270
|
toolExecutions.push({
|
|
216
|
-
name:
|
|
217
|
-
input:
|
|
271
|
+
name: toolName,
|
|
272
|
+
input: toolArgs,
|
|
218
273
|
output: trRecord.result || "success",
|
|
219
|
-
duration: 0, // AI SDK doesn't track duration
|
|
220
274
|
});
|
|
221
275
|
}
|
|
222
276
|
}
|
|
@@ -248,6 +302,12 @@ export class BaseProvider {
|
|
|
248
302
|
toolResults: result.toolResults,
|
|
249
303
|
toolsUsed: uniqueToolsUsed,
|
|
250
304
|
toolExecutions, // ✅ Add extracted tool executions
|
|
305
|
+
availableTools: Object.keys(tools).map((name) => ({
|
|
306
|
+
name,
|
|
307
|
+
description: tools[name].description || "No description available",
|
|
308
|
+
parameters: tools[name].parameters || {},
|
|
309
|
+
server: tools[name].serverId || "direct",
|
|
310
|
+
})),
|
|
251
311
|
};
|
|
252
312
|
// Enhanced result with analytics and evaluation
|
|
253
313
|
return await this.enhanceResult(enhancedResult, options, startTime);
|
|
@@ -339,6 +399,71 @@ export class BaseProvider {
|
|
|
339
399
|
// Not an error - custom tools are optional
|
|
340
400
|
}
|
|
341
401
|
}
|
|
402
|
+
// ✅ CRITICAL FIX: Add external MCP tools if SDK has external server manager
|
|
403
|
+
if (this.sdk &&
|
|
404
|
+
this.sdk.externalServerManager &&
|
|
405
|
+
typeof this.sdk.externalServerManager.getAllTools === "function") {
|
|
406
|
+
try {
|
|
407
|
+
logger.debug(`[BaseProvider] Loading external MCP tools from SDK via externalServerManager`);
|
|
408
|
+
const externalTools = this.sdk.externalServerManager.getAllTools();
|
|
409
|
+
logger.debug(`[BaseProvider] Found ${externalTools.length} external MCP tools`, {
|
|
410
|
+
tools: externalTools.map((t) => ({
|
|
411
|
+
name: t.name,
|
|
412
|
+
available: t.isAvailable,
|
|
413
|
+
server: t.serverId,
|
|
414
|
+
})),
|
|
415
|
+
});
|
|
416
|
+
for (const externalTool of externalTools) {
|
|
417
|
+
if (externalTool.isAvailable) {
|
|
418
|
+
logger.debug(`[BaseProvider] Converting external MCP tool: ${externalTool.name} from ${externalTool.serverId}`);
|
|
419
|
+
// Convert to AI SDK tool format
|
|
420
|
+
const { tool: createAISDKTool } = await import("ai");
|
|
421
|
+
const { z } = await import("zod");
|
|
422
|
+
tools[externalTool.name] = createAISDKTool({
|
|
423
|
+
description: externalTool.description ||
|
|
424
|
+
`External MCP tool ${externalTool.name}`,
|
|
425
|
+
parameters: await this.convertMCPSchemaToZod(externalTool.inputSchema),
|
|
426
|
+
execute: async (args) => {
|
|
427
|
+
logger.debug(`[BaseProvider] Executing external MCP tool: ${externalTool.name}`, { args });
|
|
428
|
+
// Execute via SDK's external server manager
|
|
429
|
+
if (this.sdk &&
|
|
430
|
+
this.sdk.externalServerManager &&
|
|
431
|
+
typeof this.sdk.externalServerManager.executeTool ===
|
|
432
|
+
"function") {
|
|
433
|
+
return await this.sdk.externalServerManager.executeTool(externalTool.serverId, externalTool.name, args);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
throw new Error(`Cannot execute external MCP tool: SDK externalServerManager.executeTool not available`);
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
logger.debug(`[BaseProvider] Successfully added external MCP tool: ${externalTool.name}`);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
logger.debug(`[BaseProvider] Skipping unavailable external MCP tool: ${externalTool.name}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
logger.debug(`[BaseProvider] External MCP tools loading complete`, {
|
|
447
|
+
totalExternalTools: externalTools.length,
|
|
448
|
+
availableExternalTools: externalTools.filter((t) => t.isAvailable)
|
|
449
|
+
.length,
|
|
450
|
+
addedToTools: externalTools.filter((t) => t.isAvailable).length,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
logger.error(`[BaseProvider] Failed to load external MCP tools for ${this.providerName}:`, error);
|
|
455
|
+
// Not an error - external tools are optional
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
logger.debug(`[BaseProvider] No external MCP tools interface available`, {
|
|
460
|
+
hasSDK: !!this.sdk,
|
|
461
|
+
hasExternalServerManager: this.sdk && !!this.sdk.externalServerManager,
|
|
462
|
+
hasGetAllTools: this.sdk &&
|
|
463
|
+
this.sdk.externalServerManager &&
|
|
464
|
+
typeof this.sdk.externalServerManager.getAllTools === "function",
|
|
465
|
+
});
|
|
466
|
+
}
|
|
342
467
|
// MCP tools loading simplified - removed functionCalling dependency
|
|
343
468
|
if (!this.mcpTools) {
|
|
344
469
|
// Set empty tools object - MCP tools are handled at a higher level
|
|
@@ -351,6 +476,80 @@ export class BaseProvider {
|
|
|
351
476
|
logger.debug(`[BaseProvider] getAllTools returning tools: ${Object.keys(tools).join(", ")}`);
|
|
352
477
|
return tools;
|
|
353
478
|
}
|
|
479
|
+
/**
|
|
480
|
+
* Convert MCP JSON Schema to Zod schema for AI SDK tools
|
|
481
|
+
* Handles common MCP schema patterns safely
|
|
482
|
+
*/
|
|
483
|
+
async convertMCPSchemaToZod(inputSchema) {
|
|
484
|
+
const { z } = await import("zod");
|
|
485
|
+
if (!inputSchema || typeof inputSchema !== "object") {
|
|
486
|
+
return z.object({});
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
const schema = inputSchema;
|
|
490
|
+
const zodFields = {};
|
|
491
|
+
// Handle JSON Schema properties
|
|
492
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
493
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
494
|
+
for (const [propName, propDef] of Object.entries(schema.properties)) {
|
|
495
|
+
const prop = propDef;
|
|
496
|
+
let zodType;
|
|
497
|
+
// Convert based on JSON Schema type
|
|
498
|
+
switch (prop.type) {
|
|
499
|
+
case "string":
|
|
500
|
+
zodType = z.string();
|
|
501
|
+
if (prop.description) {
|
|
502
|
+
zodType = zodType.describe(prop.description);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
case "number":
|
|
506
|
+
case "integer":
|
|
507
|
+
zodType = z.number();
|
|
508
|
+
if (prop.description) {
|
|
509
|
+
zodType = zodType.describe(prop.description);
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
case "boolean":
|
|
513
|
+
zodType = z.boolean();
|
|
514
|
+
if (prop.description) {
|
|
515
|
+
zodType = zodType.describe(prop.description);
|
|
516
|
+
}
|
|
517
|
+
break;
|
|
518
|
+
case "array":
|
|
519
|
+
zodType = z.array(z.unknown());
|
|
520
|
+
if (prop.description) {
|
|
521
|
+
zodType = zodType.describe(prop.description);
|
|
522
|
+
}
|
|
523
|
+
break;
|
|
524
|
+
case "object":
|
|
525
|
+
zodType = z.object({});
|
|
526
|
+
if (prop.description) {
|
|
527
|
+
zodType = zodType.describe(prop.description);
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
default:
|
|
531
|
+
// Unknown type, use string as fallback
|
|
532
|
+
zodType = z.string();
|
|
533
|
+
if (prop.description) {
|
|
534
|
+
zodType = zodType.describe(prop.description);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Make optional if not required
|
|
538
|
+
if (!required.has(propName)) {
|
|
539
|
+
zodType = zodType.optional();
|
|
540
|
+
}
|
|
541
|
+
zodFields[propName] = zodType;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return Object.keys(zodFields).length > 0
|
|
545
|
+
? z.object(zodFields)
|
|
546
|
+
: z.object({});
|
|
547
|
+
}
|
|
548
|
+
catch (error) {
|
|
549
|
+
logger.warn(`Failed to convert MCP schema to Zod, using empty schema:`, error);
|
|
550
|
+
return z.object({});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
354
553
|
/**
|
|
355
554
|
* Set session context for MCP tools
|
|
356
555
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Handles in-memory conversation storage, session management, and context injection
|
|
4
4
|
*/
|
|
5
5
|
import { ConversationMemoryError } from "../types/conversationTypes.js";
|
|
6
|
-
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN } from "../config/conversationMemoryConfig.js";
|
|
6
|
+
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN, } from "../config/conversationMemoryConfig.js";
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
8
|
export class ConversationMemoryManager {
|
|
9
9
|
sessions = new Map();
|
|
@@ -49,7 +49,8 @@ export class ConversationMemoryManager {
|
|
|
49
49
|
session.messages.push({ role: "user", content: userMessage }, { role: "assistant", content: aiResponse });
|
|
50
50
|
session.lastActivity = Date.now();
|
|
51
51
|
// Enforce per-session turn limit (each turn = MESSAGES_PER_TURN messages: user + assistant)
|
|
52
|
-
const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) *
|
|
52
|
+
const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) *
|
|
53
|
+
MESSAGES_PER_TURN;
|
|
53
54
|
if (session.messages.length > maxMessages) {
|
|
54
55
|
session.messages = session.messages.slice(-maxMessages);
|
|
55
56
|
}
|
package/dist/core/factory.js
CHANGED
|
@@ -109,13 +109,24 @@ export class AIProviderFactory {
|
|
|
109
109
|
const finalModelName = resolvedModelName === "default" || resolvedModelName === null
|
|
110
110
|
? undefined
|
|
111
111
|
: resolvedModelName;
|
|
112
|
-
|
|
112
|
+
// ✅ CRITICAL FIX: Pass external MCP tools interface to BaseProvider
|
|
113
|
+
let finalSdk = sdk;
|
|
114
|
+
if (sdk && typeof sdk.getExternalMCPTools === "function") {
|
|
115
|
+
finalSdk = {
|
|
116
|
+
...sdk,
|
|
117
|
+
externalServerManager: {
|
|
118
|
+
getAllTools: () => sdk.getExternalMCPTools(),
|
|
119
|
+
executeTool: (serverId, toolName, params) => sdk.executeExternalMCPTool(serverId, toolName, params),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Create provider with enhanced SDK
|
|
124
|
+
const provider = await ProviderFactory.createProvider(normalizedName, finalModelName, finalSdk);
|
|
113
125
|
logger.debug(componentIdentifier, "Pure factory pattern provider created", {
|
|
114
126
|
providerName: normalizedName,
|
|
115
127
|
modelName: finalModelName,
|
|
116
128
|
factoryUsed: true,
|
|
117
129
|
});
|
|
118
|
-
// PURE FACTORY PATTERN: All providers handled by ProviderFactory - no switch statements needed
|
|
119
130
|
// Wrap with MCP if enabled
|
|
120
131
|
if (enableMCP) {
|
|
121
132
|
try {
|
|
@@ -46,22 +46,16 @@ export class ProviderFactory {
|
|
|
46
46
|
model = model || registration.defaultModel;
|
|
47
47
|
}
|
|
48
48
|
try {
|
|
49
|
-
// Try calling as factory function first, then fallback to constructor
|
|
50
49
|
let result;
|
|
51
50
|
try {
|
|
52
|
-
// Try as factory function
|
|
53
|
-
result = registration.constructor(model, providerName, sdk);
|
|
51
|
+
// Try as async factory function first (most providers are async functions)
|
|
52
|
+
result = await registration.constructor(model, providerName, sdk);
|
|
54
53
|
}
|
|
55
|
-
catch (
|
|
56
|
-
// Fallback to constructor
|
|
54
|
+
catch (functionError) {
|
|
55
|
+
// Fallback to constructor - ensure parameters are maintained
|
|
57
56
|
result = new registration.constructor(model, providerName, sdk);
|
|
58
57
|
}
|
|
59
|
-
//
|
|
60
|
-
if (result &&
|
|
61
|
-
typeof result === "object" &&
|
|
62
|
-
typeof result.then === "function") {
|
|
63
|
-
result = await result;
|
|
64
|
-
}
|
|
58
|
+
// Return result (no need to await again if already awaited in try block)
|
|
65
59
|
return result;
|
|
66
60
|
}
|
|
67
61
|
catch (error) {
|
|
@@ -52,9 +52,9 @@ export class ProviderRegistry {
|
|
|
52
52
|
process.env.AZURE_OPENAI_DEPLOYMENT_ID ||
|
|
53
53
|
"gpt-4o-mini", ["azure", "azureOpenai"]);
|
|
54
54
|
// Register Google Vertex AI provider
|
|
55
|
-
ProviderFactory.registerProvider(AIProviderName.VERTEX, async (modelName) => {
|
|
55
|
+
ProviderFactory.registerProvider(AIProviderName.VERTEX, async (modelName, providerName, sdk) => {
|
|
56
56
|
const { GoogleVertexProvider } = await import("../providers/googleVertex.js");
|
|
57
|
-
return new GoogleVertexProvider(modelName);
|
|
57
|
+
return new GoogleVertexProvider(modelName, providerName, sdk);
|
|
58
58
|
}, "claude-sonnet-4@20250514", ["vertex", "googleVertex"]);
|
|
59
59
|
// Register Hugging Face provider (Unified Router implementation)
|
|
60
60
|
ProviderFactory.registerProvider(AIProviderName.HUGGINGFACE, async (modelName) => {
|
|
@@ -34,6 +34,7 @@ export function getConversationMemoryDefaults() {
|
|
|
34
34
|
return {
|
|
35
35
|
enabled: process.env.NEUROLINK_MEMORY_ENABLED === "true",
|
|
36
36
|
maxSessions: Number(process.env.NEUROLINK_MEMORY_MAX_SESSIONS) || DEFAULT_MAX_SESSIONS,
|
|
37
|
-
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
37
|
+
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
38
|
+
DEFAULT_MAX_TURNS_PER_SESSION,
|
|
38
39
|
};
|
|
39
40
|
}
|