UNPKG

ai-debug-local-mcp

Version:

🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

258 lines 12.5 kB
import { BaseToolHandler } from '../base-handler.js'; /** * Handler for Next.js cache monitoring tools (cache inspector, ISR monitor) */ export class NextJSCacheMonitoringHandler extends BaseToolHandler { nextjsEngine; constructor(nextjsEngine) { super(); this.nextjsEngine = nextjsEngine; } tools = [ { name: 'nextjs_cache_inspector', description: 'Inspect Next.js caching layers including Data Cache, Full Route Cache, and Client Router Cache with hit rates.', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID' } }, required: ['sessionId'] } }, { name: 'nextjs_isr_monitor', description: 'Monitor Incremental Static Regeneration (ISR) including cache status, revalidation times, and stale content.', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID' }, route: { type: 'string', description: 'Specific route to monitor (optional)' } }, required: ['sessionId'] } } ]; async handle(toolName, args, sessions) { const session = sessions.get(args.sessionId); if (!session) { throw new Error(`Debug session ${args.sessionId} not found`); } // Validate this is a Next.js app if (session.framework !== 'nextjs' && session.framework !== 'next' && !(session.framework === 'react' && session.isNextJS)) { throw new Error('This tool only works with Next.js applications'); } switch (toolName) { case 'nextjs_cache_inspector': return this.inspectCache(args, session); case 'nextjs_isr_monitor': return this.monitorISR(args, session); default: throw new Error(`Unknown Next.js cache monitoring tool: ${toolName}`); } } async inspectCache(args, session) { try { const cacheInfo = await this.nextjsEngine.getCacheInspector(); let content = '🗄️ **Next.js Cache Inspector**\n\n'; // Check if caches are empty if (cacheInfo.dataCache.entries === 0 && cacheInfo.fullRouteCache.cachedRoutes === 0 && cacheInfo.clientRouterCache.entries === 0) { content += '⚠️ **Caches are empty**\n\n'; content += 'This could mean:\n'; content += '- Development mode (caching disabled)\n'; content += '- Application just started\n'; content += '- Caches were recently cleared\n\n'; content += 'In production, you should see cache activity.\n'; return { content: [{ type: 'text', text: content.trim() }] }; } // Data Cache content += '### 📊 Data Cache\n'; content += `- **Size:** ${cacheInfo.dataCache.size}\n`; content += `- **Entries:** ${cacheInfo.dataCache.entries}\n`; content += `- **Hit Rate:** ${cacheInfo.dataCache.hitRate}\n\n`; // Full Route Cache content += '### 🛣️ Full Route Cache\n'; content += `- **Cached Routes:** ${cacheInfo.fullRouteCache.cachedRoutes}\n`; content += `- **Average Age:** ${cacheInfo.fullRouteCache.avgAge}\n\n`; // Client Router Cache content += '### 💻 Client Router Cache\n'; content += `- **Entries:** ${cacheInfo.clientRouterCache.entries}\n\n`; // Recent Revalidations if (cacheInfo.revalidations && cacheInfo.revalidations.length > 0) { content += '### 🔄 Recent Revalidations\n'; cacheInfo.revalidations.slice(0, 5).forEach((revalidation) => { const time = new Date(revalidation.timestamp).toLocaleTimeString(); content += `- **${revalidation.path}** at ${time} (${revalidation.trigger})\n`; }); content += '\n'; } // Performance Analysis content += '### 📈 Performance Analysis\n\n'; // Check cache hit rate const hitRate = parseFloat(cacheInfo.dataCache.hitRate); if (hitRate < 70) { content += '⚠️ **Low cache hit rate detected**\n'; content += 'Consider:\n'; content += '- Implementing proper cache headers\n'; content += '- Using ISR for frequently accessed pages\n'; content += '- Reviewing data fetching patterns\n\n'; } // Check cache size const sizeMatch = cacheInfo.dataCache.size.match(/(\d+)/); const sizeInMB = sizeMatch ? parseInt(sizeMatch[1]) : 0; if (sizeInMB > 500) { content += '⚠️ **Large cache size**\n'; content += 'Consider:\n'; content += '- Implementing cache eviction policies\n'; content += '- Reducing cached data size\n'; content += '- Using more granular cache keys\n\n'; } // Check entry count if (cacheInfo.dataCache.entries > 1000) { content += '⚠️ **High number of cache entries**\n'; content += 'This may impact lookup performance.\n\n'; } // Check stale cache const ageMatch = cacheInfo.fullRouteCache.avgAge.match(/(\d+)([hd])/); if (ageMatch) { const value = parseInt(ageMatch[1]); const unit = ageMatch[2]; if ((unit === 'h' && value > 24) || unit === 'd') { content += '⚠️ **Stale cache entries detected**\n'; content += 'Consider implementing revalidation strategies.\n\n'; } } // Best Practices content += '### 💡 Cache Best Practices\n\n'; content += '1. **Data Cache**: Use for external API responses\n'; content += '2. **Full Route Cache**: Static pages and ISR\n'; content += '3. **Router Cache**: Client-side navigation\n'; content += '4. **Revalidation**: Use time-based or on-demand\n'; content += '5. **Monitoring**: Track hit rates in production\n'; return { content: [{ type: 'text', text: content.trim() }] }; } catch (error) { throw new Error(`Failed to inspect Next.js caches: ${error instanceof Error ? error.message : String(error)}`); } } async monitorISR(args, session) { try { const route = args.route; const isrInfo = await this.nextjsEngine.getISRMonitor(route); let content = '♻️ **Next.js ISR Monitor**\n\n'; if (!isrInfo) { content += '❌ **No ISR routes detected**\n\n'; content += 'ISR (Incremental Static Regeneration) allows you to:\n'; content += '- Update static pages without rebuilding\n'; content += '- Keep content fresh automatically\n'; content += '- Scale to millions of pages\n\n'; content += '### How to Enable ISR\n\n'; content += '```typescript\n'; content += '// App Router\n'; content += 'export const revalidate = 60 // seconds\n\n'; content += '// Pages Router\n'; content += 'export async function getStaticProps() {\n'; content += ' return {\n'; content += ' props: { ... },\n'; content += ' revalidate: 60 // seconds\n'; content += ' }\n'; content += '}\n'; content += '```\n'; return { content: [{ type: 'text', text: content.trim() }] }; } // Route information content += `### 📍 Route: ${isrInfo.route}\n\n`; // Revalidation settings content += `- **Revalidate Interval:** ${isrInfo.revalidateInterval}s\n`; content += `- **On-Demand Revalidation:** ${isrInfo.onDemandRevalidation ? '✅ Enabled' : '❌ Disabled'}\n\n`; // Cache status const statusIcon = isrInfo.cacheStatus === 'FRESH' ? '✅' : isrInfo.cacheStatus === 'STALE' ? '⚠️' : '❌'; content += `### 💾 Cache Status: ${statusIcon} ${isrInfo.cacheStatus}\n\n`; // Last revalidation if (isrInfo.lastRevalidation) { const lastTime = new Date(isrInfo.lastRevalidation); const timeSince = Date.now() - lastTime.getTime(); const minutesSince = Math.floor(timeSince / 60000); content += `- **Last Revalidation:** ${lastTime.toLocaleString()}\n`; content += `- **Time Since:** ${minutesSince} minutes ago\n\n`; if (isrInfo.cacheStatus === 'STALE' && isrInfo.staleSinceTime) { const staleTime = new Date(isrInfo.staleSinceTime); const staleDuration = Date.now() - staleTime.getTime(); const staleMinutes = Math.floor(staleDuration / 60000); content += `⚠️ **Stale Since:** ${staleTime.toLocaleString()} (${staleMinutes} minutes)\n\n`; } } // Performance recommendations content += '### 💡 ISR Recommendations\n\n'; if (isrInfo.revalidateInterval < 30) { content += '⚠️ **Very short revalidation interval**\n'; content += 'Consider increasing to reduce server load.\n\n'; } else if (isrInfo.revalidateInterval > 3600) { content += '⚠️ **Long revalidation interval**\n'; content += 'Content may become stale. Consider reducing.\n\n'; } if (!isrInfo.onDemandRevalidation) { content += '💡 **Enable on-demand revalidation**\n'; content += 'Allow instant updates when content changes:\n\n'; content += '```typescript\n'; content += 'import { revalidatePath } from "next/cache"\n\n'; content += '// In your API route or server action\n'; content += 'revalidatePath("/blog/[slug]")\n'; content += '```\n\n'; } // ISR best practices content += '### 📚 ISR Best Practices\n\n'; content += '1. **Choose appropriate intervals** based on content freshness needs\n'; content += '2. **Implement on-demand revalidation** for instant updates\n'; content += '3. **Monitor cache hit rates** in production\n'; content += '4. **Use fallback strategies** for new pages\n'; content += '5. **Consider using PPR** for more granular updates\n\n'; // Debug helpers content += '### 🛠️ Debug ISR\n\n'; content += '```bash\n'; content += '# Check build output\n'; content += 'npm run build\n\n'; content += '# Monitor revalidations\n'; content += 'tail -f .next/server/pages-manifest.json\n'; content += '```\n'; return { content: [{ type: 'text', text: content.trim() }] }; } catch (error) { throw new Error(`Failed to monitor ISR: ${error instanceof Error ? error.message : String(error)}`); } } } //# sourceMappingURL=nextjs-cache-monitoring-handler.js.map