search_templates
Search for workflow templates by name or description keywords to streamline automation in n8n workflows. Input a query and set a result limit for precise template discovery.
Instructions
Search templates by name/description keywords. NOT for node types! For nodes use list_node_templates. Example: "chatbot".
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results. Default 20. | |
| query | Yes | Search keyword as string. Example: "chatbot" |
Implementation Reference
- dist/mcp/tools.js:207-296 (schema)Defines the input schema, description, parameters, and validation rules for the search_templates MCP tool{ name: 'search_templates', description: `Search templates with multiple modes. Use searchMode='keyword' for text search, 'by_nodes' to find templates using specific nodes, 'by_task' for curated task-based templates, 'by_metadata' for filtering by complexity/setup time/services.`, inputSchema: { type: 'object', properties: { searchMode: { type: 'string', enum: ['keyword', 'by_nodes', 'by_task', 'by_metadata'], description: 'Search mode. keyword=text search (default), by_nodes=find by node types, by_task=curated task templates, by_metadata=filter by complexity/services', default: 'keyword', }, query: { type: 'string', description: 'For searchMode=keyword: search keyword (e.g., "chatbot")', }, fields: { type: 'array', items: { type: 'string', enum: ['id', 'name', 'description', 'author', 'nodes', 'views', 'created', 'url', 'metadata'], }, description: 'For searchMode=keyword: fields to include in response. Default: all fields.', }, nodeTypes: { type: 'array', items: { type: 'string' }, description: 'For searchMode=by_nodes: array of node types (e.g., ["n8n-nodes-base.httpRequest", "n8n-nodes-base.slack"])', }, task: { type: 'string', enum: [ 'ai_automation', 'data_sync', 'webhook_processing', 'email_automation', 'slack_integration', 'data_transformation', 'file_processing', 'scheduling', 'api_integration', 'database_operations' ], description: 'For searchMode=by_task: the type of task', }, category: { type: 'string', description: 'For searchMode=by_metadata: filter by category (e.g., "automation", "integration")', }, complexity: { type: 'string', enum: ['simple', 'medium', 'complex'], description: 'For searchMode=by_metadata: filter by complexity level', }, maxSetupMinutes: { type: 'number', description: 'For searchMode=by_metadata: maximum setup time in minutes', minimum: 5, maximum: 480, }, minSetupMinutes: { type: 'number', description: 'For searchMode=by_metadata: minimum setup time in minutes', minimum: 5, maximum: 480, }, requiredService: { type: 'string', description: 'For searchMode=by_metadata: filter by required service (e.g., "openai", "slack")', }, targetAudience: { type: 'string', description: 'For searchMode=by_metadata: filter by target audience (e.g., "developers", "marketers")', }, limit: { type: 'number', description: 'Maximum number of results. Default 20.', default: 20, minimum: 1, maximum: 100, }, offset: { type: 'number', description: 'Pagination offset. Default 0.', default: 0, minimum: 0, }, }, }, },
- dist/mcp/server.d.ts:70-72 (handler)Handler methods for search_templates tool in N8NDocumentationMCPServer classprivate searchTemplates; private getTemplatesForTask; private searchTemplatesByMetadata;
- dist/mcp/tools.js:3-382 (registration)Registration of search_templates in the tools list returned by MCP tools/list endpointexports.n8nDocumentationToolsFinal = void 0; exports.n8nDocumentationToolsFinal = [ { name: 'tools_documentation', description: `Get documentation for n8n MCP tools. Call without parameters for quick start guide. Use topic parameter to get documentation for specific tools. Use depth='full' for comprehensive documentation.`, inputSchema: { type: 'object', properties: { topic: { type: 'string', description: 'Tool name (e.g., "search_nodes") or "overview" for general guide. Leave empty for quick reference.', }, depth: { type: 'string', enum: ['essentials', 'full'], description: 'Level of detail. "essentials" (default) for quick reference, "full" for comprehensive docs.', default: 'essentials', }, }, }, }, { name: 'search_nodes', description: `Search n8n nodes by keyword with optional real-world examples. Pass query as string. Example: query="webhook" or query="database". Returns max 20 results. Use includeExamples=true to get top 2 template configs per node.`, inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search terms. Use quotes for exact phrase.', }, limit: { type: 'number', description: 'Max results (default 20)', default: 20, }, mode: { type: 'string', enum: ['OR', 'AND', 'FUZZY'], description: 'OR=any word, AND=all words, FUZZY=typo-tolerant', default: 'OR', }, includeExamples: { type: 'boolean', description: 'Include top 2 real-world configuration examples from popular templates (default: false)', default: false, }, }, required: ['query'], }, }, { name: 'get_node', description: `Get node info with progressive detail levels and multiple modes. Detail: minimal (~200 tokens), standard (~1-2K, default), full (~3-8K). Modes: info (default), docs (markdown documentation), search_properties (find properties), versions/compare/breaking/migrations (version info). Use format='docs' for readable documentation, mode='search_properties' with propertyQuery for finding specific fields.`, inputSchema: { type: 'object', properties: { nodeType: { type: 'string', description: 'Full node type: "nodes-base.httpRequest" or "nodes-langchain.agent"', }, detail: { type: 'string', enum: ['minimal', 'standard', 'full'], default: 'standard', description: 'Information detail level. standard=essential properties (recommended), full=everything', }, mode: { type: 'string', enum: ['info', 'docs', 'search_properties', 'versions', 'compare', 'breaking', 'migrations'], default: 'info', description: 'Operation mode. info=node schema, docs=readable markdown documentation, search_properties=find specific properties, versions/compare/breaking/migrations=version info', }, includeTypeInfo: { type: 'boolean', default: false, description: 'Include type structure metadata (type category, JS type, validation rules). Only applies to mode=info. Adds ~80-120 tokens per property.', }, includeExamples: { type: 'boolean', default: false, description: 'Include real-world configuration examples from templates. Only applies to mode=info with detail=standard. Adds ~200-400 tokens per example.', }, fromVersion: { type: 'string', description: 'Source version for compare/breaking/migrations modes (e.g., "1.0")', }, toVersion: { type: 'string', description: 'Target version for compare mode (e.g., "2.0"). Defaults to latest if omitted.', }, propertyQuery: { type: 'string', description: 'For mode=search_properties: search term to find properties (e.g., "auth", "header", "body")', }, maxPropertyResults: { type: 'number', description: 'For mode=search_properties: max results (default 20)', default: 20, }, }, required: ['nodeType'], }, }, { name: 'validate_node', description: `Validate n8n node configuration. Use mode='full' for comprehensive validation with errors/warnings/suggestions, mode='minimal' for quick required fields check. Example: nodeType="nodes-base.slack", config={resource:"channel",operation:"create"}`, inputSchema: { type: 'object', properties: { nodeType: { type: 'string', description: 'Node type as string. Example: "nodes-base.slack"', }, config: { type: 'object', description: 'Configuration as object. For simple nodes use {}. For complex nodes include fields like {resource:"channel",operation:"create"}', }, mode: { type: 'string', enum: ['full', 'minimal'], description: 'Validation mode. full=comprehensive validation with errors/warnings/suggestions, minimal=quick required fields check only. Default is "full"', default: 'full', }, profile: { type: 'string', enum: ['strict', 'runtime', 'ai-friendly', 'minimal'], description: 'Profile for mode=full: "minimal", "runtime", "ai-friendly", or "strict". Default is "ai-friendly"', default: 'ai-friendly', }, }, required: ['nodeType', 'config'], additionalProperties: false, }, outputSchema: { type: 'object', properties: { nodeType: { type: 'string' }, workflowNodeType: { type: 'string' }, displayName: { type: 'string' }, valid: { type: 'boolean' }, errors: { type: 'array', items: { type: 'object', properties: { type: { type: 'string' }, property: { type: 'string' }, message: { type: 'string' }, fix: { type: 'string' } } } }, warnings: { type: 'array', items: { type: 'object', properties: { type: { type: 'string' }, property: { type: 'string' }, message: { type: 'string' }, suggestion: { type: 'string' } } } }, suggestions: { type: 'array', items: { type: 'string' } }, missingRequiredFields: { type: 'array', items: { type: 'string' }, description: 'Only present in mode=minimal' }, summary: { type: 'object', properties: { hasErrors: { type: 'boolean' }, errorCount: { type: 'number' }, warningCount: { type: 'number' }, suggestionCount: { type: 'number' } } } }, required: ['nodeType', 'displayName', 'valid'] }, }, { name: 'get_template', description: `Get template by ID. Use mode to control response size: nodes_only (minimal), structure (nodes+connections), full (complete workflow).`, inputSchema: { type: 'object', properties: { templateId: { type: 'number', description: 'The template ID to retrieve', }, mode: { type: 'string', enum: ['nodes_only', 'structure', 'full'], description: 'Response detail level. nodes_only: just node list, structure: nodes+connections, full: complete workflow JSON.', default: 'full', }, }, required: ['templateId'], }, }, { name: 'search_templates', description: `Search templates with multiple modes. Use searchMode='keyword' for text search, 'by_nodes' to find templates using specific nodes, 'by_task' for curated task-based templates, 'by_metadata' for filtering by complexity/setup time/services.`, inputSchema: { type: 'object', properties: { searchMode: { type: 'string', enum: ['keyword', 'by_nodes', 'by_task', 'by_metadata'], description: 'Search mode. keyword=text search (default), by_nodes=find by node types, by_task=curated task templates, by_metadata=filter by complexity/services', default: 'keyword', }, query: { type: 'string', description: 'For searchMode=keyword: search keyword (e.g., "chatbot")', }, fields: { type: 'array', items: { type: 'string', enum: ['id', 'name', 'description', 'author', 'nodes', 'views', 'created', 'url', 'metadata'], }, description: 'For searchMode=keyword: fields to include in response. Default: all fields.', }, nodeTypes: { type: 'array', items: { type: 'string' }, description: 'For searchMode=by_nodes: array of node types (e.g., ["n8n-nodes-base.httpRequest", "n8n-nodes-base.slack"])', }, task: { type: 'string', enum: [ 'ai_automation', 'data_sync', 'webhook_processing', 'email_automation', 'slack_integration', 'data_transformation', 'file_processing', 'scheduling', 'api_integration', 'database_operations' ], description: 'For searchMode=by_task: the type of task', }, category: { type: 'string', description: 'For searchMode=by_metadata: filter by category (e.g., "automation", "integration")', }, complexity: { type: 'string', enum: ['simple', 'medium', 'complex'], description: 'For searchMode=by_metadata: filter by complexity level', }, maxSetupMinutes: { type: 'number', description: 'For searchMode=by_metadata: maximum setup time in minutes', minimum: 5, maximum: 480, }, minSetupMinutes: { type: 'number', description: 'For searchMode=by_metadata: minimum setup time in minutes', minimum: 5, maximum: 480, }, requiredService: { type: 'string', description: 'For searchMode=by_metadata: filter by required service (e.g., "openai", "slack")', }, targetAudience: { type: 'string', description: 'For searchMode=by_metadata: filter by target audience (e.g., "developers", "marketers")', }, limit: { type: 'number', description: 'Maximum number of results. Default 20.', default: 20, minimum: 1, maximum: 100, }, offset: { type: 'number', description: 'Pagination offset. Default 0.', default: 0, minimum: 0, }, }, }, }, { name: 'validate_workflow', description: `Full workflow validation: structure, connections, expressions, AI tools. Returns errors/warnings/fixes. Essential before deploy.`, inputSchema: { type: 'object', properties: { workflow: { type: 'object', description: 'The complete workflow JSON to validate. Must include nodes array and connections object.', }, options: { type: 'object', properties: { validateNodes: { type: 'boolean', description: 'Validate individual node configurations. Default true.', default: true, }, validateConnections: { type: 'boolean', description: 'Validate node connections and flow. Default true.', default: true, }, validateExpressions: { type: 'boolean', description: 'Validate n8n expressions syntax and references. Default true.', default: true, }, profile: { type: 'string', enum: ['minimal', 'runtime', 'ai-friendly', 'strict'], description: 'Validation profile for node validation. Default "runtime".', default: 'runtime', }, }, description: 'Optional validation settings', }, }, required: ['workflow'], additionalProperties: false, }, outputSchema: { type: 'object', properties: { valid: { type: 'boolean' }, summary: { type: 'object', properties: { totalNodes: { type: 'number' }, enabledNodes: { type: 'number' }, triggerNodes: { type: 'number' }, validConnections: { type: 'number' }, invalidConnections: { type: 'number' }, expressionsValidated: { type: 'number' }, errorCount: { type: 'number' }, warningCount: { type: 'number' } } }, errors: { type: 'array', items: { type: 'object', properties: { node: { type: 'string' }, message: { type: 'string' }, details: { type: 'string' } } } }, warnings: { type: 'array', items: { type: 'object', properties: { node: { type: 'string' }, message: { type: 'string' }, details: { type: 'string' } } } }, suggestions: { type: 'array', items: { type: 'string' } } }, required: ['valid', 'summary'] }, }, ];
- Core keyword-based template search using SQLite FTS5 virtual table or LIKE fallbacksearchTemplates(query, limit = 20, offset = 0) { logger_1.logger.debug(`Searching templates for: "${query}" (FTS5: ${this.hasFTS5Support})`); if (!this.hasFTS5Support) { logger_1.logger.debug('Using LIKE search (FTS5 not available)'); return this.searchTemplatesLIKE(query, limit, offset); } try { const ftsQuery = query.split(' ').map(term => { const escaped = term.replace(/"/g, '""'); return `"${escaped}"`; }).join(' OR '); logger_1.logger.debug(`FTS5 query: ${ftsQuery}`); const results = this.db.prepare(` SELECT t.* FROM templates t JOIN templates_fts ON t.id = templates_fts.rowid WHERE templates_fts MATCH ? ORDER BY rank, t.views DESC LIMIT ? OFFSET ? `).all(ftsQuery, limit, offset); logger_1.logger.debug(`FTS5 search returned ${results.length} results`); return results.map(t => this.decompressWorkflow(t)); } catch (error) { logger_1.logger.warn('FTS5 template search failed, using LIKE fallback:', { message: error.message, query: query, ftsQuery: query.split(' ').map(term => `"${term}"`).join(' OR ') }); return this.searchTemplatesLIKE(query, limit, offset); } } searchTemplatesLIKE(query, limit = 20, offset = 0) { const likeQuery = `%${query}%`; logger_1.logger.debug(`Using LIKE search with pattern: ${likeQuery}`); const results = this.db.prepare(` SELECT * FROM templates WHERE name LIKE ? OR description LIKE ? ORDER BY views DESC, created_at DESC LIMIT ? OFFSET ? `).all(likeQuery, likeQuery, limit, offset); logger_1.logger.debug(`LIKE search returned ${results.length} results`); return results.map(t => this.decompressWorkflow(t)); }
- Metadata filtering search implementation for by_metadata mode, building conditions from filters like complexity, setup time, servicessearchTemplatesByMetadata(filters, limit = 20, offset = 0) { const startTime = Date.now(); const { conditions, params } = this.buildMetadataFilterConditions(filters); const idsQuery = ` SELECT id FROM templates WHERE ${conditions.join(' AND ')} ORDER BY views DESC, created_at DESC, id ASC LIMIT ? OFFSET ? `; params.push(limit, offset); const ids = this.db.prepare(idsQuery).all(...params); const phase1Time = Date.now() - startTime; if (ids.length === 0) { logger_1.logger.debug('Metadata search found 0 results', { filters, phase1Ms: phase1Time }); return []; } const idValues = ids.map(r => r.id).filter(id => typeof id === 'number' && id > 0 && Number.isInteger(id)); if (idValues.length === 0) { logger_1.logger.warn('No valid IDs after filtering', { filters, originalCount: ids.length }); return []; } if (idValues.length !== ids.length) { logger_1.logger.warn('Some IDs were filtered out as invalid', { original: ids.length, valid: idValues.length, filtered: ids.length - idValues.length }); } const phase2Start = Date.now(); const orderedQuery = ` WITH ordered_ids(id, sort_order) AS ( VALUES ${idValues.map((id, idx) => `(${id}, ${idx})`).join(', ')} ) SELECT t.* FROM templates t INNER JOIN ordered_ids o ON t.id = o.id ORDER BY o.sort_order `; const results = this.db.prepare(orderedQuery).all(); const phase2Time = Date.now() - phase2Start; logger_1.logger.debug(`Metadata search found ${results.length} results`, { filters, count: results.length, phase1Ms: phase1Time, phase2Ms: phase2Time, totalMs: Date.now() - startTime, optimization: 'two-phase-with-ordering' }); return results.map(t => this.decompressWorkflow(t)); }