Skip to main content
Glama
mlaurel

Structured Workflow Engine MCP Server

by mlaurel
workflow-cache.ts5.17 kB
import fs from 'fs'; import path from 'path'; import { WorkflowParser, ParsedWorkflow } from './workflow-parser'; export interface WorkflowCache { workflows: ParsedWorkflow[]; lastUpdated: string; totalCount: number; categories: string[]; } export class WorkflowCacheManager { private cachePath: string; private parser: WorkflowParser; constructor(cachePath: string = './src/data/workflows.json') { this.cachePath = cachePath; this.parser = new WorkflowParser(); } /** * Generate and save workflow cache */ async generateCache(): Promise<WorkflowCache> { console.log('[Cache] Generating workflow cache...'); try { // Parse all workflows const workflows = await this.parser.parseAllWorkflows(); // Generate cache object const cache: WorkflowCache = { workflows, lastUpdated: new Date().toISOString(), totalCount: workflows.length, categories: [...new Set(workflows.map(w => w.category))] }; // Ensure cache directory exists const cacheDir = path.dirname(this.cachePath); if (!fs.existsSync(cacheDir)) { fs.mkdirSync(cacheDir, { recursive: true }); } // Save cache to file fs.writeFileSync(this.cachePath, JSON.stringify(cache, null, 2)); console.log(`[Cache] Generated cache with ${workflows.length} workflows`); console.log(`[Cache] Categories: ${cache.categories.join(', ')}`); return cache; } catch (error) { console.error('[Cache] Error generating cache:', error); throw error; } } /** * Load cache from file */ async loadCache(): Promise<WorkflowCache | null> { try { if (!fs.existsSync(this.cachePath)) { console.log('[Cache] Cache file not found, generating...'); return await this.generateCache(); } const cacheContent = fs.readFileSync(this.cachePath, 'utf-8'); const cache: WorkflowCache = JSON.parse(cacheContent); console.log(`[Cache] Loaded cache with ${cache.totalCount} workflows`); return cache; } catch (error) { console.error('[Cache] Error loading cache:', error); console.log('[Cache] Regenerating cache...'); return await this.generateCache(); } } /** * Check if cache needs refresh */ async shouldRefreshCache(): Promise<boolean> { try { if (!fs.existsSync(this.cachePath)) { return true; } const cache = await this.loadCache(); if (!cache) return true; // Check if cache is older than 1 hour (for development) const cacheAge = Date.now() - new Date(cache.lastUpdated).getTime(); const oneHour = 60 * 60 * 1000; return cacheAge > oneHour; } catch (error) { console.error('[Cache] Error checking cache freshness:', error); return true; } } /** * Get workflows with optional filtering */ async getWorkflows(category?: string): Promise<ParsedWorkflow[]> { const cache = await this.loadCache(); if (!cache) return []; if (category) { return cache.workflows.filter(w => w.category === category); } return cache.workflows; } /** * Get workflow by ID */ async getWorkflowById(id: string): Promise<ParsedWorkflow | null> { const cache = await this.loadCache(); if (!cache) return null; return cache.workflows.find(w => w.id === id) || null; } /** * Search workflows by keywords */ async searchWorkflows(query: string): Promise<{ workflow: ParsedWorkflow; score: number }[]> { const cache = await this.loadCache(); if (!cache) return []; const normalizedQuery = query.toLowerCase(); const results: { workflow: ParsedWorkflow; score: number }[] = []; cache.workflows.forEach(workflow => { let score = 0; // Check title (highest weight) if (workflow.title.toLowerCase().includes(normalizedQuery)) { score += 50; } // Check description if (workflow.description.toLowerCase().includes(normalizedQuery)) { score += 30; } // Check keywords const keywordMatches = workflow.keywords.filter(keyword => keyword.toLowerCase().includes(normalizedQuery) || normalizedQuery.includes(keyword.toLowerCase()) ); score += keywordMatches.length * 20; // Check use case and output if (workflow.use_case.toLowerCase().includes(normalizedQuery)) { score += 15; } if (workflow.output.toLowerCase().includes(normalizedQuery)) { score += 15; } // Partial matches in title/description const titleWords = workflow.title.toLowerCase().split(/\s+/); const queryWords = normalizedQuery.split(/\s+/); const titleMatches = queryWords.filter(word => titleWords.some(titleWord => titleWord.includes(word) || word.includes(titleWord)) ); score += titleMatches.length * 10; if (score > 0) { results.push({ workflow, score }); } }); // Sort by score descending return results.sort((a, b) => b.score - a.score); } }

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/mlaurel/mcp-workflow-engine'

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