@juspay/neurolink 5.1.0 → 5.3.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.
Files changed (190) hide show
  1. package/CHANGELOG.md +21 -9
  2. package/README.md +123 -126
  3. package/dist/agent/direct-tools.d.ts +6 -6
  4. package/dist/cli/commands/config.d.ts +3 -3
  5. package/dist/cli/commands/mcp.js +8 -7
  6. package/dist/cli/factories/command-factory.d.ts +4 -0
  7. package/dist/cli/factories/command-factory.js +63 -8
  8. package/dist/cli/index.js +87 -140
  9. package/dist/core/base-provider.d.ts +423 -0
  10. package/dist/core/base-provider.js +376 -0
  11. package/dist/core/constants.d.ts +2 -1
  12. package/dist/core/constants.js +2 -1
  13. package/dist/core/dynamic-models.d.ts +6 -6
  14. package/dist/core/evaluation.d.ts +19 -80
  15. package/dist/core/evaluation.js +185 -484
  16. package/dist/core/factory.d.ts +3 -3
  17. package/dist/core/factory.js +31 -91
  18. package/dist/core/service-registry.d.ts +47 -0
  19. package/dist/core/service-registry.js +112 -0
  20. package/dist/core/types.d.ts +8 -1
  21. package/dist/factories/compatibility-factory.js +1 -1
  22. package/dist/factories/provider-factory.d.ts +72 -0
  23. package/dist/factories/provider-factory.js +144 -0
  24. package/dist/factories/provider-registry.d.ts +38 -0
  25. package/dist/factories/provider-registry.js +107 -0
  26. package/dist/index.d.ts +4 -3
  27. package/dist/index.js +2 -4
  28. package/dist/lib/agent/direct-tools.d.ts +6 -6
  29. package/dist/lib/core/base-provider.d.ts +423 -0
  30. package/dist/lib/core/base-provider.js +376 -0
  31. package/dist/lib/core/constants.d.ts +2 -1
  32. package/dist/lib/core/constants.js +2 -1
  33. package/dist/lib/core/dynamic-models.d.ts +6 -6
  34. package/dist/lib/core/evaluation.d.ts +19 -80
  35. package/dist/lib/core/evaluation.js +185 -484
  36. package/dist/lib/core/factory.d.ts +3 -3
  37. package/dist/lib/core/factory.js +30 -91
  38. package/dist/lib/core/service-registry.d.ts +47 -0
  39. package/dist/lib/core/service-registry.js +112 -0
  40. package/dist/lib/core/types.d.ts +8 -1
  41. package/dist/lib/factories/compatibility-factory.js +1 -1
  42. package/dist/lib/factories/provider-factory.d.ts +72 -0
  43. package/dist/lib/factories/provider-factory.js +144 -0
  44. package/dist/lib/factories/provider-registry.d.ts +38 -0
  45. package/dist/lib/factories/provider-registry.js +107 -0
  46. package/dist/lib/index.d.ts +4 -3
  47. package/dist/lib/index.js +2 -4
  48. package/dist/lib/mcp/client.d.ts +1 -0
  49. package/dist/lib/mcp/client.js +1 -0
  50. package/dist/lib/mcp/config.js +28 -3
  51. package/dist/lib/mcp/context-manager.d.ts +1 -0
  52. package/dist/lib/mcp/context-manager.js +8 -4
  53. package/dist/lib/mcp/function-calling.d.ts +13 -0
  54. package/dist/lib/mcp/function-calling.js +134 -35
  55. package/dist/lib/mcp/initialize-tools.d.ts +1 -1
  56. package/dist/lib/mcp/initialize-tools.js +45 -1
  57. package/dist/lib/mcp/initialize.js +16 -6
  58. package/dist/lib/mcp/neurolink-mcp-client.d.ts +1 -0
  59. package/dist/lib/mcp/neurolink-mcp-client.js +21 -5
  60. package/dist/lib/mcp/servers/agent/direct-tools-server.d.ts +8 -0
  61. package/dist/lib/mcp/servers/agent/direct-tools-server.js +109 -0
  62. package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +3 -1
  63. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  64. package/dist/lib/mcp/unified-registry.d.ts +4 -0
  65. package/dist/lib/mcp/unified-registry.js +42 -9
  66. package/dist/lib/neurolink.d.ts +156 -117
  67. package/dist/lib/neurolink.js +619 -404
  68. package/dist/lib/providers/amazon-bedrock.d.ts +32 -0
  69. package/dist/lib/providers/amazon-bedrock.js +143 -0
  70. package/dist/lib/providers/analytics-helper.js +7 -4
  71. package/dist/lib/providers/anthropic-baseprovider.d.ts +23 -0
  72. package/dist/lib/providers/anthropic-baseprovider.js +114 -0
  73. package/dist/lib/providers/anthropic.d.ts +19 -43
  74. package/dist/lib/providers/anthropic.js +82 -306
  75. package/dist/lib/providers/azure-openai.d.ts +20 -0
  76. package/dist/lib/providers/azure-openai.js +89 -0
  77. package/dist/lib/providers/function-calling-provider.d.ts +64 -2
  78. package/dist/lib/providers/function-calling-provider.js +208 -9
  79. package/dist/lib/providers/google-ai-studio.d.ts +23 -0
  80. package/dist/lib/providers/google-ai-studio.js +107 -0
  81. package/dist/lib/providers/google-vertex.d.ts +47 -0
  82. package/dist/lib/providers/google-vertex.js +205 -0
  83. package/dist/lib/providers/huggingFace.d.ts +32 -25
  84. package/dist/lib/providers/huggingFace.js +97 -431
  85. package/dist/lib/providers/index.d.ts +9 -9
  86. package/dist/lib/providers/index.js +9 -9
  87. package/dist/lib/providers/mcp-provider.js +24 -5
  88. package/dist/lib/providers/mistral.d.ts +42 -0
  89. package/dist/lib/providers/mistral.js +160 -0
  90. package/dist/lib/providers/ollama.d.ts +52 -36
  91. package/dist/lib/providers/ollama.js +297 -520
  92. package/dist/lib/providers/openAI.d.ts +19 -18
  93. package/dist/lib/providers/openAI.js +76 -275
  94. package/dist/lib/sdk/tool-extension.d.ts +181 -0
  95. package/dist/lib/sdk/tool-extension.js +283 -0
  96. package/dist/lib/sdk/tool-registration.d.ts +95 -0
  97. package/dist/lib/sdk/tool-registration.js +167 -0
  98. package/dist/lib/services/streaming/streaming-manager.js +11 -10
  99. package/dist/lib/services/websocket/websocket-server.js +12 -11
  100. package/dist/lib/telemetry/telemetry-service.js +8 -7
  101. package/dist/lib/types/generate-types.d.ts +1 -0
  102. package/dist/lib/types/mcp-types.d.ts +116 -0
  103. package/dist/lib/types/mcp-types.js +5 -0
  104. package/dist/lib/types/stream-types.d.ts +30 -18
  105. package/dist/lib/types/universal-provider-options.d.ts +87 -0
  106. package/dist/lib/types/universal-provider-options.js +53 -0
  107. package/dist/mcp/client.d.ts +1 -0
  108. package/dist/mcp/client.js +1 -0
  109. package/dist/mcp/config.js +28 -3
  110. package/dist/mcp/context-manager.d.ts +1 -0
  111. package/dist/mcp/context-manager.js +8 -4
  112. package/dist/mcp/function-calling.d.ts +13 -0
  113. package/dist/mcp/function-calling.js +134 -35
  114. package/dist/mcp/initialize-tools.d.ts +1 -1
  115. package/dist/mcp/initialize-tools.js +45 -1
  116. package/dist/mcp/initialize.js +16 -6
  117. package/dist/mcp/neurolink-mcp-client.d.ts +1 -0
  118. package/dist/mcp/neurolink-mcp-client.js +21 -5
  119. package/dist/mcp/servers/agent/direct-tools-server.d.ts +8 -0
  120. package/dist/mcp/servers/agent/direct-tools-server.js +109 -0
  121. package/dist/mcp/servers/ai-providers/ai-core-server.js +3 -1
  122. package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  123. package/dist/mcp/unified-registry.d.ts +4 -0
  124. package/dist/mcp/unified-registry.js +42 -9
  125. package/dist/neurolink.d.ts +156 -117
  126. package/dist/neurolink.js +619 -404
  127. package/dist/providers/amazon-bedrock.d.ts +32 -0
  128. package/dist/providers/amazon-bedrock.js +143 -0
  129. package/dist/providers/analytics-helper.js +7 -4
  130. package/dist/providers/anthropic-baseprovider.d.ts +23 -0
  131. package/dist/providers/anthropic-baseprovider.js +114 -0
  132. package/dist/providers/anthropic.d.ts +19 -43
  133. package/dist/providers/anthropic.js +81 -305
  134. package/dist/providers/azure-openai.d.ts +20 -0
  135. package/dist/providers/azure-openai.js +89 -0
  136. package/dist/providers/function-calling-provider.d.ts +64 -2
  137. package/dist/providers/function-calling-provider.js +208 -9
  138. package/dist/providers/google-ai-studio.d.ts +23 -0
  139. package/dist/providers/google-ai-studio.js +108 -0
  140. package/dist/providers/google-vertex.d.ts +47 -0
  141. package/dist/providers/google-vertex.js +205 -0
  142. package/dist/providers/huggingFace.d.ts +32 -25
  143. package/dist/providers/huggingFace.js +96 -430
  144. package/dist/providers/index.d.ts +9 -9
  145. package/dist/providers/index.js +9 -9
  146. package/dist/providers/mcp-provider.js +24 -5
  147. package/dist/providers/mistral.d.ts +42 -0
  148. package/dist/providers/mistral.js +160 -0
  149. package/dist/providers/ollama.d.ts +52 -36
  150. package/dist/providers/ollama.js +297 -519
  151. package/dist/providers/openAI.d.ts +19 -18
  152. package/dist/providers/openAI.js +76 -276
  153. package/dist/sdk/tool-extension.d.ts +181 -0
  154. package/dist/sdk/tool-extension.js +283 -0
  155. package/dist/sdk/tool-registration.d.ts +95 -0
  156. package/dist/sdk/tool-registration.js +168 -0
  157. package/dist/services/streaming/streaming-manager.js +11 -10
  158. package/dist/services/websocket/websocket-server.js +12 -11
  159. package/dist/telemetry/telemetry-service.js +8 -7
  160. package/dist/types/generate-types.d.ts +1 -0
  161. package/dist/types/mcp-types.d.ts +116 -0
  162. package/dist/types/mcp-types.js +5 -0
  163. package/dist/types/stream-types.d.ts +30 -18
  164. package/dist/types/universal-provider-options.d.ts +87 -0
  165. package/dist/types/universal-provider-options.js +53 -0
  166. package/package.json +12 -5
  167. package/dist/lib/providers/agent-enhanced-provider.d.ts +0 -93
  168. package/dist/lib/providers/agent-enhanced-provider.js +0 -605
  169. package/dist/lib/providers/amazonBedrock.d.ts +0 -28
  170. package/dist/lib/providers/amazonBedrock.js +0 -364
  171. package/dist/lib/providers/azureOpenAI.d.ts +0 -42
  172. package/dist/lib/providers/azureOpenAI.js +0 -347
  173. package/dist/lib/providers/googleAIStudio.d.ts +0 -42
  174. package/dist/lib/providers/googleAIStudio.js +0 -364
  175. package/dist/lib/providers/googleVertexAI.d.ts +0 -34
  176. package/dist/lib/providers/googleVertexAI.js +0 -547
  177. package/dist/lib/providers/mistralAI.d.ts +0 -37
  178. package/dist/lib/providers/mistralAI.js +0 -325
  179. package/dist/providers/agent-enhanced-provider.d.ts +0 -93
  180. package/dist/providers/agent-enhanced-provider.js +0 -606
  181. package/dist/providers/amazonBedrock.d.ts +0 -28
  182. package/dist/providers/amazonBedrock.js +0 -364
  183. package/dist/providers/azureOpenAI.d.ts +0 -42
  184. package/dist/providers/azureOpenAI.js +0 -348
  185. package/dist/providers/googleAIStudio.d.ts +0 -42
  186. package/dist/providers/googleAIStudio.js +0 -364
  187. package/dist/providers/googleVertexAI.d.ts +0 -34
  188. package/dist/providers/googleVertexAI.js +0 -547
  189. package/dist/providers/mistralAI.d.ts +0 -37
  190. package/dist/providers/mistralAI.js +0 -325
