Skip to main content
Glama
tool-variant-generator.ts5.41 kB
/** * Tool Variant Generator * * Generates Tool variant nodes for nodes with usableAsTool: true. * * n8n dynamically creates Tool variants (e.g., supabaseTool from supabase) * that can be connected to AI Agents. These variants have: * - A 'Tool' suffix on the node type * - An additional 'toolDescription' property * - Output type 'ai_tool' instead of 'main' */ import type { ParsedNode } from '../parsers/node-parser'; export class ToolVariantGenerator { /** * Generate a Tool variant from a base node with usableAsTool: true * * @param baseNode - The base ParsedNode that has isAITool: true * @returns A new ParsedNode representing the Tool variant, or null if not applicable */ generateToolVariant(baseNode: ParsedNode): ParsedNode | null { // Only generate for nodes with usableAsTool: true (isAITool) if (!baseNode.isAITool) { return null; } // Don't generate Tool variant for nodes that are already Tool variants if (baseNode.isToolVariant) { return null; } // Don't generate for trigger nodes (they can't be used as tools) if (baseNode.isTrigger) { return null; } // Validate nodeType exists if (!baseNode.nodeType) { return null; } // Generate the Tool variant node type // e.g., nodes-base.supabase -> nodes-base.supabaseTool const toolNodeType = `${baseNode.nodeType}Tool`; // Ensure properties is an array to prevent spread operator errors const baseProperties = Array.isArray(baseNode.properties) ? baseNode.properties : []; return { ...baseNode, nodeType: toolNodeType, displayName: `${baseNode.displayName} Tool`, description: baseNode.description ? `${baseNode.description} (AI Tool variant for use with AI Agents)` : 'AI Tool variant for use with AI Agents', // Mark as Tool variant isToolVariant: true, toolVariantOf: baseNode.nodeType, hasToolVariant: false, // Tool variants don't have further variants // Override outputs for Tool variant outputs: [{ type: 'ai_tool', displayName: 'Tool' }], outputNames: ['Tool'], // Add toolDescription property at the beginning properties: this.addToolDescriptionProperty(baseProperties, baseNode.displayName), }; } /** * Add the toolDescription property to the beginning of the properties array */ private addToolDescriptionProperty(properties: any[], displayName: string): any[] { const toolDescriptionProperty = { displayName: 'Tool Description', name: 'toolDescription', type: 'string', default: '', required: false, description: 'Description for the AI to understand what this tool does and when to use it', typeOptions: { rows: 3 }, placeholder: `e.g., Use this tool to ${this.generateDescriptionPlaceholder(displayName)}` }; return [toolDescriptionProperty, ...properties]; } /** * Generate a placeholder description based on the node display name */ private generateDescriptionPlaceholder(displayName: string): string { const lowerName = displayName.toLowerCase(); // Common patterns if (lowerName.includes('database') || lowerName.includes('sql')) { return 'query and manage data in the database'; } if (lowerName.includes('email') || lowerName.includes('mail')) { return 'send and manage emails'; } if (lowerName.includes('sheet') || lowerName.includes('spreadsheet')) { return 'read and write spreadsheet data'; } if (lowerName.includes('file') || lowerName.includes('drive') || lowerName.includes('storage')) { return 'manage files and storage'; } if (lowerName.includes('message') || lowerName.includes('chat') || lowerName.includes('slack')) { return 'send messages and communicate'; } if (lowerName.includes('http') || lowerName.includes('api') || lowerName.includes('request')) { return 'make API requests and fetch data'; } if (lowerName.includes('calendar') || lowerName.includes('event')) { return 'manage calendar events and schedules'; } // Default placeholder return `interact with ${displayName}`; } /** * Check if a node type looks like a Tool variant. * Valid Tool variants must: * - End with 'Tool' but not 'ToolTool' * - Have a valid package.nodeName pattern (contain a dot) * - Have content after the dot before 'Tool' suffix */ static isToolVariantNodeType(nodeType: string): boolean { if (!nodeType || !nodeType.endsWith('Tool') || nodeType.endsWith('ToolTool')) { return false; } // The base part (without 'Tool' suffix) should be a valid node pattern const basePart = nodeType.slice(0, -4); // Valid pattern: package.nodeName (must contain a dot and have content after it) return basePart.includes('.') && basePart.split('.').pop()!.length > 0; } /** * Get the base node type from a Tool variant node type */ static getBaseNodeType(toolNodeType: string): string | null { if (!ToolVariantGenerator.isToolVariantNodeType(toolNodeType)) { return null; } return toolNodeType.slice(0, -4); // Remove 'Tool' suffix } /** * Get the Tool variant node type from a base node type */ static getToolVariantNodeType(baseNodeType: string): string { return `${baseNodeType}Tool`; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/czlonkowski/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server