n8n-mcp 2.14.7 → 2.15.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.
package/data/nodes.db CHANGED
Binary file
@@ -35,7 +35,6 @@ export declare class N8NDocumentationMCPServer {
35
35
  private getDatabaseStatistics;
36
36
  private getNodeEssentials;
37
37
  private searchNodeProperties;
38
- private getNodeForTask;
39
38
  private getPropertyValue;
40
39
  private listTasks;
41
40
  private validateNodeConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqB5D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,EAAE,CAAgC;IAC1C,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,qBAAqB,CAAsB;gBAEvC,eAAe,CAAC,EAAE,eAAe;YA2D/B,kBAAkB;YAsBlB,wBAAwB;YAgBxB,iBAAiB;IAO/B,OAAO,CAAC,aAAa;IA+QrB,OAAO,CAAC,wBAAwB;IAoFhC,OAAO,CAAC,kBAAkB;IAqE1B,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,qBAAqB;YAoSf,SAAS;YA2DT,WAAW;YAiEX,WAAW;YAsCX,cAAc;YAwId,gBAAgB;IAqD9B,OAAO,CAAC,mBAAmB;IAwE3B,OAAO,CAAC,eAAe;YAsBT,eAAe;IAqE7B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,uBAAuB;IA0D/B,OAAO,CAAC,iBAAiB;YAqFX,WAAW;YAgCX,oBAAoB;YA2EpB,qBAAqB;YAwDrB,iBAAiB;YA+EjB,oBAAoB;YAsDpB,cAAc;IAqC5B,OAAO,CAAC,gBAAgB;YAiBV,SAAS;YA6CT,kBAAkB;YA+DlB,uBAAuB;YAsDvB,iBAAiB;IAqE/B,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,uBAAuB;IAwD/B,OAAO,CAAC,iBAAiB;YAoDX,mBAAmB;YAkGnB,qBAAqB;IAS7B,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;YAS9B,aAAa;YAcb,iBAAiB;YAoBjB,WAAW;YAwBX,eAAe;YAqBf,mBAAmB;YAwBnB,yBAAyB;IA4CvC,OAAO,CAAC,kBAAkB;YAiBZ,gBAAgB;YA6HhB,2BAA2B;YAiE3B,2BAA2B;IAyEnC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBhC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqB5D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,EAAE,CAAgC;IAC1C,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,qBAAqB,CAAsB;gBAEvC,eAAe,CAAC,EAAE,eAAe;YA2D/B,kBAAkB;YAsBlB,wBAAwB;YAgBxB,iBAAiB;IAO/B,OAAO,CAAC,aAAa;IA+QrB,OAAO,CAAC,wBAAwB;IAoFhC,OAAO,CAAC,kBAAkB;IAqE1B,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,qBAAqB;YAiSf,SAAS;YA2DT,WAAW;YAiEX,WAAW;YAyCX,cAAc;YAyKd,gBAAgB;IAqD9B,OAAO,CAAC,mBAAmB;IAwE3B,OAAO,CAAC,eAAe;YAsBT,eAAe;IAqI7B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,uBAAuB;IA0D/B,OAAO,CAAC,iBAAiB;YAqFX,WAAW;YAgCX,oBAAoB;YA2EpB,qBAAqB;YAwDrB,iBAAiB;YA6HjB,oBAAoB;IAsDlC,OAAO,CAAC,gBAAgB;YAiBV,SAAS;YA6CT,kBAAkB;YA+DlB,uBAAuB;YAsDvB,iBAAiB;IAqE/B,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,uBAAuB;IAwD/B,OAAO,CAAC,iBAAiB;YAoDX,mBAAmB;YAkGnB,qBAAqB;IAS7B,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;YAS9B,aAAa;YAcb,iBAAiB;YAoBjB,WAAW;YAwBX,eAAe;YAqBf,mBAAmB;YAwBnB,yBAAyB;IA4CvC,OAAO,CAAC,kBAAkB;YAiBZ,gBAAgB;YA6HhB,2BAA2B;YAiE3B,2BAA2B;IAyEnC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBhC"}
@@ -556,7 +556,7 @@ class N8NDocumentationMCPServer {
556
556
  case 'search_nodes':
557
557
  this.validateToolParams(name, args, ['query']);
558
558
  const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
559
- return this.searchNodes(args.query, limit, { mode: args.mode });
559
+ return this.searchNodes(args.query, limit, { mode: args.mode, includeExamples: args.includeExamples });
560
560
  case 'list_ai_tools':
561
561
  return this.listAITools();
562
562
  case 'get_node_documentation':
@@ -566,14 +566,11 @@ class N8NDocumentationMCPServer {
566
566
  return this.getDatabaseStatistics();
567
567
  case 'get_node_essentials':
568
568
  this.validateToolParams(name, args, ['nodeType']);
569
- return this.getNodeEssentials(args.nodeType);
569
+ return this.getNodeEssentials(args.nodeType, args.includeExamples);
570
570
  case 'search_node_properties':
571
571
  this.validateToolParams(name, args, ['nodeType', 'query']);
572
572
  const maxResults = args.maxResults !== undefined ? Number(args.maxResults) || 20 : 20;
573
573
  return this.searchNodeProperties(args.nodeType, args.query, maxResults);
574
- case 'get_node_for_task':
575
- this.validateToolParams(name, args, ['task']);
576
- return this.getNodeForTask(args.task);
577
574
  case 'list_tasks':
578
575
  return this.listTasks(args.category);
579
576
  case 'validate_node_operation':
@@ -847,13 +844,15 @@ class N8NDocumentationMCPServer {
847
844
  WHERE type='table' AND name='nodes_fts'
848
845
  `).get();
849
846
  if (ftsExists) {
850
- return this.searchNodesFTS(normalizedQuery, limit, searchMode);
847
+ logger_1.logger.debug(`Using FTS5 search with includeExamples=${options?.includeExamples}`);
848
+ return this.searchNodesFTS(normalizedQuery, limit, searchMode, options);
851
849
  }
852
850
  else {
853
- return this.searchNodesLIKE(normalizedQuery, limit);
851
+ logger_1.logger.debug('Using LIKE search (no FTS5)');
852
+ return this.searchNodesLIKE(normalizedQuery, limit, options);
854
853
  }
855
854
  }
856
- async searchNodesFTS(query, limit, mode) {
855
+ async searchNodesFTS(query, limit, mode, options) {
857
856
  if (!this.db)
858
857
  throw new Error('Database not initialized');
859
858
  const cleanedQuery = query.trim();
@@ -933,6 +932,32 @@ class N8NDocumentationMCPServer {
933
932
  if (mode !== 'OR') {
934
933
  result.mode = mode;
935
934
  }
935
+ if (options && options.includeExamples) {
936
+ try {
937
+ for (const nodeResult of result.results) {
938
+ const examples = this.db.prepare(`
939
+ SELECT
940
+ parameters_json,
941
+ template_name,
942
+ template_views
943
+ FROM template_node_configs
944
+ WHERE node_type = ?
945
+ ORDER BY rank
946
+ LIMIT 2
947
+ `).all(nodeResult.workflowNodeType);
948
+ if (examples.length > 0) {
949
+ nodeResult.examples = examples.map((ex) => ({
950
+ configuration: JSON.parse(ex.parameters_json),
951
+ template: ex.template_name,
952
+ views: ex.template_views
953
+ }));
954
+ }
955
+ }
956
+ }
957
+ catch (error) {
958
+ logger_1.logger.error(`Failed to add examples:`, error);
959
+ }
960
+ }
936
961
  telemetry_1.telemetry.trackSearchQuery(query, scoredNodes.length, mode ?? 'OR');
937
962
  return result;
938
963
  }
@@ -1060,18 +1085,18 @@ class N8NDocumentationMCPServer {
1060
1085
  }
1061
1086
  return dp[m][n];
1062
1087
  }
1063
- async searchNodesLIKE(query, limit) {
1088
+ async searchNodesLIKE(query, limit, options) {
1064
1089
  if (!this.db)
1065
1090
  throw new Error('Database not initialized');
1066
1091
  if (query.startsWith('"') && query.endsWith('"')) {
1067
1092
  const exactPhrase = query.slice(1, -1);
1068
1093
  const nodes = this.db.prepare(`
1069
- SELECT * FROM nodes
1094
+ SELECT * FROM nodes
1070
1095
  WHERE node_type LIKE ? OR display_name LIKE ? OR description LIKE ?
1071
1096
  LIMIT ?
1072
1097
  `).all(`%${exactPhrase}%`, `%${exactPhrase}%`, `%${exactPhrase}%`, limit * 3);
1073
1098
  const rankedNodes = this.rankSearchResults(nodes, exactPhrase, limit);
1074
- return {
1099
+ const result = {
1075
1100
  query,
1076
1101
  results: rankedNodes.map(node => ({
1077
1102
  nodeType: node.node_type,
@@ -1083,6 +1108,33 @@ class N8NDocumentationMCPServer {
1083
1108
  })),
1084
1109
  totalCount: rankedNodes.length
1085
1110
  };
1111
+ if (options?.includeExamples) {
1112
+ for (const nodeResult of result.results) {
1113
+ try {
1114
+ const examples = this.db.prepare(`
1115
+ SELECT
1116
+ parameters_json,
1117
+ template_name,
1118
+ template_views
1119
+ FROM template_node_configs
1120
+ WHERE node_type = ?
1121
+ ORDER BY rank
1122
+ LIMIT 2
1123
+ `).all(nodeResult.workflowNodeType);
1124
+ if (examples.length > 0) {
1125
+ nodeResult.examples = examples.map((ex) => ({
1126
+ configuration: JSON.parse(ex.parameters_json),
1127
+ template: ex.template_name,
1128
+ views: ex.template_views
1129
+ }));
1130
+ }
1131
+ }
1132
+ catch (error) {
1133
+ logger_1.logger.warn(`Failed to fetch examples for ${nodeResult.nodeType}:`, error.message);
1134
+ }
1135
+ }
1136
+ }
1137
+ return result;
1086
1138
  }
1087
1139
  const words = query.toLowerCase().split(/\s+/).filter(w => w.length > 0);
1088
1140
  if (words.length === 0) {
@@ -1097,7 +1149,7 @@ class N8NDocumentationMCPServer {
1097
1149
  LIMIT ?
1098
1150
  `).all(...params);
1099
1151
  const rankedNodes = this.rankSearchResults(nodes, query, limit);
1100
- return {
1152
+ const result = {
1101
1153
  query,
1102
1154
  results: rankedNodes.map(node => ({
1103
1155
  nodeType: node.node_type,
@@ -1109,6 +1161,33 @@ class N8NDocumentationMCPServer {
1109
1161
  })),
1110
1162
  totalCount: rankedNodes.length
1111
1163
  };
1164
+ if (options?.includeExamples) {
1165
+ for (const nodeResult of result.results) {
1166
+ try {
1167
+ const examples = this.db.prepare(`
1168
+ SELECT
1169
+ parameters_json,
1170
+ template_name,
1171
+ template_views
1172
+ FROM template_node_configs
1173
+ WHERE node_type = ?
1174
+ ORDER BY rank
1175
+ LIMIT 2
1176
+ `).all(nodeResult.workflowNodeType);
1177
+ if (examples.length > 0) {
1178
+ nodeResult.examples = examples.map((ex) => ({
1179
+ configuration: JSON.parse(ex.parameters_json),
1180
+ template: ex.template_name,
1181
+ views: ex.template_views
1182
+ }));
1183
+ }
1184
+ }
1185
+ catch (error) {
1186
+ logger_1.logger.warn(`Failed to fetch examples for ${nodeResult.nodeType}:`, error.message);
1187
+ }
1188
+ }
1189
+ }
1190
+ return result;
1112
1191
  }
1113
1192
  calculateRelevance(node, query) {
1114
1193
  const lowerQuery = query.toLowerCase();
@@ -1363,11 +1442,11 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
1363
1442
  })),
1364
1443
  };
1365
1444
  }
