@juspay/neurolink 5.2.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 (37) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/dist/cli/factories/command-factory.js +6 -5
  3. package/dist/core/base-provider.js +24 -13
  4. package/dist/core/constants.d.ts +1 -0
  5. package/dist/core/constants.js +1 -0
  6. package/dist/lib/core/base-provider.js +24 -13
  7. package/dist/lib/core/constants.d.ts +1 -0
  8. package/dist/lib/core/constants.js +1 -0
  9. package/dist/lib/mcp/client.d.ts +1 -0
  10. package/dist/lib/mcp/client.js +1 -0
  11. package/dist/lib/mcp/context-manager.d.ts +1 -0
  12. package/dist/lib/mcp/context-manager.js +8 -4
  13. package/dist/lib/mcp/function-calling.d.ts +13 -0
  14. package/dist/lib/mcp/function-calling.js +133 -34
  15. package/dist/lib/mcp/neurolink-mcp-client.d.ts +1 -0
  16. package/dist/lib/mcp/neurolink-mcp-client.js +21 -5
  17. package/dist/lib/providers/function-calling-provider.d.ts +64 -2
  18. package/dist/lib/providers/function-calling-provider.js +208 -9
  19. package/dist/lib/providers/mcp-provider.js +20 -5
  20. package/dist/lib/services/streaming/streaming-manager.js +11 -10
  21. package/dist/lib/services/websocket/websocket-server.js +12 -11
  22. package/dist/lib/telemetry/telemetry-service.js +8 -7
  23. package/dist/mcp/client.d.ts +1 -0
  24. package/dist/mcp/client.js +1 -0
  25. package/dist/mcp/context-manager.d.ts +1 -0
  26. package/dist/mcp/context-manager.js +8 -4
  27. package/dist/mcp/function-calling.d.ts +13 -0
  28. package/dist/mcp/function-calling.js +133 -34
  29. package/dist/mcp/neurolink-mcp-client.d.ts +1 -0
  30. package/dist/mcp/neurolink-mcp-client.js +21 -5
  31. package/dist/providers/function-calling-provider.d.ts +64 -2
  32. package/dist/providers/function-calling-provider.js +208 -9
  33. package/dist/providers/mcp-provider.js +20 -5
  34. package/dist/services/streaming/streaming-manager.js +11 -10
  35. package/dist/services/websocket/websocket-server.js +12 -11
  36. package/dist/telemetry/telemetry-service.js +8 -7
  37. package/package.json +12 -10
