Files
n8n-mcp/docs/MCP_V2_FINAL_PLAN.md
czlonkowski 12a255ace1 docs: add simplified MCP V2 plan focused on MVP fixes
- Create 3-day plan to fix critical issues only
- Focus on deployment, search, and deduplication
- Remove overengineered 4-week plan versions
- Add rollback strategy and test endpoints
- Total new code: ~62 lines instead of enterprise architecture

Ship the fixes, not the framework.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-16 14:27:18 +02:00

7.8 KiB

MCP V2 Final Plan - MVP Approach

Executive Summary

Forget the 4-week enterprise architecture. Here's how to fix everything in 3 days.

Day 1 (Monday) - Deploy & Debug

1. Fix Deployment (1 hour)

# Just build and push
docker build -t ghcr.io/czlonkowski/n8n-mcp:latest .
docker push ghcr.io/czlonkowski/n8n-mcp:latest

2. Add Version & Test Endpoints (30 min)

// In http-server-fixed.ts
app.get('/version', (req, res) => {
  res.json({ 
    version: '2.4.1',
    buildTime: new Date().toISOString(),
    tools: n8nDocumentationToolsFinal.map(t => t.name),
    commit: process.env.GIT_COMMIT || 'unknown'
  });
});

app.get('/test-tools', async (req, res) => {
  try {
    const result = await server.executeTool('get_node_essentials', { nodeType: 'nodes-base.httpRequest' });
    res.json({ status: 'ok', hasData: !!result, toolCount: n8nDocumentationToolsFinal.length });
  } catch (error) {
    res.json({ status: 'error', message: error.message });
  }
});

3. Debug list_nodes (1 hour)

// Add ONE line to see what's broken
private async listNodes(filters: any = {}): Promise<any> {
  console.log('DEBUG list_nodes:', { filters, query, params }); // ADD THIS
  // ... rest of code
}

4. Debug list_ai_tools (30 min)

-- Run this query to check
SELECT COUNT(*) as ai_count FROM nodes WHERE is_ai_tool = 1;
-- If 0, the column isn't populated. Fix in rebuild script.

Day 2 (Tuesday) - Core Fixes

1. Fix Multi-Word Search (2 hours)

// Replace the search function in server-update.ts
private async searchNodes(query: string, limit: number = 20): Promise<any> {
  // Split query into words
  // Handle exact phrase searches with quotes
  if (query.startsWith('"') && query.endsWith('"')) {
    const exactPhrase = query.slice(1, -1);
    const nodes = this.db!.prepare(`
      SELECT * FROM nodes 
      WHERE node_type LIKE ? OR display_name LIKE ? OR description LIKE ?
      ORDER BY display_name
      LIMIT ?
    `).all(`%${exactPhrase}%`, `%${exactPhrase}%`, `%${exactPhrase}%`, limit) as NodeRow[];
    
    return { query, results: this.formatNodeResults(nodes), totalCount: nodes.length };
  }
  
  // Split into words for normal search
  const words = query.toLowerCase().split(/\s+/).filter(w => w.length > 0);
  
  if (words.length === 0) {
    return { query, results: [], totalCount: 0 };
  }
  
  // Build conditions for each word
  const conditions = words.map(() => 
    '(node_type LIKE ? OR display_name LIKE ? OR description LIKE ?)'
  ).join(' OR ');
  
  const params = words.flatMap(w => [`%${w}%`, `%${w}%`, `%${w}%`]);
  params.push(limit);
  
  const nodes = this.db!.prepare(`
    SELECT DISTINCT * FROM nodes 
    WHERE ${conditions}
    ORDER BY display_name
    LIMIT ?
  `).all(...params) as NodeRow[];
  
  return {
    query,
    results: nodes.map(node => ({
      nodeType: node.node_type,
      displayName: node.display_name,
      description: node.description,
      category: node.category,
      package: node.package_name
    })),
    totalCount: nodes.length
  };
}

2. Simple Property Deduplication (2 hours)

// Add to PropertyFilter.ts
static deduplicateProperties(properties: any[]): any[] {
  const seen = new Map<string, any>();
  
  return properties.filter(prop => {
    // Create unique key from name + conditions
    const conditions = JSON.stringify(prop.displayOptions || {});
    const key = `${prop.name}_${conditions}`;
    
    if (seen.has(key)) {
      return false; // Skip duplicate
    }
    
    seen.set(key, prop);
    return true;
  });
}