1366
- async getNodeEssentials(nodeType) {
1445
+ async getNodeEssentials(nodeType, includeExamples) {
1367
1446
  await this.ensureInitialized();
1368
1447
  if (!this.repository)
1369
1448
  throw new Error('Repository not initialized');
1370
- const cacheKey = `essentials:${nodeType}`;
1449
+ const cacheKey = `essentials:${nodeType}:${includeExamples ? 'withExamples' : 'basic'}`;
1371
1450
  const cached = this.cache.get(cacheKey);
1372
1451
  if (cached)
1373
1452
  return cached;
@@ -1418,6 +1497,50 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
1418
1497
  developmentStyle: node.developmentStyle ?? 'programmatic'
1419
1498
  }
1420
1499
  };
1500
+ if (includeExamples) {
1501
+ try {
1502
+ const fullNodeType = (0, node_utils_1.getWorkflowNodeType)(node.package ?? 'n8n-nodes-base', node.nodeType);
1503
+ const examples = this.db.prepare(`
1504
+ SELECT
1505
+ parameters_json,
1506
+ template_name,
1507
+ template_views,
1508
+ complexity,
1509
+ use_cases,
1510
+ has_credentials,
1511
+ has_expressions
1512
+ FROM template_node_configs
1513
+ WHERE node_type = ?
1514
+ ORDER BY rank
1515
+ LIMIT 3
1516
+ `).all(fullNodeType);
1517
+ if (examples.length > 0) {
1518
+ result.examples = examples.map((ex) => ({
1519
+ configuration: JSON.parse(ex.parameters_json),
1520
+ source: {
1521
+ template: ex.template_name,
1522
+ views: ex.template_views,
1523
+ complexity: ex.complexity
1524
+ },
1525
+ useCases: ex.use_cases ? JSON.parse(ex.use_cases).slice(0, 2) : [],
1526
+ metadata: {
1527
+ hasCredentials: ex.has_credentials === 1,
1528
+ hasExpressions: ex.has_expressions === 1
1529
+ }
1530
+ }));
1531
+ result.examplesCount = examples.length;
1532
+ }
1533
+ else {
1534
+ result.examples = [];
1535
+ result.examplesCount = 0;
1536
+ }
1537
+ }
1538
+ catch (error) {
1539
+ logger_1.logger.warn(`Failed to fetch examples for ${nodeType}:`, error.message);
1540
+ result.examples = [];
1541
+ result.examplesCount = 0;
1542
+ }
1543
+ }
1421
1544
  this.cache.set(cacheKey, result, 3600);
