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
JavaScript
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