v8-cpu-profile-decoder-mcp

Decodifica perfiles de CPU de V8 en resúmenes de flame graph y hotspots para agentes de IA

Documentación

v8-cpu-profile-decoder-mcp 🐸⚡

npm version npm downloads CI License: MIT

Un servidor MCP que decodifica perfiles de CPU de V8 en resúmenes de cuellos de botella eficientes en tokens para agentes de IA.

Tu aplicación Node.js es lenta. Ejecutaste --cpu-prof. Ahora tienes un archivo .cpuprofile de 20MB — y tu agente de IA es completamente ciego a él.


🤔 El Problema

Los perfiles de CPU de V8 son masivos. Un .cpuprofile típico de una aplicación Node.js en producción es de 5 a 50MB de JSON crudo — millones de líneas que mapean direcciones de memoria, conteos de ticks y secuencias de ejecución en microsegundos. Se ve así:

{
  "nodes": [
    { "id": 1482, "callFrame": { "functionName": "processRequest", "url": "file:///app/dist/server.js", "lineNumber": 847 }, "hitCount": 3241, "children": [1483, 1490] },
    ...
  ],
  "samples": [1482, 1483, 1482, 1490, 1482, ...],
  "timeDeltas": [120, 98, 115, 102, ...]
}

Un agente de IA que intente leer este archivo colapsa instantáneamente su ventana de contexto y falla. Incluso si pudiera leerlo, no puede ejecutar los algoritmos de agregación necesarios para calcular los tiempos de CPU inclusivos/exclusivos a través del árbol de llamadas.

Entonces, cuando le preguntas a tu agente:

  • 🙈 "¿Qué función está consumiendo más CPU?"
  • 🙈 "¿Qué está llamando a mi consulta de base de datos lenta?"
  • 🙈 "¿De qué archivo TypeScript proviene realmente el cuello de botella?"

...está adivinando. No tiene acceso a los datos de perfilado.

v8-cpu-profile-decoder-mcp soluciona eso. Decodifica el perfil localmente y entrega al agente un resumen semántico de 10 líneas en lugar de un archivo de 50MB.


🛠️ Herramientas

extract_hottest_functions

Analiza el .cpuprofile y devuelve las N funciones principales clasificadas por tiempo de CPU exclusivo (tiempo propio). Filtra los internos de V8 y los componentes integrados de Node.js — solo código de usuario.

{
  "profile_path": "/app/profiles/CPU.20260516.cpuprofile",
  "top_n": 5,
  "min_self_percent": 1.0
}
[
  {
    "rank": 1,
    "functionName": "hashPassword",
    "url": "file:///app/dist/auth/crypto.js",
    "lineNumber": 42,
    "selfTimeMs": 1842.5,
    "totalTimeMs": 1842.5,
    "selfPercent": 61.32,
    "totalPercent": 61.32,
    "hitCount": 3241
  },
  {
    "rank": 2,
    "functionName": "parseJsonBody",
    "url": "file:///app/dist/middleware/body.js",
    "lineNumber": 18,
    "selfTimeMs": 412.1,
    "totalTimeMs": 412.1,
    "selfPercent": 13.71,
    "totalPercent": 13.71,
    "hitCount": 724
  }
]

analyze_call_tree_path

Encuentra todos los llamadores de una función específica y muestra con qué frecuencia cada uno la invocó. Acepta coincidencia parcial de nombres de funciones sin distinción de mayúsculas/minúsculas.

{
  "profile_path": "/app/profiles/CPU.20260516.cpuprofile",
  "function_name": "hashPassword",
  "top_callers": 3
}
{
  "targetFunction": "hashPassword",
  "matchedNodes": 2,
  "totalSelfTimeMs": 1842.5,
  "totalPercent": 61.32,
  "callers": [
    {
      "functionName": "loginHandler",
      "url": "file:///app/dist/routes/auth.js",
      "lineNumber": 94,
      "callCount": 2180,
      "selfTimeMs": 240.1
    },
    {
      "functionName": "validateSession",
      "url": "file:///app/dist/middleware/auth.js",
      "lineNumber": 31,
      "callCount": 1061,
      "selfTimeMs": 116.8
    }
  ]
}

correlate_source_code

Mapea los cuellos de botella de JS compilado de vuelta a sus ubicaciones originales en código fuente TypeScript usando archivos .js.map. Se degrada elegantemente a ubicaciones de JS compilado si no se encuentra un mapa de origen.

{
  "profile_path": "/app/profiles/CPU.20260516.cpuprofile",
  "top_n": 5
}
{
  "resolved": [
    {
      "rank": 1,
      "generatedUrl": "file:///app/dist/auth/crypto.js",
      "generatedLine": 42,
      "source": {
        "originalFile": "src/auth/crypto.ts",
        "originalLine": 38,
        "originalColumn": 2,
        "originalFunction": "hashPassword"
      },
      "selfTimeMs": 1842.5,
      "selfPercent": 61.32
    }
  ],
  "sourcemapErrors": []
}