1422
1545
  return result;
1423
1546
  }
@@ -1463,37 +1586,6 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
1463
1586
  searchedIn: allProperties.length + ' properties'
1464
1587
  };
1465
1588
  }
1466
- async getNodeForTask(task) {
1467
- const template = task_templates_1.TaskTemplates.getTaskTemplate(task);
1468
- if (!template) {
1469
- const similar = task_templates_1.TaskTemplates.searchTasks(task);
1470
- throw new Error(`Unknown task: ${task}. ` +
1471
- (similar.length > 0
1472
- ? `Did you mean: ${similar.slice(0, 3).join(', ')}?`
1473
- : `Use 'list_tasks' to see available tasks.`));
1474
- }
1475
- return {
1476
- task: template.task,
1477
- description: template.description,
1478
- nodeType: template.nodeType,
1479
- configuration: template.configuration,
1480
- userMustProvide: template.userMustProvide,
1481
- optionalEnhancements: template.optionalEnhancements || [],
1482
- notes: template.notes || [],
1483
- example: {
1484
- node: {
1485
- type: template.nodeType,
1486
- parameters: template.configuration
1487
- },
1488
- userInputsNeeded: template.userMustProvide.map(p => ({
1489
- property: p.property,
1490
- currentValue: this.getPropertyValue(template.configuration, p.property),
1491
- description: p.description,
1492
- example: p.example
1493
- }))
1494
- }
1495
- };
1496
- }
1497
1589
  getPropertyValue(config, path) {
1498
1590
  const parts = path.split('.');
1499
1591
  let value = config;