mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 06:22:04 +00:00
Merge pull request #283 from czlonkowski/update/n8n-and-templates-20251007
Update n8n to v1.114.3 and optimize template fetching (v2.17.2)
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
[](https://www.npmjs.com/package/n8n-mcp)
|
||||
[](https://codecov.io/gh/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp/actions)
|
||||
[](https://github.com/n8n-io/n8n)
|
||||
[](https://github.com/n8n-io/n8n)
|
||||
[](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp)
|
||||
[](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)
|
||||
|
||||
|
||||
BIN
data/nodes.db
BIN
data/nodes.db
Binary file not shown.
826
package-lock.json
generated
826
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp",
|
||||
"version": "2.17.1",
|
||||
"version": "2.17.2",
|
||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
@@ -132,15 +132,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.2",
|
||||
"@n8n/n8n-nodes-langchain": "^1.112.2",
|
||||
"@n8n/n8n-nodes-langchain": "^1.113.1",
|
||||
"@supabase/supabase-js": "^2.57.4",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"express-rate-limit": "^7.1.5",
|
||||
"lru-cache": "^11.2.1",
|
||||
"n8n": "^1.113.3",
|
||||
"n8n-core": "^1.112.1",
|
||||
"n8n-workflow": "^1.110.0",
|
||||
"n8n": "^1.114.3",
|
||||
"n8n-core": "^1.113.1",
|
||||
"n8n-workflow": "^1.111.0",
|
||||
"openai": "^4.77.0",
|
||||
"sql.js": "^1.13.0",
|
||||
"uuid": "^10.0.0",
|
||||
|
||||
@@ -417,12 +417,28 @@ async function generateTemplateMetadata(db: any, service: TemplateService) {
|
||||
} catch (error) {
|
||||
console.warn(`Failed to parse workflow for template ${t.id}:`, error);
|
||||
}
|
||||
|
||||
|
||||
// Parse nodes_used safely
|
||||
let nodes: string[] = [];
|
||||
try {
|
||||
if (t.nodes_used) {
|
||||
nodes = JSON.parse(t.nodes_used);
|
||||
// Ensure it's an array
|
||||
if (!Array.isArray(nodes)) {
|
||||
console.warn(`Template ${t.id} has invalid nodes_used (not an array), using empty array`);
|
||||
nodes = [];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to parse nodes_used for template ${t.id}:`, error);
|
||||
nodes = [];
|
||||
}
|
||||
|
||||
return {
|
||||
templateId: t.id,
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
nodes: JSON.parse(t.nodes_used),
|
||||
nodes: nodes,
|
||||
workflow
|
||||
};
|
||||
});
|
||||
|
||||
@@ -45,19 +45,22 @@ export class TemplateFetcher {
|
||||
* Fetch all templates and filter to last 12 months
|
||||
* This fetches ALL pages first, then applies date filter locally
|
||||
*/
|
||||
async fetchTemplates(progressCallback?: (current: number, total: number) => void): Promise<TemplateWorkflow[]> {
|
||||
async fetchTemplates(progressCallback?: (current: number, total: number) => void, sinceDate?: Date): Promise<TemplateWorkflow[]> {
|
||||
const allTemplates = await this.fetchAllTemplates(progressCallback);
|
||||
|
||||
// Apply date filter locally after fetching all
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setMonth(oneYearAgo.getMonth() - 12);
|
||||
|
||||
|
||||
// Use provided date or default to 12 months ago
|
||||
const cutoffDate = sinceDate || (() => {
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setMonth(oneYearAgo.getMonth() - 12);
|
||||
return oneYearAgo;
|
||||
})();
|
||||
|
||||
const recentTemplates = allTemplates.filter((w: TemplateWorkflow) => {
|
||||
const createdDate = new Date(w.createdAt);
|
||||
return createdDate >= oneYearAgo;
|
||||
return createdDate >= cutoffDate;
|
||||
});
|
||||
|
||||
logger.info(`Filtered to ${recentTemplates.length} templates from last 12 months (out of ${allTemplates.length} total)`);
|
||||
|
||||
logger.info(`Filtered to ${recentTemplates.length} templates since ${cutoffDate.toISOString().split('T')[0]} (out of ${allTemplates.length} total)`);
|
||||
return recentTemplates;
|
||||
}
|
||||
|
||||
|
||||
@@ -442,7 +442,19 @@ export class TemplateRepository {
|
||||
const rows = this.db.prepare('SELECT id FROM templates').all() as { id: number }[];
|
||||
return new Set(rows.map(r => r.id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the most recent template creation date
|
||||
* Used in update mode to fetch only newer templates
|
||||
*/
|
||||
getMostRecentTemplateDate(): Date | null {
|
||||
const result = this.db.prepare('SELECT MAX(created_at) as max_date FROM templates').get() as { max_date: string | null } | undefined;
|
||||
if (!result || !result.max_date) {
|
||||
return null;
|
||||
}
|
||||
return new Date(result.max_date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a template exists in the database
|
||||
*/
|
||||
|
||||
@@ -319,22 +319,38 @@ export class TemplateService {
|
||||
|
||||
// Get existing template IDs if in update mode
|
||||
let existingIds: Set<number> = new Set();
|
||||
let sinceDate: Date | undefined;
|
||||
|
||||
if (mode === 'update') {
|
||||
existingIds = this.repository.getExistingTemplateIds();
|
||||
logger.info(`Update mode: Found ${existingIds.size} existing templates in database`);
|
||||
|
||||
// Get most recent template date and fetch only templates from last 2 weeks
|
||||
const mostRecentDate = this.repository.getMostRecentTemplateDate();
|
||||
if (mostRecentDate) {
|
||||
// Fetch templates from 2 weeks before the most recent template
|
||||
sinceDate = new Date(mostRecentDate);
|
||||
sinceDate.setDate(sinceDate.getDate() - 14);
|
||||
logger.info(`Update mode: Fetching templates since ${sinceDate.toISOString().split('T')[0]} (2 weeks before most recent)`);
|
||||
} else {
|
||||
// No templates yet, fetch from last 2 weeks
|
||||
sinceDate = new Date();
|
||||
sinceDate.setDate(sinceDate.getDate() - 14);
|
||||
logger.info(`Update mode: No existing templates, fetching from last 2 weeks`);
|
||||
}
|
||||
} else {
|
||||
// Clear existing templates in rebuild mode
|
||||
this.repository.clearTemplates();
|
||||
logger.info('Rebuild mode: Cleared existing templates');
|
||||
}
|
||||
|
||||
|
||||
// Fetch template list
|
||||
logger.info(`Fetching template list from n8n.io (mode: ${mode})`);
|
||||
const templates = await fetcher.fetchTemplates((current, total) => {
|
||||
progressCallback?.('Fetching template list', current, total);
|
||||
});
|
||||
}, sinceDate);
|
||||
|
||||
logger.info(`Found ${templates.length} templates from last 12 months`);
|
||||
logger.info(`Found ${templates.length} templates matching date criteria`);
|
||||
|
||||
// Filter to only new templates if in update mode
|
||||
let templatesToFetch = templates;
|
||||
|
||||
@@ -79,6 +79,7 @@ describe('TemplateService', () => {
|
||||
getTemplateCount: vi.fn(),
|
||||
getTemplateStats: vi.fn(),
|
||||
getExistingTemplateIds: vi.fn(),
|
||||
getMostRecentTemplateDate: vi.fn(),
|
||||
clearTemplates: vi.fn(),
|
||||
saveTemplate: vi.fn(),
|
||||
rebuildTemplateFTS: vi.fn(),
|
||||
@@ -471,6 +472,7 @@ describe('TemplateService', () => {
|
||||
}));
|
||||
|
||||
mockRepository.getExistingTemplateIds = vi.fn().mockReturnValue(new Set([1, 2]));
|
||||
mockRepository.getMostRecentTemplateDate = vi.fn().mockReturnValue(new Date('2025-09-01'));
|
||||
mockRepository.saveTemplate = vi.fn();
|
||||
mockRepository.rebuildTemplateFTS = vi.fn();
|
||||
|
||||
@@ -498,6 +500,7 @@ describe('TemplateService', () => {
|
||||
}));
|
||||
|
||||
mockRepository.getExistingTemplateIds = vi.fn().mockReturnValue(new Set([1, 2]));
|
||||
mockRepository.getMostRecentTemplateDate = vi.fn().mockReturnValue(new Date('2025-09-01'));
|
||||
mockRepository.saveTemplate = vi.fn();
|
||||
mockRepository.rebuildTemplateFTS = vi.fn();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user