package/CHANGELOG.md CHANGED
@@ -1,9 +1,15 @@
1
- # [5.2.0](https://github.com/juspay/neurolink/compare/v5.1.0...v5.2.0) (2025-07-22)
1
+ # [5.3.0](https://github.com/juspay/neurolink/compare/v5.2.0...v5.3.0) (2025-07-23)
2
+
3
+
4
+ ### Features
5
+
6
+ * **mcp:** enhance MCP integration with comprehensive testing infrastructure and tool ecosystem improvements ([a38d845](https://github.com/juspay/neurolink/commit/a38d845032133eee098de10b966cbdd3a329fdfd))
2
7
 
8
+ # [5.2.0](https://github.com/juspay/neurolink/compare/v5.1.0...v5.2.0) (2025-07-22)
3
9
 
4
10
  ### Features
5
11
 
6
- * **core:** implement comprehensive factory pattern architecture with full MCP integration and provider unification ([b13963a](https://github.com/juspay/neurolink/commit/b13963aaf6f95233be8b1e9bdc69ce1b65604cdf))
12
+ - **core:** implement comprehensive factory pattern architecture with full MCP integration and provider unification ([b13963a](https://github.com/juspay/neurolink/commit/b13963aaf6f95233be8b1e9bdc69ce1b65604cdf))
7
13
 
8
14
  # [5.1.0](https://github.com/juspay/neurolink/compare/v5.0.0...v5.1.0) (2025-07-13)
9
15
 
@@ -1,6 +1,7 @@
1
1
  import { NeuroLink } from "../../lib/neurolink.js";
2
2
  import ora from "ora";
3
3
  import chalk from "chalk";
4
+ import { logger } from "../../lib/utils/logger.js";
4
5
  /**
5
6
  * CLI Command Factory for generate commands
6
7
  */
@@ -161,14 +162,14 @@ export class CLICommandFactory {
161
162
  console.log(result.content);
162
163
  }
163
164
  if (argv.debug) {
164
- console.log("\n" + chalk.yellow("Debug Information:"));
165
- console.log("Provider:", result.provider);
166
- console.log("Model:", result.model);
165
+ logger.debug("\n" + chalk.yellow("Debug Information:"));
166
+ logger.debug("Provider:", result.provider);
167
+ logger.debug("Model:", result.model);
167
168
  if (result.analytics) {
168
- console.log("Analytics:", JSON.stringify(result.analytics, null, 2));
169
+ logger.debug("Analytics:", JSON.stringify(result.analytics, null, 2));
169
170
  }
170
171
  if (result.evaluation) {
171
- console.log("Evaluation:", JSON.stringify(result.evaluation, null, 2));
172
+ logger.debug("Evaluation:", JSON.stringify(result.evaluation, null, 2));
172
173
  }
173
174
  }
174
175
  // Exit successfully
@@ -4,6 +4,18 @@ import { directAgentTools } from "../agent/direct-tools.js";
4
4
  // import { evaluateResponse } from "../core/evaluation.js";
5
5
  // import { getAvailableFunctionTools } from "../mcp/function-calling.js";
6
6
  // Analytics helper will be dynamically imported when needed
7
+ /**
8
+ * Validates if a result contains a valid toolsObject structure
9
+ * @param result - The result object to validate
10
+ * @returns true if the result contains a valid toolsObject, false otherwise
11
+ */
12
+ function isValidToolsObject(result) {
13
+ return (result &&
14
+ typeof result === "object" &&
15
+ result.toolsObject &&
16
+ typeof result.toolsObject === "object" &&
17
+ Object.keys(result.toolsObject).length > 0);
18
+ }
7
19
  /**
8
20
  * Abstract base class for all AI providers
9
21
  * Tools are integrated as first-class citizens - always available by default
@@ -170,16 +182,16 @@ export class BaseProvider {
170
182
  const tools = {
171
183
  ...this.directTools, // Always include direct tools
172
184
  };
173
- logger.info(`[BaseProvider] getAllTools called, SDK available: ${!!this.sdk}, type: ${typeof this.sdk}`);
174
- console.log(`[BaseProvider] Direct tools: ${Object.keys(this.directTools).join(", ")}`);
185
+ logger.debug(`[BaseProvider] getAllTools called, SDK available: ${!!this.sdk}, type: ${typeof this.sdk}`);
186
+ logger.debug(`[BaseProvider] Direct tools: ${Object.keys(this.directTools).join(", ")}`);
175
187
  // Add custom tools from SDK if available
176
- console.log(`[BaseProvider] Checking SDK: ${!!this.sdk}, has getInMemoryServers: ${this.sdk && typeof this.sdk.getInMemoryServers}`);
188
+ logger.debug(`[BaseProvider] Checking SDK: ${!!this.sdk}, has getInMemoryServers: ${this.sdk && typeof this.sdk.getInMemoryServers}`);
177
189
  if (this.sdk &&
178
190
  typeof this.sdk.getInMemoryServers === "function") {
179
- console.log(`[BaseProvider] SDK check passed, loading custom tools`);
191
+ logger.debug(`[BaseProvider] SDK check passed, loading custom tools`);
180
192
  try {
181
193
  const inMemoryServers = this.sdk.getInMemoryServers();
182
- console.log(`[BaseProvider] Got servers:`, inMemoryServers.size);
194
+ logger.debug(`[BaseProvider] Got servers:`, inMemoryServers.size);
183
195
  logger.debug(`[BaseProvider] Loading custom tools from SDK, found ${inMemoryServers.size} servers`);
184
196
  if (inMemoryServers && inMemoryServers.size > 0) {
185
197
  // Convert in-memory server tools to AI SDK format
@@ -192,7 +204,7 @@ export class BaseProvider {
192
204
  : Object.entries(server.tools || {});
193
205
  for (const [toolName, toolInfo] of toolEntries) {
194
206
  if (toolInfo && typeof toolInfo.execute === "function") {
195
- console.log(`[BaseProvider] Converting custom tool: ${toolName}`);
207
+ logger.debug(`[BaseProvider] Converting custom tool: ${toolName}`);
196
208
  // Convert to AI SDK tool format
197
209
  const { tool: createAISDKTool } = await import("ai");
198
210
  const { z } = await import("zod");
@@ -233,12 +245,11 @@ export class BaseProvider {
233
245
  try {
234
246
  const { getAvailableFunctionTools } = await import("../mcp/function-calling.js");
235
247
  const result = await getAvailableFunctionTools();
236
- if (result.tools && result.tools.length > 0) {
237
- this.mcpTools = {};
238
- for (const tool of result.tools) {
239
- const toolName = tool.name || "unknown";
240
- this.mcpTools[toolName] = tool;
241
- }
248
+ if (isValidToolsObject(result)) {
249
+ this.mcpTools = result.toolsObject;
250
+ }
251
+ else {
252
+ logger.debug(`Invalid or empty toolsObject for ${this.providerName}: Expected an object with at least one key, but got ${typeof result.toolsObject} with ${result.toolsObject ? Object.keys(result.toolsObject).length : 0} keys. Full result:`, result);
242
253
  }
243
254
  }
244
255
  catch (error) {
@@ -250,7 +261,7 @@ export class BaseProvider {
250
261
  if (this.mcpTools) {
251
262
  Object.assign(tools, this.mcpTools);
252
263
  }
253
- console.log(`[BaseProvider] getAllTools returning tools: ${Object.keys(tools).join(", ")}`);
264
+ logger.debug(`[BaseProvider] getAllTools returning tools: ${Object.keys(tools).join(", ")}`);
254
265
  return tools;
255
266
  }
256
267
  /**
@@ -5,6 +5,7 @@
5
5
  export declare const DEFAULT_MAX_TOKENS = 8192;
6
6
  export declare const DEFAULT_TEMPERATURE = 0.7;
7
7
  export declare const DEFAULT_TIMEOUT = 30000;
8
+ export declare const DEFAULT_MAX_STEPS = 5;
8
9
  export declare const DEFAULT_EVALUATION_MAX_TOKENS = 500;
9
10
  export declare const DEFAULT_ANALYSIS_MAX_TOKENS = 800;
10
11
  export declare const DEFAULT_DOCUMENTATION_MAX_TOKENS = 12000;
@@ -6,6 +6,7 @@
6
6
  export const DEFAULT_MAX_TOKENS = 8192; // Changed from 10000 to fix Anthropic error
7
7
  export const DEFAULT_TEMPERATURE = 0.7;
8
8
  export const DEFAULT_TIMEOUT = 30000;
9
+ export const DEFAULT_MAX_STEPS = 5; // Default multi-turn tool execution steps
9
10
  // Specialized Use Case Defaults
10
11
  export const DEFAULT_EVALUATION_MAX_TOKENS = 500; // Keep evaluation fast
11
12
  export const DEFAULT_ANALYSIS_MAX_TOKENS = 800; // For analysis tools
@@ -4,6 +4,18 @@ import { directAgentTools } from "../agent/direct-tools.js";
4
4
  // import { evaluateResponse } from "../core/evaluation.js";
5
5
  // import { getAvailableFunctionTools } from "../mcp/function-calling.js";
6
6
  // Analytics helper will be dynamically imported when needed
7
+ /**
8
+ * Validates if a result contains a valid toolsObject structure
9
+ * @param result - The result object to validate
10
+ * @returns true if the result contains a valid toolsObject, false otherwise
11
+ */
12
+ function isValidToolsObject(result) {
13
+ return (result &&
14
+ typeof result === "object" &&
15
+ result.toolsObject &&
16
+ typeof result.toolsObject === "object" &&
17
+ Object.keys(result.toolsObject).length > 0);
18
+ }
7
19
  /**
8
20
  * Abstract base class for all AI providers
9
21
  * Tools are integrated as first-class citizens - always available by default
@@ -170,16 +182,16 @@ export class BaseProvider {
170
182
  const tools = {
171
183
  ...this.directTools, // Always include direct tools
172
184
  };
173
- logger.info(`[BaseProvider] getAllTools called, SDK available: ${!!this.sdk}, type: ${typeof this.sdk}`);
174
- console.log(`[BaseProvider] Direct tools: ${Object.keys(this.directTools).join(", ")}`);
185
+ logger.debug(`[BaseProvider] getAllTools called, SDK available: ${!!this.sdk}, type: ${typeof this.sdk}`);
186
+ logger.debug(`[BaseProvider] Direct tools: ${Object.keys(this.directTools).join(", ")}`);
175
187
  // Add custom tools from SDK if available
176
- console.log(`[BaseProvider] Checking SDK: ${!!this.sdk}, has getInMemoryServers: ${this.sdk && typeof this.sdk.getInMemoryServers}`);
188
+ logger.debug(`[BaseProvider] Checking SDK: ${!!this.sdk}, has getInMemoryServers: ${this.sdk && typeof this.sdk.getInMemoryServers}`);
177
189
  if (this.sdk &&
178
190
  typeof this.sdk.getInMemoryServers === "function") {
179
- console.log(`[BaseProvider] SDK check passed, loading custom tools`);
191
+ logger.debug(`[BaseProvider] SDK check passed, loading custom tools`);
180
192
  try {
181
193
  const inMemoryServers = this.sdk.getInMemoryServers();
182
- console.log(`[BaseProvider] Got servers:`, inMemoryServers.size);
194
+ logger.debug(`[BaseProvider] Got servers:`, inMemoryServers.size);
183
195
  logger.debug(`[BaseProvider] Loading custom tools from SDK, found ${inMemoryServers.size} servers`);
184
196
  if (inMemoryServers && inMemoryServers.size > 0) {
185
197
  // Convert in-memory server tools to AI SDK format
@@ -192,7 +204,7 @@ export class BaseProvider {
192
204
  : Object.entries(server.tools || {});
193
205
  for (const [toolName, toolInfo] of toolEntries) {
194
206
  if (toolInfo && typeof toolInfo.execute === "function") {
195
- console.log(`[BaseProvider] Converting custom tool: ${toolName}`);
207
+ logger.debug(`[BaseProvider] Converting custom tool: ${toolName}`);
196
208
  // Convert to AI SDK tool format
197
209
  const { tool: createAISDKTool } = await import("ai");
198
210
  const { z } = await import("zod");
@@ -233,12 +245,11 @@ export class BaseProvider {
233
245
  try {
234
246
  const { getAvailableFunctionTools } = await import("../mcp/function-calling.js");
235
247
  const result = await getAvailableFunctionTools();
236
- if (result.tools && result.tools.length > 0) {
237
- this.mcpTools = {};
238
- for (const tool of result.tools) {
239
- const toolName = tool.name || "unknown";
240
- this.mcpTools[toolName] = tool;
241
- }
248
+ if (isValidToolsObject(result)) {
249
+ this.mcpTools = result.toolsObject;
250
+ }
251
+ else {
252
+ logger.debug(`Invalid or empty toolsObject for ${this.providerName}: Expected an object with at least one key, but got ${typeof result.toolsObject} with ${result.toolsObject ? Object.keys(result.toolsObject).length : 0} keys. Full result:`, result);
242
253
  }
243
254
  }
244
255
  catch (error) {
@@ -250,7 +261,7 @@ export class BaseProvider {
250
261
  if (this.mcpTools) {
251
262
  Object.assign(tools, this.mcpTools);
252
263
  }
253
- console.log(`[BaseProvider] getAllTools returning tools: ${Object.keys(tools).join(", ")}`);
264
+ logger.debug(`[BaseProvider] getAllTools returning tools: ${Object.keys(tools).join(", ")}`);
254
265
  return tools;
255
266
  }
256
267
  /**
@@ -5,6 +5,7 @@
5
5
  export declare const DEFAULT_MAX_TOKENS = 8192;
6
6
  export declare const DEFAULT_TEMPERATURE = 0.7;
7
7
  export declare const DEFAULT_TIMEOUT = 30000;
8
+ export declare const DEFAULT_MAX_STEPS = 5;
8
9
  export declare const DEFAULT_EVALUATION_MAX_TOKENS = 500;
9
10
  export declare const DEFAULT_ANALYSIS_MAX_TOKENS = 800;
10
11
  export declare const DEFAULT_DOCUMENTATION_MAX_TOKENS = 12000;
@@ -6,6 +6,7 @@
6
6
  export const DEFAULT_MAX_TOKENS = 8192; // Changed from 10000 to fix Anthropic error
7
7
  export const DEFAULT_TEMPERATURE = 0.7;
8
8
  export const DEFAULT_TIMEOUT = 30000;
9
+ export const DEFAULT_MAX_STEPS = 5; // Default multi-turn tool execution steps
9
10
  // Specialized Use Case Defaults
10
11
  export const DEFAULT_EVALUATION_MAX_TOKENS = 500; // Keep evaluation fast
11
12
  export const DEFAULT_ANALYSIS_MAX_TOKENS = 800; // For analysis tools
@@ -33,6 +33,7 @@ export declare class NeuroLinkMCPClient extends EventEmitter {
33
33
  * Get all registered tools
34
34
  */
35
35
  getTools(): Record<string, {
36
+ name: string;
36
37
  description?: string;
37
38
  inputSchema?: unknown;
38
39
  }>;
@@ -130,6 +130,7 @@ export class NeuroLinkMCPClient extends EventEmitter {
130
130
  const tools = {};
131
131
  for (const [name, tool] of this.tools) {
132
132
  tools[name] = {
133
+ name: name, // Include the tool name as a property
133
134
  description: tool.description,
134
135
  inputSchema: tool.inputSchema,
135
136
  };
@@ -29,6 +29,7 @@ export interface ContextRequest {
29
29
  export declare class ContextManager {
30
30
  private sessionCounter;
31
31
  private activeContexts;
32
+ private static cachedLogger;
32
33
  /**
33
34
  * Create a new execution context with rich information
34
35
  *
@@ -10,6 +10,7 @@
10
10
  export class ContextManager {
11
11
  sessionCounter = 0;
12
12
  activeContexts = new Map();
13
+ static cachedLogger = null;
13
14
  /**
14
15
  * Create a new execution context with rich information
15
16
  *
@@ -64,6 +65,7 @@ export class ContextManager {
64
65
  },
65
66
  path: {
66
67
  join: (...paths) => {
68
+ // Use dynamic require for synchronous path operations
67
69
  const path = require("path");
68
70
  return path.join(...paths);
69
71
  },
@@ -85,11 +87,13 @@ export class ContextManager {
85
87
  },
86
88
  },
87
89
  grantedPermissions: request.permissions || [],
88
- log: (level, message, data) => {
89
- // Use logger if available, otherwise console
90
+ log: async (level, message, data) => {
91
+ // Use cached logger if available, otherwise import and cache
90
92
  try {
91
- const { logger } = require("../utils/logger.js");
92
- logger[level](message, data);
93
+ if (!ContextManager.cachedLogger) {
94
+ ContextManager.cachedLogger = await import("../utils/logger.js").then(({ logger }) => logger);
95
+ }
96
+ ContextManager.cachedLogger[level](message, data);
93
97
  }
94
98
  catch {
95
99
  console[level === "debug" ? "log" : level](message, data);
@@ -14,6 +14,7 @@ export declare function mcpToolToAISDKTool(tool: NeuroLinkMCPTool, serverId: str
14
14
  */
15
15
  export declare function getAvailableFunctionTools(): Promise<{
16
16
  tools: Tool[];
17
+ toolsObject: Record<string, Tool>;
17
18
  toolMap: Map<string, {
18
19
  serverId: string;
19
20
  toolName: string;
@@ -49,3 +50,15 @@ export declare function getFunctionToolsForCategory(category?: string, maxTools?
49
50
  * Check if function calling is available
50
51
  */
51
52
  export declare function isFunctionCallingAvailable(): Promise<boolean>;
53
+ /**
54
+ * Utility function to create a named tool object for debugging
55
+ */
56
+ export declare function createNamedTool(name: string, toolDef: any): any & {
57
+ name: string;
58
+ };
59
+ /**
60
+ * Get tools with proper name properties for debugging
61
+ */
62
+ export declare function getToolsWithNames(): Promise<Record<string, any & {
63
+ name: string;
64
+ }>>;
@@ -8,6 +8,100 @@ import { z } from "zod";
8
8
  import { unifiedRegistry } from "./unified-registry.js";
9
9
  import { createExecutionContext } from "./context-manager.js";
10
10
  import { mcpLogger } from "./logging.js";
11
+ /**
12
+ * Parses neurolink-specific function name patterns to extract the server ID and tool name.
13
+ *
14
+ * @param {string[]} parts - An array of strings representing parts of a function name,
15
+ * typically obtained by splitting the function name on underscores.
16
+ * @returns {{ serverId: string; toolName: string } | null} An object containing the `serverId`
17
+ * and `toolName` if the input matches a neurolink-specific pattern, or `null` if no match is found.
18
+ *
19
+ * @example
20
+ * // Returns { serverId: "neurolink_ai_core", toolName: "generate" }
21
+ * parseNeuroLinkPattern(["neurolink", "ai", "core", "generate"]);
22
+ *
23
+ * @example
24
+ * // Returns { serverId: "neurolink_utility", toolName: "format_number" }
25
+ * parseNeuroLinkPattern(["neurolink", "utility", "format_number"]);
26
+ *
27
+ * @example
28
+ * // Returns null
29
+ * parseNeuroLinkPattern(["other", "pattern"]);
30
+ */
31
+ function parseNeuroLinkPattern(parts) {
32
+ if (parts.length >= 3 &&
33
+ parts[0] === "neurolink" &&
34
+ (parts[1] === "ai" || parts[1] === "utility")) {
35
+ // neurolink_ai_core_generate -> serverId: "neurolink_ai_core", toolName: "generate"
36
+ // neurolink_utility_format_number -> serverId: "neurolink_utility", toolName: "format_number"
37
+ if (parts[1] === "ai" && parts[2] === "core") {
38
+ return {
39
+ serverId: "neurolink_ai_core",
40
+ toolName: parts.slice(3).join("_"),
41
+ };
42
+ }
43
+ else if (parts[1] === "utility") {
44
+ return {
45
+ serverId: "neurolink_utility",
46
+ toolName: parts.slice(2).join("_"),
47
+ };
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Parse underscore-separated function name format
54
+ */
55
+ function parseUnderscoreFormat(functionName) {
56
+ const parts = functionName.split("_");
57
+ if (parts.length >= 2) {
58
+ // Try neurolink-specific patterns first
59
+ const neurolinkResult = parseNeuroLinkPattern(parts);
60
+ if (neurolinkResult) {
61
+ return neurolinkResult;
62
+ }
63
+ // Default underscore parsing
64
+ return {
65
+ serverId: parts[0],
66
+ toolName: parts.slice(1).join("_"),
67
+ };
68
+ }
69
+ return null;
70
+ }
71
+ /**
72
+ * Parse dot-separated function name format (legacy support)
73
+ */
74
+ function parseDotFormat(functionName) {
75
+ const parts = functionName.split(".");
76
+ if (parts.length >= 2) {
77
+ return {
78
+ serverId: parts[0],
79
+ toolName: parts.slice(1).join("."),
80
+ };
81
+ }
82
+ return null;
83
+ }
84
+ /**
85
+ * Parse function name to extract server ID and tool name
86
+ * Handles various naming patterns including neurolink server patterns
87
+ */
88
+ function parseFunctionName(functionName) {
89
+ // Try underscore format first (most common)
90
+ const underscoreResult = parseUnderscoreFormat(functionName);
91
+ if (underscoreResult) {
92
+ return underscoreResult;
93
+ }
94
+ // Fallback to dot format for backward compatibility
95
+ const dotResult = parseDotFormat(functionName);
96
+ if (dotResult) {
97
+ return dotResult;
98
+ }
99
+ // Final fallback - return as-is with unknown server
100
+ return {
101
+ serverId: "unknown",
102
+ toolName: functionName,
103
+ };
104
+ }
11
105
  /**
12
106
  * Convert MCP tool to AI SDK function definition
13
107
  */
@@ -55,6 +149,7 @@ export function mcpToolToAISDKTool(tool, serverId) {
55
149
  export async function getAvailableFunctionTools() {
56
150
  const functionTag = "getAvailableFunctionTools";
57
151
  const tools = [];
152
+ const toolsObject = {};
58
153
  const toolMap = new Map();
59
154
  try {
60
155
  // Add overall timeout for the entire function
@@ -281,6 +376,8 @@ export async function getAvailableFunctionTools() {
281
376
  },
282
377
  });
283
378
  tools.push(aiTool);
379
+ // Store tool with proper name association
380
+ toolsObject[functionName] = aiTool;
284
381
  // Store mapping for execution - CRITICAL: Use sanitized functionName as key
285
382
  toolMap.set(functionName, {
286
383
  serverId: typeof toolInfo.serverId === "string"
@@ -302,7 +399,7 @@ export async function getAvailableFunctionTools() {
302
399
  if (overallTimeoutId) {
303
400
  clearTimeout(overallTimeoutId);
304
401
  }
305
- return { tools, toolMap };
402
+ return { tools, toolsObject, toolMap };
306
403
  }
307
404
  catch (error) {
308
405
  if (overallTimeoutId) {
@@ -315,7 +412,7 @@ export async function getAvailableFunctionTools() {
315
412
  }
316
413
  catch (error) {
317
414
  mcpLogger.error(`[${functionTag}] Error getting function tools:`, error);
318
- return { tools: [], toolMap: new Map() };
415
+ return { tools: [], toolsObject: {}, toolMap: new Map() };
319
416
  }
320
417
  }
321
418
  /**
@@ -345,38 +442,12 @@ export async function executeFunctionCall(functionName, parameters, context) {
345
442
  });
346
443
  }
347
444
  // Parse server and tool name from function name
348
- // First try underscore format (sanitized), then fallback to dot format
349
- let parts = functionName.split("_");
350
- let serverId = "unknown";
351
- let toolName = functionName;
352
- if (parts.length >= 2) {
353
- // Handle underscore format (sanitized names)
354
- const firstPart = parts[0];
355
- if (firstPart && typeof firstPart === "string" && firstPart.length > 0) {
356
- serverId = firstPart;
357
- }
358
- else {
359
- serverId = "unknown";
360
- }
361
- toolName = parts.slice(1).join("_"); // Rejoin in case tool name had underscores
362
- }
363
- else {
364
- // Fallback to dot format for backward compatibility
365
- parts = functionName.split(".");
366
- if (parts.length === 2) {
367
- const parsedServerId = parts[0];
368
- const parsedToolName = parts[1];
369
- if (parsedServerId && parsedToolName) {
370
- serverId = parsedServerId;
371
- toolName = parsedToolName;
372
- }
373
- }
374
- else {
375
- // Can't parse - try executing as-is through unified registry
376
- mcpLogger.debug(`[${functionTag}] Cannot parse function name format: ${functionName}, trying unified registry`);
377
- const result = await unifiedRegistry.executeTool(functionName, actualParameters, finalContext);
378
- return result;
379
- }
445
+ const { serverId, toolName } = parseFunctionName(functionName);
446
+ if (serverId === "unknown") {
447
+ // Can't parse - try executing as-is through unified registry
448
+ mcpLogger.debug(`[${functionTag}] Cannot parse function name format: ${functionName}, trying unified registry`);
449
+ const result = await unifiedRegistry.executeTool(functionName, actualParameters, finalContext);
450
+ return result;
380
451
  }
381
452
  // Handle built-in NeuroLink servers directly
382
453
  if (serverId === "neurolink-utility" ||
@@ -541,3 +612,31 @@ export async function isFunctionCallingAvailable() {
541
612
  return false;
542
613
  }
543
614
  }
615
+ /**
616
+ * Utility function to create a named tool object for debugging
617
+ */
618
+ export function createNamedTool(name, toolDef) {
619
+ return {
620
+ name,
621
+ description: toolDef.description,
622
+ parameters: toolDef.parameters,
623
+ execute: toolDef.execute,
624
+ };
625
+ }
626
+ /**
627
+ * Get tools with proper name properties for debugging
628
+ */
629
+ export async function getToolsWithNames() {
630
+ try {
631
+ const { toolsObject } = await getAvailableFunctionTools();
632
+ const namedTools = {};
633
+ for (const [name, tool] of Object.entries(toolsObject)) {
634
+ namedTools[name] = createNamedTool(name, tool);
635
+ }
636
+ return namedTools;
637
+ }
638
+ catch (error) {
639
+ mcpLogger.warn("[getToolsWithNames] Failed to get tools with names:", error);
640
+ return {};
641
+ }
642
+ }
@@ -70,6 +70,7 @@ export declare class NeuroLinkMCPClient extends EventEmitter {
70
70
  * Get registered tools
71
71
  */
72
72
  getTools(): Record<string, {
73
+ name: string;
73
74
  description?: string;
74
75
  inputSchema?: unknown;
75
76
  }>;
@@ -244,11 +244,26 @@ Response (JSON array only):`;
244
244
  exists: async () => false,
245
245
  },
246
246
  path: {
247
- join: (...paths) => require("path").join(...paths),
248
- resolve: (...paths) => require("path").resolve(...paths),
249
- relative: (from, to) => require("path").relative(from, to),
250
- dirname: (path) => require("path").dirname(path),
251
- basename: (path, ext) => require("path").basename(path, ext),
247
+ join: (...paths) => {
248
+ const pathModule = require("path");
249
+ return pathModule.join(...paths);
250
+ },
251
+ resolve: (...paths) => {
252
+ const pathModule = require("path");
253
+ return pathModule.resolve(...paths);
254
+ },
255
+ relative: (from, to) => {
256
+ const pathModule = require("path");
257
+ return pathModule.relative(from, to);
258
+ },
259
+ dirname: (pathArg) => {
260
+ const pathModule = require("path");
261
+ return pathModule.dirname(pathArg);
262
+ },
263
+ basename: (pathArg, ext) => {
264
+ const pathModule = require("path");
265
+ return pathModule.basename(pathArg, ext);
266
+ },
252
267
  },
253
268
  grantedPermissions: [],
254
269
  log: console.log,
@@ -409,6 +424,7 @@ Please provide a natural response based on the tool results.`;
409
424
  const tools = {};
410
425
  for (const [name, tool] of this.tools) {
411
426
  tools[name] = {
427
+ name: name, // Include the tool name as a property
412
428
  description: tool.description,
413
429
  inputSchema: tool.inputSchema,
414
430
  };