// Use in getEssentials
static getEssentials(allProperties: any[], nodeType: string): FilteredProperties {
  // Deduplicate first
  const uniqueProperties = this.deduplicateProperties(allProperties);
  
  // ... rest of existing code
}

3. Fix Package Name Mismatch (1 hour)

// In listNodes, be more flexible with package names
if (filters.package) {
  // Handle both formats
  const packageVariants = [
    filters.package,
    `@n8n/${filters.package}`,
    filters.package.replace('@n8n/', '')
  ];
  query += ' AND package_name IN (' + packageVariants.map(() => '?').join(',') + ')';
  params.push(...packageVariants);
}

Day 3 (Wednesday) - Polish & Test

1. Add Simple Memory Cache (2 hours)

// Super simple cache - no frameworks needed
class SimpleCache {
  private cache = new Map<string, { data: any; expires: number }>();
  
  constructor() {
    // Clean up expired entries every minute
    setInterval(() => {
      const now = Date.now();
      for (const [key, item] of this.cache.entries()) {
        if (item.expires < now) this.cache.delete(key);
      }
    }, 60000);
  }
  
  get(key: string): any {
    const item = this.cache.get(key);
    if (!item || item.expires < Date.now()) {
      this.cache.delete(key);
      return null;
    }
    return item.data;
  }
  
  set(key: string, data: any, ttlSeconds: number = 300): void {
    this.cache.set(key, {
      data,
      expires: Date.now() + (ttlSeconds * 1000)
    });
  }
  
  clear(): void {
    this.cache.clear();
  }
}

// Use in server
const cache = new SimpleCache();

// Example in getNodeEssentials
const cacheKey = `essentials:${nodeType}`;
const cached = cache.get(cacheKey);
if (cached) return cached;

// ... get data ...
cache.set(cacheKey, result, 3600); // Cache for 1 hour

2. Add Basic Documentation Fallback (1 hour)

// In getNodeDocumentation
if (!node.documentation) {
  const essentials = await this.getNodeEssentials(nodeType);
  
  return {
    nodeType: node.node_type,
    displayName: node.display_name,
    documentation: `
# ${node.display_name}

${node.description || 'No description available.'}

## Common Properties

${essentials.commonProperties.map(p => 
  `### ${p.displayName}\n${p.description || `Type: ${p.type}`}`
).join('\n\n')}

## Note
Full documentation is being prepared. For now, use get_node_essentials for configuration help.
`,
    hasDocumentation: false
  };
}

3. Testing Checklist (2 hours)

  • Rebuild and deploy Docker image
  • Test all tools appear in Claude Desktop
  • Test multi-word search: "send slack message"
  • Test list_nodes with package filter
  • Test list_ai_tools returns 263 nodes
  • Verify no duplicate properties in webhook/email nodes
  • Check response times with cache

Total Time: 3 Days

Day 1: 3 hours of actual work

Day 2: 5 hours of actual work

Day 3: 5 hours of actual work

Rollback Plan

# Before starting, tag current version
docker pull ghcr.io/czlonkowski/n8n-mcp:latest
docker tag ghcr.io/czlonkowski/n8n-mcp:latest ghcr.io/czlonkowski/n8n-mcp:v2.4.0-backup

# If anything breaks, instant rollback:
docker tag ghcr.io/czlonkowski/n8n-mcp:v2.4.0-backup ghcr.io/czlonkowski/n8n-mcp:latest
docker push ghcr.io/czlonkowski/n8n-mcp:latest

What We're NOT Doing

  • No complex deployment verifiers
  • No semantic search
  • No AI documentation generators
  • No workflow assistants
  • No real-time validators
  • No enterprise caching frameworks
  • No complex service architectures

Success Metrics

  • All tools visible in Claude Desktop
  • Multi-word search works
  • No duplicate properties
  • list_nodes and list_ai_tools return results
  • Basic caching improves performance

Code Changes Summary

  1. http-server-fixed.ts: Add version endpoint (5 lines)
  2. server-update.ts: Fix search (20 lines), add debug logs (2 lines)
  3. property-filter.ts: Add deduplication (15 lines)
  4. New file: simple-cache.ts: Basic cache (20 lines)

Total new code: ~62 lines

Next Steps After MVP

Only after everything above works perfectly:

  1. Monitor actual performance - add better caching IF needed
  2. Collect user feedback on search - improve IF needed
  3. Generate better docs for AI nodes IF users complain

But not before. Ship the fixes first.