analyze_gc_pressure

Informa la sobrecarga de recolección de basura como porcentaje de la duración del perfilado, desglosada por tipo de GC. Señala cuando el GC excede un umbral configurable y proporciona una recomendación específica.

{
  "profile_path": "/app/profiles/CPU.cpuprofile",
  "threshold_percent": 10
}
{
  "gc_ticks": 184,
  "total_ticks": 1240,
  "gc_percentage": 14.84,
  "gc_type_breakdown": {
    "scavenger": 122,
    "mark_sweep": 0,
    "mark_compact": 0,
    "incremental": 62,
    "generic": 0
  },
  "exceeds_threshold": true,
  "threshold_percent": 10,
  "verdict": "GC consumed 14.84% of CPU — exceeds the 10% threshold. Dominated by Scavenger (short-lived object pressure). Consider object pooling, reusing buffers, or reducing closure captures."
}

diff_profiles

Compara dos archivos .cpuprofile (antes/después de una optimización) y devuelve los deltas de tiempo de CPU por función, normalizados contra la duración total de cada perfil. Los marcos se emparejan por coordenadas de marco de llamada, no por IDs de nodo transitorios, por lo que la alineación es estable entre sesiones de perfilado.

{
  "before_profile_path": "/app/profiles/before.cpuprofile",
  "after_profile_path": "/app/profiles/after.cpuprofile",
  "top_n": 5
}
{
  "before_duration_ms": 5000,
  "after_duration_ms": 4800,
  "total_execution_delta_ms": -200,
  "total_execution_delta_percent": -4,
  "top_improvements": [
    {
      "function_name": "hashPassword",
      "url": "file:///app/dist/auth/crypto.js",
      "line_number": 42,
      "before_ms": 1842.5,
      "after_ms": 620.1,
      "absolute_diff_ms": -1222.4,
      "relative_diff_percent": -66.34
    }
  ],
  "top_regressions": [],
  "only_in_before": [],
  "only_in_after": []
}

analyze_async_bottlenecks

Detecta la sobrecarga del bucle de eventos identificando marcos internos de V8 que representan maquinaria asíncrona — procesamiento de cola de microtareas, saturación de nextTick y callbacks de temporizadores/inmediatos.

{
  "profile_path": "/app/profiles/CPU.cpuprofile",
  "threshold_percent": 10
}
{
  "total_ticks": 1240,
  "async_ticks": 186,
  "event_loop_overhead_ms": 372,
  "event_loop_overhead_percent": 15.0,
  "dominant_async_patterns": [
    { "pattern": "promise_chains", "ticks": 142, "percent": 11.45 },
    { "pattern": "nexttick_saturation", "ticks": 44, "percent": 3.55 }
  ],
  "verdict": "Event-loop overhead is 15.0% of CPU — exceeds the 10% threshold. Promise chain overhead is visible in the profile. Consider batching microtasks, using Promise.all() to parallelise I/O, or offloading CPU-bound continuations to worker threads."
}

🚀 Instalación

npx v8-cpu-profile-decoder-mcp

O instalar globalmente:

npm install -g v8-cpu-profile-decoder-mcp

Generar un perfil de CPU en Node.js

# Single run
node --cpu-prof your-script.js

# With custom output dir
node --cpu-prof --cpu-prof-dir ./profiles your-script.js

O programáticamente a través de Chrome DevTools → pestaña Performance → Record.

Configuración de Claude Desktop

{
  "mcpServers": {
    "v8-cpu-profile-decoder-mcp": {
      "command": "npx",
      "args": ["-y", "v8-cpu-profile-decoder-mcp"]
    }
  }
}

💡 Ejemplos de Prompts para Agentes

"Aquí está mi perfil de CPU en /app/profiles/CPU.cpuprofile — ¿qué función está consumiendo más CPU?"

"Encuentra qué está llamando a processRequest en este perfil y con qué frecuencia"

"Mapea las 10 funciones más calientes de vuelta a sus archivos TypeScript originales"

"Mi API de Node.js es lenta bajo carga — el perfil está en /tmp/CPU.cpuprofile, encuentra el cuello de botella"

"¿Es el GC el cuello de botella? Revisa el perfil en /tmp/CPU.cpuprofile y dime qué tipo de asignación lo está causando"

"Compara estos dos perfiles antes y después de mi optimización — ¿qué funciones mejoraron y cuáles empeoraron?"

"¿Está esta aplicación gastando demasiada CPU en sobrecarga asíncrona y maquinaria del bucle de eventos?"


🔗 Proyectos Relacionados


📄 Licencia

MIT © vola-trebla