UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

259 lines 8.52 kB
/** * Tool Converter Utilities * * Converts between NeuroLink tool format and MCP tool format. * Enables seamless interoperability between NeuroLink's internal * tool representation and the MCP protocol specification. * * @module mcp/toolConverter * @since 8.39.0 */ import { inferAnnotations } from "./toolAnnotations.js"; import { withTimeout } from "../utils/async/withTimeout.js"; /** * Convert NeuroLink tool to MCP server tool format */ export function neuroLinkToolToMCP(tool, options = {}) { const { inferAnnotations: shouldInfer = true, defaultAnnotations = {}, preserveMetadata = true, namespacePrefix, } = options; // Apply namespace prefix if provided const toolName = namespacePrefix ? `${namespacePrefix}_${tool.name}` : tool.name; // Infer annotations from tool definition const inferredAnnotations = shouldInfer ? inferAnnotations({ name: tool.name, description: tool.description }) : {}; // Build annotations const annotations = { ...defaultAnnotations, ...inferredAnnotations, }; // Add tags if present if (tool.tags?.length) { annotations.tags = [ ...new Set([...(annotations.tags ?? []), ...tool.tags]), ]; } // Build input schema const inputSchema = tool.parameters ?? { type: "object", properties: {}, }; // Build metadata const metadata = preserveMetadata ? { ...tool.metadata } : {}; if (tool.category) { metadata.category = tool.category; } if (tool.isAsync !== undefined) { metadata.isAsync = tool.isAsync; } return { name: toolName, description: tool.description, inputSchema, annotations, execute: tool.execute, metadata, }; } /** * Convert MCP server tool to NeuroLink tool format */ export function mcpToolToNeuroLink(tool, options = {}) { const { removeNamespacePrefix } = options; // Remove namespace prefix if provided let toolName = tool.name; if (removeNamespacePrefix && tool.name.startsWith(`${removeNamespacePrefix}_`)) { toolName = tool.name.slice(removeNamespacePrefix.length + 1); } return { name: toolName, description: tool.description, parameters: tool.inputSchema, execute: tool.execute, category: tool.metadata?.category, tags: tool.annotations?.tags, metadata: tool.metadata, }; } /** * Convert MCP protocol tool to MCPServerTool * (For tools received from external MCP servers) */ export function mcpProtocolToolToServerTool(protocolTool, executor, options = {}) { const { inferAnnotations: shouldInfer = true, defaultAnnotations = {} } = options; // Convert protocol annotations to our format const protocolAnnotations = protocolTool.annotations ?? {}; // Infer additional annotations const inferredAnnotations = shouldInfer ? inferAnnotations({ name: protocolTool.name, description: protocolTool.description ?? "", }) : {}; // Merge annotations with precedence: protocol > inferred > defaults const annotations = { ...defaultAnnotations, ...inferredAnnotations, title: protocolAnnotations.title ?? inferredAnnotations.title ?? defaultAnnotations.title, readOnlyHint: protocolAnnotations.readOnlyHint ?? inferredAnnotations.readOnlyHint ?? defaultAnnotations.readOnlyHint, destructiveHint: protocolAnnotations.destructiveHint ?? inferredAnnotations.destructiveHint ?? defaultAnnotations.destructiveHint, idempotentHint: protocolAnnotations.idempotentHint ?? inferredAnnotations.idempotentHint ?? defaultAnnotations.idempotentHint, openWorldHint: protocolAnnotations.openWorldHint ?? inferredAnnotations.openWorldHint ?? defaultAnnotations.openWorldHint, }; return { name: protocolTool.name, description: protocolTool.description ?? "No description provided", inputSchema: protocolTool.inputSchema, annotations, execute: executor, }; } /** * Convert MCPServerTool to MCP protocol tool format * (For exposing tools to external MCP clients) */ export function serverToolToMCPProtocol(tool) { // Build protocol annotations const annotations = {}; if (tool.annotations?.title) { annotations.title = tool.annotations.title; } if (tool.annotations?.readOnlyHint !== undefined) { annotations.readOnlyHint = tool.annotations.readOnlyHint; } if (tool.annotations?.destructiveHint !== undefined) { annotations.destructiveHint = tool.annotations.destructiveHint; } if (tool.annotations?.idempotentHint !== undefined) { annotations.idempotentHint = tool.annotations.idempotentHint; } if (tool.annotations?.openWorldHint !== undefined) { annotations.openWorldHint = tool.annotations.openWorldHint; } // Build input schema const inputSchema = tool.inputSchema ?? { type: "object", properties: {}, }; return { name: tool.name, description: tool.description, inputSchema: { type: "object", properties: (inputSchema.properties ?? {}), required: ("required" in inputSchema ? inputSchema.required : undefined), }, annotations: Object.keys(annotations).length > 0 ? annotations : undefined, }; } /** * Batch convert NeuroLink tools to MCP format */ export function batchConvertToMCP(tools, options = {}) { return tools.map((tool) => neuroLinkToolToMCP(tool, options)); } /** * Batch convert MCP tools to NeuroLink format */ export function batchConvertToNeuroLink(tools, options = {}) { return tools.map((tool) => mcpToolToNeuroLink(tool, options)); } /** * Create a tool from a function with automatic schema inference */ export function createToolFromFunction(name, description, fn, options) { const inferredAnnotations = inferAnnotations({ name, description }); return { name, description, inputSchema: options?.parameters ?? { type: "object", properties: {} }, annotations: { ...inferredAnnotations, ...options?.annotations, }, execute: async (params, context) => { const toolTimeoutMs = 30_000; const result = await withTimeout(fn(params, context), toolTimeoutMs, `Tool '${name}' execution timed out after ${toolTimeoutMs}ms`); return result; }, metadata: options?.metadata, }; } /** * Validate tool name according to MCP specification */ export function validateToolName(name) { const errors = []; if (!name || typeof name !== "string") { errors.push("Tool name is required and must be a string"); } else { if (name.length > 64) { errors.push("Tool name must be 64 characters or less"); } if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) { errors.push("Tool name must start with a letter or underscore and contain only alphanumeric characters, underscores, and hyphens"); } } return { valid: errors.length === 0, errors }; } /** * Sanitize tool name for MCP compatibility */ export function sanitizeToolName(name) { // Replace invalid characters with underscores let sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "_"); // Ensure starts with letter or underscore if (!/^[a-zA-Z_]/.test(sanitized)) { sanitized = `_${sanitized}`; } // Truncate to 64 characters if (sanitized.length > 64) { sanitized = sanitized.slice(0, 64); } return sanitized; } /** * Tool compatibility matrix */ export const TOOL_COMPATIBILITY = { /** * Features supported by MCP 2024-11-05 specification */ MCP_2024_11_05: { annotations: true, inputSchema: true, outputSchema: false, streamingResults: false, batchExecution: false, }, /** * Features supported by NeuroLink */ NEUROLINK: { annotations: true, inputSchema: true, outputSchema: true, streamingResults: true, batchExecution: true, categories: true, tags: true, }, }; //# sourceMappingURL=toolConverter.js.map