UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and

186 lines (185 loc) 7.93 kB
/** * NeuroLink SDK Tool Registration API * Simple interface for developers to register custom tools */ import { z } from "zod"; import { tool as createAISDKTool } from "ai"; import { logger } from "../utils/logger.js"; /** * Configuration constants for tool validation */ const envValue = parseInt(process.env.NEUROLINK_TOOL_DESCRIPTION_MAX_LENGTH || "200", 10); const DEFAULT_DESCRIPTION_MAX_LENGTH = Number.isInteger(envValue) && envValue > 0 ? envValue : 200; /** * Converts a SimpleTool to Vercel AI SDK format */ export function convertToAISDKTool(name, simpleTool) { return createAISDKTool({ description: simpleTool.description, parameters: simpleTool.parameters || z.object({}), execute: async (args) => { try { // Create a minimal context for standalone execution const context = { sessionId: `tool-${name}-${Date.now()}`, logger, }; const result = await simpleTool.execute(args, context); return result; } catch (error) { logger.error(`Tool ${name} execution failed:`, error); throw error; } }, }); } /** * Converts a SimpleTool to MCP tool format */ export function convertToMCPTool(simpleTool) { return { description: simpleTool.description, execute: async (params) => { const typedParams = params; try { const result = await simpleTool.execute(typedParams); return { success: true, data: result, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error("MCP tool execution failed:", error); return { success: false, error: errorMessage, }; } }, inputSchema: simpleTool.parameters, isImplemented: true, metadata: simpleTool.metadata, }; } /** * Creates an in-memory MCP server configuration from a set of tools */ export function createMCPServerFromTools(serverId, tools, metadata) { const mcpTools = {}; for (const [name, tool] of Object.entries(tools)) { mcpTools[name] = convertToMCPTool(tool); } return { server: { title: metadata?.title || serverId, description: metadata?.description, tools: mcpTools, }, category: metadata?.category, metadata: metadata || {}, }; } /** * Helper to create a tool with type safety */ export function createTool(config) { return config; } /** * Helper to create a tool with typed parameters */ export function createTypedTool(config) { return config; } /** * Validate tool description length */ function validateDescriptionLength(name, description) { const maxDescriptionLength = Number.isInteger(DEFAULT_DESCRIPTION_MAX_LENGTH) && DEFAULT_DESCRIPTION_MAX_LENGTH > 0 ? DEFAULT_DESCRIPTION_MAX_LENGTH : 200; if (description.length > maxDescriptionLength) { throw new Error(`Tool '${name}' description should be concise (max ${maxDescriptionLength} characters). ` + `Current length: ${description.length}. ` + `Consider shortening: "${description.substring(0, 50)}..."`); } } /** * Validate tool configuration with detailed error messages */ export function validateTool(name, tool) { // Validate tool name if (!name || typeof name !== "string" || name.trim() === "") { throw new Error(`Invalid tool name: must be a non-empty string. Received: ${name}`); } // Validate tool name format (alphanumeric, hyphens, underscores only) const validNamePattern = /^[a-zA-Z0-9_-]+$/; if (!validNamePattern.test(name)) { throw new Error(`Invalid tool name format: '${name}'. Tool names must contain only alphanumeric characters, hyphens, and underscores. ` + `Examples: 'calculate-tax', 'get_weather', 'sendEmail123'`); } // Validate tool object if (!tool || typeof tool !== "object") { throw new Error(`Tool '${name}' must be an object with description and execute properties. Received: ${typeof tool}`); } // Validate description if (!tool.description || typeof tool.description !== "string" || tool.description.trim() === "") { throw new Error(`Tool '${name}' must have a non-empty description string. ` + `Example: { description: "Calculates mathematical expressions", execute: async (params) => {...} }`); } // Validate execute function with signature guidance if (typeof tool.execute !== "function") { throw new Error(`Tool '${name}' must have an execute function. ` + `Expected signature: async (params?: ToolArgs) => Promise<unknown>. ` + `Received: ${typeof tool.execute}. ` + `Example: { execute: async (params) => { return { success: true, data: result }; } }`); } // Check for common mistake: using 'schema' instead of 'parameters' if ("schema" in tool && !("parameters" in tool)) { throw new Error(`Tool '${name}' uses 'schema' property, but NeuroLink expects 'parameters'. ` + `Please change 'schema' to 'parameters' and use a Zod schema: ` + `{ parameters: z.object({ ... }), execute: ... } ` + `See documentation: https://docs.neurolink.com/tools`); } // Validate parameters schema if provided - support both Zod and custom schemas if (tool.parameters) { if (typeof tool.parameters !== "object") { throw new Error(`Tool '${name}' parameters must be an object. ` + `Received: ${typeof tool.parameters}`); } // Check for common schema validation methods (Zod uses 'parse', others might use 'validate') const params = tool.parameters; const hasValidationMethod = typeof params.parse === "function" || typeof params.validate === "function" || "_def" in params; // Zod schemas have _def property // Check for plain JSON schema objects (common mistake) if ("type" in params && "properties" in params && !hasValidationMethod) { throw new Error(`Tool '${name}' appears to use a plain JSON schema object as parameters. ` + `NeuroLink requires a Zod schema for proper type validation and tool integration. ` + `Please change from:\n` + ` { type: 'object', properties: { ... } }\n` + `To:\n` + ` z.object({ fieldName: z.string() })\n` + `Import Zod with: import { z } from 'zod'`); } if (!hasValidationMethod) { const errorMessage = typeof params.parse === "function" || "_def" in params ? `Tool '${name}' has a Zod-like schema but validation failed. Ensure it's a valid Zod schema: z.object({ ... })` : typeof params.validate === "function" ? `Tool '${name}' has a validate method but it may not be callable. Ensure: { parameters: { validate: (data) => { ... } } }` : `Tool '${name}' parameters must be a schema object with validation. ` + `Supported formats:\n` + `• Zod schema: { parameters: z.object({ value: z.string() }) }\n` + `• Custom schema: { parameters: { validate: (data) => { ... } } }\n` + `• Custom schema: { parameters: { parse: (data) => { ... } } }`; throw new Error(errorMessage); } } // Validate description length for better UX validateDescriptionLength(name, tool.description); }