@@ -0,0 +1,283 @@
1
+ /**
2
+ * NeuroLink SDK Tool Extension System
3
+ * Allows developers to register custom tools that integrate with AI providers
4
+ */
5
+ import { z } from "zod";
6
+ import { tool as createAISDKTool } from "ai";
7
+ import { zodToJsonSchema } from "zod-to-json-schema";
8
+ import { logger } from "../utils/logger.js";
9
+ /**
10
+ * Converts a custom tool to Vercel AI SDK format
11
+ */
12
+ export function convertToAISDKTool(name, customTool) {
13
+ // Convert parameters to JSON schema if needed
14
+ let parametersSchema = {};
15
+ let zodSchema;
16
+ if (customTool.parameters) {
17
+ if ("parse" in customTool.parameters && "_def" in customTool.parameters) {
18
+ // It's a Zod schema
19
+ zodSchema = customTool.parameters;
20
+ parametersSchema = zodToJsonSchema(zodSchema);
21
+ }
22
+ else {
23
+ // It's already a JSON schema - convert to Zod
24
+ parametersSchema = customTool.parameters;
25
+ zodSchema = z.object({}).passthrough(); // Allow any properties
26
+ }
27
+ }
28
+ else {
29
+ zodSchema = z.object({});
30
+ }
31
+ return createAISDKTool({
32
+ description: customTool.description,
33
+ parameters: zodSchema,
34
+ execute: async (args) => {
35
+ try {
36
+ // Apply timeout if configured
37
+ if (customTool.config?.timeout) {
38
+ return await Promise.race([
39
+ customTool.execute(args),
40
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Tool ${name} timed out`)), customTool.config.timeout)),
41
+ ]);
42
+ }
43
+ return await customTool.execute(args);
44
+ }
45
+ catch (error) {
46
+ logger.error(`Tool ${name} execution failed:`, error);
47
+ throw error;
48
+ }
49
+ },
50
+ });
51
+ }
52
+ /**
53
+ * Tool registry for managing custom tools
54
+ */
55
+ export class ToolRegistry {
56
+ tools = new Map();
57
+ middleware = [];
58
+ permissions = {};
59
+ rateLimits = new Map();
60
+ /**
61
+ * Simple rate limiting check with automatic cleanup
62
+ */
63
+ checkRateLimit(name, rateLimit) {
64
+ const now = Date.now();
65
+ // Clean up expired entries to prevent memory leaks
66
+ for (const [key, limit] of this.rateLimits.entries()) {
67
+ if (limit.resetTime <= now) {
68
+ this.rateLimits.delete(key);
69
+ }
70
+ }
71
+ const limit = this.rateLimits.get(name);
72
+ if (limit && limit.resetTime > now) {
73
+ if (limit.count >= rateLimit.requests) {
74
+ throw new Error(`Tool ${name} rate limit exceeded`);
75
+ }
76
+ limit.count++;
77
+ }
78
+ else {
79
+ this.rateLimits.set(name, {
80
+ count: 1,
81
+ resetTime: now + rateLimit.window,
82
+ });
83
+ }
84
+ }
85
+ /**
86
+ * Register a custom tool
87
+ */
88
+ register(name, tool) {
89
+ if (this.tools.has(name)) {
90
+ logger.warn(`Tool ${name} already registered, overwriting`);
91
+ }
92
+ this.tools.set(name, tool);
93
+ logger.info(`Registered custom tool: ${name}`);
94
+ }
95
+ /**
96
+ * Register multiple tools at once
97
+ */
98
+ registerMany(tools) {
99
+ Object.entries(tools).forEach(([name, tool]) => {
100
+ this.register(name, tool);
101
+ });
102
+ }
103
+ /**
104
+ * Unregister a tool
105
+ */
106
+ unregister(name) {
107
+ const result = this.tools.delete(name);
108
+ if (result) {
109
+ logger.info(`Unregistered tool: ${name}`);
110
+ }
111
+ return result;
112
+ }
113
+ /**
114
+ * Get a tool by name
115
+ */
116
+ get(name) {
117
+ return this.tools.get(name);
118
+ }
119
+ /**
120
+ * Get all registered tools
121
+ */
122
+ getAll() {
123
+ return new Map(this.tools);
124
+ }
125
+ /**
126
+ * Convert all tools to AI SDK format
127
+ */
128
+ toAISDKTools() {
129
+ const aiTools = {};
130
+ for (const [name, tool] of this.tools) {
131
+ if (this.isToolAllowed(name)) {
132
+ aiTools[name] = convertToAISDKTool(name, tool);
133
+ }
134
+ }
135
+ return aiTools;
136
+ }
137
+ /**
138
+ * Add middleware
139
+ */
140
+ use(middleware) {
141
+ this.middleware.push(middleware);
142
+ }
143
+ /**
144
+ * Set permissions
145
+ */
146
+ setPermissions(permissions) {
147
+ this.permissions = permissions;
148
+ }
149
+ /**
150
+ * Check if a tool is allowed
151
+ */
152
+ isToolAllowed(name) {
153
+ // Check denylist first
154
+ if (this.permissions.denylist?.includes(name)) {
155
+ return false;
156
+ }
157
+ // Check allowlist if specified
158
+ if (this.permissions.allowlist &&
159
+ !this.permissions.allowlist.includes(name)) {
160
+ return false;
161
+ }
162
+ return true;
163
+ }
164
+ /**
165
+ * Execute a tool with middleware
166
+ */
167
+ async execute(name, args, context) {
168
+ const tool = this.tools.get(name);
169
+ if (!tool) {
170
+ throw new Error(`Tool ${name} not found`);
171
+ }
172
+ // Check permissions
173
+ if (!this.isToolAllowed(name)) {
174
+ throw new Error(`Tool ${name} is not allowed`);
175
+ }
176
+ // Check custom validator
177
+ if (this.permissions.customValidator) {
178
+ const allowed = await this.permissions.customValidator(name, args);
179
+ if (!allowed) {
180
+ throw new Error(`Tool ${name} execution denied by custom validator`);
181
+ }
182
+ }
183
+ // Check rate limit
184
+ if (tool.config?.rateLimit) {
185
+ this.checkRateLimit(name, tool.config.rateLimit);
186
+ }
187
+ // Build middleware chain
188
+ let index = 0;
189
+ const next = async () => {
190
+ if (index < this.middleware.length) {
191
+ const middleware = this.middleware[index++];
192
+ return middleware(name, args, next, context);
193
+ }
194
+ else {
195
+ // Execute the actual tool
196
+ return tool.execute(args, context);
197
+ }
198
+ };
199
+ return next();
200
+ }
201
+ }
202
+ /**
203
+ * Create a simple tool helper
204
+ */
205
+ export function createTool(config) {
206
+ return config;
207
+ }
208
+ /**
209
+ * Create an async tool helper
210
+ */
211
+ export function createAsyncTool(config) {
212
+ return config;
213
+ }
214
+ /**
215
+ * Create a batch tool that processes multiple items
216
+ */
217
+ export function createBatchTool(config) {
218
+ return {
219
+ ...config,
220
+ execute: async ({ items }, context) => {
221
+ const batchSize = config.batchSize || 10;
222
+ const results = [];
223
+ for (let i = 0; i < items.length; i += batchSize) {
224
+ const batch = items.slice(i, i + batchSize);
225
+ const batchResults = await Promise.all(batch.map((item) => config.processItem(item, context)));
226
+ results.push(...batchResults);
227
+ }
228
+ return results;
229
+ },
230
+ };
231
+ }
232
+ /**
233
+ * Tool testing utilities
234
+ */
235
+ export const TestUtils = {
236
+ /**
237
+ * Create a mock tool context
238
+ */
239
+ mockContext(overrides) {
240
+ return {
241
+ callTool: async (name, args) => {
242
+ logger.debug(`Mock tool call: ${name}`, args);
243
+ return {};
244
+ },
245
+ session: {
246
+ id: "test-session",
247
+ userId: "test-user",
248
+ provider: "test-provider",
249
+ model: "test-model",
250
+ },
251
+ logger,
252
+ ...overrides,
253
+ };
254
+ },
255
+ /**
256
+ * Test a tool with mock data
257
+ */
258
+ async testTool(tool, testCases) {
259
+ const context = TestUtils.mockContext();
260
+ const results = [];
261
+ for (const testCase of testCases) {
262
+ try {
263
+ const result = await tool.execute(testCase.input, context);
264
+ results.push({
265
+ input: testCase.input,
266
+ output: result,
267
+ success: true,
268
+ matches: testCase.expected
269
+ ? JSON.stringify(result) === JSON.stringify(testCase.expected)
270
+ : undefined,
271
+ });
272
+ }
273
+ catch (error) {
274
+ results.push({
275
+ input: testCase.input,
276
+ error: error instanceof Error ? error.message : String(error),
277
+ success: false,
278
+ });
279
+ }
280
+ }
281
+ return results;
282
+ },
283
+ };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * NeuroLink SDK Tool Registration API
3
+ * Simple interface for developers to register custom tools
4
+ */
5
+ import { z } from "zod";
6
+ import type { Tool } from "ai";
7
+ import { logger } from "../utils/logger.js";
8
+ import type { InMemoryMCPServerConfig, InMemoryToolInfo } from "../types/mcp-types.js";
9
+ /**
10
+ * Context provided to tools during execution
11
+ */
12
+ export interface ToolContext {
13
+ /**
14
+ * Current session ID
15
+ */
16
+ sessionId: string;
17
+ /**
18
+ * User ID if available
19
+ */
20
+ userId?: string;
21
+ /**
22
+ * AI provider being used
23
+ */
24
+ provider?: string;
25
+ /**
26
+ * Model being used
27
+ */
28
+ model?: string;
29
+ /**
30
+ * Call another tool
31
+ */
32
+ callTool?: (name: string, args: any) => Promise<any>;
33
+ /**
34
+ * Logger instance
35
+ */
36
+ logger: typeof logger;
37
+ }
38
+ /**
39
+ * Simple tool interface for SDK users
40
+ */
41
+ export interface SimpleTool {
42
+ /**
43
+ * Tool description that helps AI understand when to use it
44
+ */
45
+ description: string;
46
+ /**
47
+ * Parameters schema using Zod (optional)
48
+ */
49
+ parameters?: z.ZodSchema;
50
+ /**
51
+ * Tool execution function
52
+ */
53
+ execute: (args: any, context?: ToolContext) => Promise<any> | any;
54
+ /**
55
+ * Optional metadata
56
+ */
57
+ metadata?: {
58
+ category?: string;
59
+ version?: string;
60
+ author?: string;
61
+ [key: string]: any;
62
+ };
63
+ }
64
+ /**
65
+ * Converts a SimpleTool to Vercel AI SDK format
66
+ */
67
+ export declare function convertToAISDKTool(name: string, simpleTool: SimpleTool): Tool;
68
+ /**
69
+ * Converts a SimpleTool to MCP tool format
70
+ */
71
+ export declare function convertToMCPTool(simpleTool: SimpleTool): InMemoryToolInfo;
72
+ /**
73
+ * Creates an in-memory MCP server configuration from a set of tools
74
+ */
75
+ export declare function createMCPServerFromTools(serverId: string, tools: Record<string, SimpleTool>, metadata?: {
76
+ title?: string;
77
+ description?: string;
78
+ category?: string;
79
+ [key: string]: any;
80
+ }): InMemoryMCPServerConfig;
81
+ /**
82
+ * Helper to create a tool with type safety
83
+ */
84
+ export declare function createTool<TParams = any>(config: SimpleTool): SimpleTool;
85
+ /**
86
+ * Helper to create a tool with typed parameters
87
+ */
88
+ export declare function createTypedTool<TParams extends z.ZodSchema>(config: Omit<SimpleTool, "execute"> & {
89
+ parameters: TParams;
90
+ execute: (args: z.infer<TParams>, context?: ToolContext) => Promise<any> | any;
91
+ }): SimpleTool;
92
+ /**
93
+ * Validate tool configuration with detailed error messages
94
+ */
95
+ export declare function validateTool(name: string, tool: SimpleTool): void;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * NeuroLink SDK Tool Registration API
3
+ * Simple interface for developers to register custom tools
4
+ */
5
+ import { z } from "zod";
6
+ import { tool as createAISDKTool } from "ai";
7
+ import { logger } from "../utils/logger.js";
8
+ /**
9
+ * Configuration constants for tool validation
10
+ */
11
+ const envValue = parseInt(process.env.NEUROLINK_TOOL_DESCRIPTION_MAX_LENGTH || "200", 10);
12
+ const DEFAULT_DESCRIPTION_MAX_LENGTH = Number.isInteger(envValue) && envValue > 0 ? envValue : 200;
13
+ /**
14
+ * Converts a SimpleTool to Vercel AI SDK format
15
+ */
16
+ export function convertToAISDKTool(name, simpleTool) {
17
+ return createAISDKTool({
18
+ description: simpleTool.description,
19
+ parameters: simpleTool.parameters || z.object({}),
20
+ execute: async (args) => {
21
+ try {
22
+ // Create a minimal context for standalone execution
23
+ const context = {
24
+ sessionId: `tool-${name}-${Date.now()}`,
25
+ logger,
26
+ };
27
+ const result = await simpleTool.execute(args, context);
28
+ return result;
29
+ }
30
+ catch (error) {
31
+ logger.error(`Tool ${name} execution failed:`, error);
32
+ throw error;
33
+ }
34
+ },
35
+ });
36
+ }
37
+ /**
38
+ * Converts a SimpleTool to MCP tool format
39
+ */
40
+ export function convertToMCPTool(simpleTool) {
41
+ return {
42
+ description: simpleTool.description,
43
+ execute: async (params) => {
44
+ try {
45
+ const result = await simpleTool.execute(params);
46
+ return {
47
+ success: true,
48
+ data: result,
49
+ };
50
+ }
51
+ catch (error) {
52
+ const errorMessage = error instanceof Error ? error.message : String(error);
53
+ logger.error("MCP tool execution failed:", error);
54
+ return {
55
+ success: false,
56
+ error: errorMessage,
57
+ };
58
+ }
59
+ },
60
+ inputSchema: simpleTool.parameters,
61
+ isImplemented: true,
62
+ metadata: simpleTool.metadata,
63
+ };
64
+ }
65
+ /**
66
+ * Creates an in-memory MCP server configuration from a set of tools
67
+ */
68
+ export function createMCPServerFromTools(serverId, tools, metadata) {
69
+ const mcpTools = {};
70
+ for (const [name, tool] of Object.entries(tools)) {
71
+ mcpTools[name] = convertToMCPTool(tool);
72
+ }
73
+ return {
74
+ server: {
75
+ title: metadata?.title || serverId,
76
+ description: metadata?.description,
77
+ tools: mcpTools,
78
+ },
79
+ category: metadata?.category,
80
+ metadata: metadata || {},
81
+ };
82
+ }
83
+ /**
84
+ * Helper to create a tool with type safety
85
+ */
86
+ export function createTool(config) {
87
+ return config;
88
+ }
89
+ /**
90
+ * Helper to create a tool with typed parameters
91
+ */
92
+ export function createTypedTool(config) {
93
+ return config;
94
+ }
95
+ /**
96
+ * Validate tool description length
97
+ */
98
+ function validateDescriptionLength(name, description) {
99
+ const maxDescriptionLength = Number.isInteger(DEFAULT_DESCRIPTION_MAX_LENGTH) &&
100
+ DEFAULT_DESCRIPTION_MAX_LENGTH > 0
101
+ ? DEFAULT_DESCRIPTION_MAX_LENGTH
102
+ : 200;
103
+ if (description.length > maxDescriptionLength) {
104
+ throw new Error(`Tool '${name}' description should be concise (max ${maxDescriptionLength} characters). ` +
105
+ `Current length: ${description.length}. ` +
106
+ `Consider shortening: "${description.substring(0, 50)}..."`);
107
+ }
108
+ }
109
+ /**
110
+ * Validate tool configuration with detailed error messages
111
+ */
112
+ export function validateTool(name, tool) {
113
+ // Validate tool name
114
+ if (!name || typeof name !== "string" || name.trim() === "") {
115
+ throw new Error(`Invalid tool name: must be a non-empty string. Received: ${name}`);
116
+ }
117
+ // Validate tool name format (alphanumeric, hyphens, underscores only)
118
+ const validNamePattern = /^[a-zA-Z0-9_-]+$/;
119
+ if (!validNamePattern.test(name)) {
120
+ throw new Error(`Invalid tool name format: '${name}'. Tool names must contain only alphanumeric characters, hyphens, and underscores. ` +
121
+ `Examples: 'calculate-tax', 'get_weather', 'sendEmail123'`);
122
+ }
123
+ // Validate tool object
124
+ if (!tool || typeof tool !== "object") {
125
+ throw new Error(`Tool '${name}' must be an object with description and execute properties. Received: ${typeof tool}`);
126
+ }
127
+ // Validate description
128
+ if (!tool.description ||
129
+ typeof tool.description !== "string" ||
130
+ tool.description.trim() === "") {
131
+ throw new Error(`Tool '${name}' must have a non-empty description string. ` +
132
+ `Example: { description: "Calculates mathematical expressions", execute: async (params) => {...} }`);
133
+ }
134
+ // Validate execute function with signature guidance
135
+ if (typeof tool.execute !== "function") {
136
+ throw new Error(`Tool '${name}' must have an execute function. ` +
137
+ `Expected signature: async (params?: any) => Promise<any>. ` +
138
+ `Received: ${typeof tool.execute}. ` +
139
+ `Example: { execute: async (params) => { return { success: true, data: result }; } }`);
140
+ }
141
+ // Validate parameters schema if provided - support both Zod and custom schemas
142
+ if (tool.parameters) {
143
+ if (typeof tool.parameters !== "object") {
144
+ throw new Error(`Tool '${name}' parameters must be an object. ` +
145
+ `Received: ${typeof tool.parameters}`);
146
+ }
147
+ // Check for common schema validation methods (Zod uses 'parse', others might use 'validate')
148
+ const params = tool.parameters;
149
+ const hasValidationMethod = typeof params.parse === "function" ||
150
+ typeof params.validate === "function" ||
151
+ "_def" in params; // Zod schemas have _def property
152
+ if (!hasValidationMethod) {
153
+ const errorMessage = typeof params.parse === "function" || "_def" in params
154
+ ? `Tool '${name}' has a Zod-like schema but validation failed. Ensure it's a valid Zod schema: z.object({ ... })`
155
+ : typeof params.validate === "function"
156
+ ? `Tool '${name}' has a validate method but it may not be callable. Ensure: { parameters: { validate: (data) => { ... } } }`
157
+ : `Tool '${name}' parameters must be a schema object with validation. ` +
158
+ `Supported formats:\n` +
159
+ `• Zod schema: { parameters: z.object({ value: z.string() }) }\n` +
160
+ `• Custom schema: { parameters: { validate: (data) => { ... } } }\n` +
161
+ `• Custom schema: { parameters: { parse: (data) => { ... } } }`;
162
+ throw new Error(errorMessage);
163
+ }
164
+ }
165
+ // Validate description length for better UX
166
+ validateDescriptionLength(name, tool.description);
167
+ }
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from "events";
2
2
  import { randomUUID } from "crypto";
3
+ import { logger } from "../../utils/logger.js";
3
4
  export class StreamingManager extends EventEmitter {
4
5
  activeSessions = new Map();
5
6
  streamingPools = new Map();
@@ -40,7 +41,7 @@ export class StreamingManager extends EventEmitter {
40
41
  };
41
42
  this.activeSessions.set(sessionId, session);
42
43
  this.updateGlobalMetrics();
43
- console.log(`[Streaming Manager] Created session ${sessionId} for provider ${config.provider}`);
44
+ logger.debug(`[Streaming Manager] Created session ${sessionId} for provider ${config.provider}`);
44
45
  this.emit("session-created", session);
45
46
  return session;
46
47
  }
@@ -52,7 +53,7 @@ export class StreamingManager extends EventEmitter {
52
53
  session.status = "terminated";
53
54
  this.activeSessions.delete(sessionId);
54
55
  this.updateGlobalMetrics();
55
- console.log(`[Streaming Manager] Terminated session ${sessionId}`);
56
+ logger.debug(`[Streaming Manager] Terminated session ${sessionId}`);
56
57
  this.emit("session-terminated", session);
57
58
  }
58
59
  async pauseStreamingSession(sessionId) {
@@ -62,7 +63,7 @@ export class StreamingManager extends EventEmitter {
62
63
  }
63
64
  if (session.status === "active") {
64
65
  session.status = "paused";
65
- console.log(`[Streaming Manager] Paused session ${sessionId}`);
66
+ logger.debug(`[Streaming Manager] Paused session ${sessionId}`);
66
67
  this.emit("session-paused", session);
67
68
  }
68
69
  }
@@ -74,7 +75,7 @@ export class StreamingManager extends EventEmitter {
74
75
  if (session.status === "paused") {
75
76
  session.status = "active";
76
77
  session.lastActivity = Date.now();
77
- console.log(`[Streaming Manager] Resumed session ${sessionId}`);
78
+ logger.debug(`[Streaming Manager] Resumed session ${sessionId}`);
78
79
  this.emit("session-resumed", session);
79
80
  }
80
81
  }
@@ -97,7 +98,7 @@ export class StreamingManager extends EventEmitter {
97
98
  session.config.bufferSize = Math.max(session.config.bufferSize * 0.8, 1024);
98
99
  session.config.streamingMode = "real-time";
99
100
  }
100
- console.log(`[Streaming Manager] Optimized session ${sessionId}: latency=${currentLatency}ms, mode=${session.config.streamingMode}`);
101
+ logger.debug(`[Streaming Manager] Optimized session ${sessionId}: latency=${currentLatency}ms, mode=${session.config.streamingMode}`);
101
102
  }
102
103
  async enableStreamingCompression(sessionId) {
103
104
  const session = this.activeSessions.get(sessionId);
@@ -105,7 +106,7 @@ export class StreamingManager extends EventEmitter {
105
106
  return;
106
107
  }
107
108
  session.config.compressionEnabled = true;
108
- console.log(`[Streaming Manager] Enabled compression for session ${sessionId}`);
109
+ logger.debug(`[Streaming Manager] Enabled compression for session ${sessionId}`);
109
110
  }
110
111
  async configureStreamingBuffering(sessionId, bufferConfig) {
111
112
  const session = this.activeSessions.get(sessionId);
@@ -114,7 +115,7 @@ export class StreamingManager extends EventEmitter {
114
115
  }
115
116
  session.config.bufferSize = bufferConfig.maxSize;
116
117
  session.config.maxChunkSize = Math.min(session.config.maxChunkSize, bufferConfig.flushThreshold);
117
- console.log(`[Streaming Manager] Updated buffer config for session ${sessionId}:`, bufferConfig);
118
+ logger.debug(`[Streaming Manager] Updated buffer config for session ${sessionId}:`, bufferConfig);
118
119
  }
119
120
  // Multi-Stream Coordination
120
121
  async createStreamingPool(poolId, config) {
@@ -126,7 +127,7 @@ export class StreamingManager extends EventEmitter {
126
127
  loadBalancer: config.loadBalancing,
127
128
  };
128
129
  this.streamingPools.set(poolId, pool);
129
- console.log(`[Streaming Manager] Created pool ${poolId} with max ${config.maxConcurrentSessions} sessions`);
130
+ logger.debug(`[Streaming Manager] Created pool ${poolId} with max ${config.maxConcurrentSessions} sessions`);
130
131
  }
131
132
  async balanceStreamingLoad(poolId) {
132
133
  const pool = this.streamingPools.get(poolId);
@@ -160,7 +161,7 @@ export class StreamingManager extends EventEmitter {
160
161
  const newMaxSessions = Math.max(1, Math.floor(pool.maxSessions * scale));
161
162
  pool.maxSessions = newMaxSessions;
162
163
  pool.config.maxConcurrentSessions = newMaxSessions;
163
- console.log(`[Streaming Manager] Scaled pool ${poolId} to ${newMaxSessions} max sessions (${scale}x)`);
164
+ logger.debug(`[Streaming Manager] Scaled pool ${poolId} to ${newMaxSessions} max sessions (${scale}x)`);
164
165
  }
165
166
  // Performance Monitoring
166
167
  getStreamingMetrics(sessionId) {
@@ -217,7 +218,7 @@ export class StreamingManager extends EventEmitter {
217
218
  this.healthCheckInterval = setInterval(() => {
218
219
  const health = this.getStreamingHealthStatus();
219
220
  if (health.status !== "healthy") {
220
- console.warn("[Streaming Manager] Health check:", health);
221
+ logger.debug("[Streaming Manager] Health check:", health);
221
222
  this.emit("health-warning", health);
222
223
  }
223
224
  }, 30000); // Check every 30 seconds