mcp-jvm-diagnostics 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ A Model Context Protocol (MCP) server that gives AI assistants the ability to an
9
9
 
10
10
  JVM diagnostic MCP servers exist (TDA, jfr-mcp, Arthas) — but they're all **Java-based**, requiring a JVM runtime just to diagnose JVM problems. This tool runs on **Node.js** via `npx` — no JVM, no Docker, no SSH.
11
11
 
12
- It analyzes **offline** artifacts (thread dumps, GC logs, heap histograms) rather than requiring a running JVM. Paste a thread dump or GC log and get instant analysis. Part of the [MCP Java Backend Suite](https://www.npmjs.com/package/mcp-java-backend-suite) — 5 tools covering databases, JVM, migrations, Spring Boot, and Redis for Java backend developers.
12
+ It analyzes **offline** artifacts (thread dumps, GC logs, heap histograms) rather than requiring a running JVM. Paste a thread dump or GC log and get instant analysis.
13
13
 
14
14
  ## Features
15
15
 
@@ -160,6 +160,13 @@ Add these JVM flags to your application:
160
160
  - **HotSpot/OpenJDK only**: Parser targets HotSpot/OpenJDK thread dump format. GraalVM native-image or Eclipse OpenJ9 dumps may parse incompletely.
161
161
  - **Classloader leak detection**: Heap analysis flags growing ClassLoader instances but cannot definitively prove a leak without memory profiler data.
162
162
 
163
+ ## Part of the MCP Java Backend Suite
164
+
165
+ - [mcp-db-analyzer](https://www.npmjs.com/package/mcp-db-analyzer) — PostgreSQL/MySQL/SQLite schema analysis
166
+ - [mcp-spring-boot-actuator](https://www.npmjs.com/package/mcp-spring-boot-actuator) — Spring Boot health, metrics, and bean analysis
167
+ - [mcp-redis-diagnostics](https://www.npmjs.com/package/mcp-redis-diagnostics) — Redis memory, slowlog, and client diagnostics
168
+ - [mcp-migration-advisor](https://www.npmjs.com/package/mcp-migration-advisor) — Flyway/Liquibase migration risk analysis
169
+
163
170
  ## License
164
171
 
165
172
  MIT
package/build/index.js CHANGED
@@ -12,7 +12,7 @@ import { compareHeapHistos } from "./analyzers/heap-diff.js";
12
12
  import { parseJfrSummary } from "./parsers/jfr-summary.js";
13
13
  // Handle --help
14
14
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
15
- console.log(`mcp-jvm-diagnostics v0.1.0 — MCP server for JVM diagnostics
15
+ console.log(`mcp-jvm-diagnostics v0.1.5 — MCP server for JVM diagnostics
16
16
 
17
17
  Usage:
18
18
  mcp-jvm-diagnostics [options]
@@ -21,8 +21,8 @@ Options:
21
21
  --help, -h Show this help message
22
22
 
23
23
  Tools provided:
24
- analyze_thread_dump Parse thread dump, detect deadlocks and contention
25
- analyze_gc_log Parse GC log, detect pressure and tuning opportunities
24
+ analyze_thread_dump Parse thread dump, detect deadlocks and contention (platform + virtual threads)
25
+ analyze_gc_log Parse GC log, detect pressure and tuning (G1, ZGC, Parallel, Shenandoah)
26
26
  analyze_heap_histo Parse jmap -histo output, detect memory leak candidates
27
27
  compare_heap_histos Compare two jmap histos to detect memory growth
28
28
  analyze_jfr Parse JFR summary output, detect performance hotspots
@@ -31,10 +31,10 @@ Tools provided:
31
31
  }
32
32
  const server = new McpServer({
33
33
  name: "mcp-jvm-diagnostics",
34
- version: "0.1.0",
34
+ version: "0.1.4",
35
35
  });
36
36
  // --- Tool: analyze_thread_dump ---
37
- server.tool("analyze_thread_dump", "Parse a JVM thread dump (jstack output) and analyze thread states, detect deadlocks, identify lock contention hotspots, and find thread starvation patterns.", {
37
+ server.tool("analyze_thread_dump", "Parse a JVM thread dump (jstack output) and analyze thread states, detect deadlocks, identify lock contention hotspots, and find thread starvation patterns. Handles both platform threads and virtual threads (Java 21+).", {
38
38
  thread_dump: z
39
39
  .string()
40
40
  .describe("The full thread dump text (from jstack, kill -3, or VisualVM)"),
@@ -121,7 +121,7 @@ server.tool("analyze_thread_dump", "Parse a JVM thread dump (jstack output) and
121
121
  }
122
122
  });
123
123
  // --- Tool: analyze_gc_log ---
124
- server.tool("analyze_gc_log", "Parse a JVM GC log and analyze garbage collection patterns, pause times, allocation rates, and memory pressure. Supports G1, ZGC, and Parallel GC formats.", {
124
+ server.tool("analyze_gc_log", "Parse a JVM GC log and analyze garbage collection patterns, pause times, allocation rates, and memory pressure. Supports G1, ZGC, Parallel, Serial, and Shenandoah GC formats.", {
125
125
  gc_log: z
126
126
  .string()
127
127
  .describe("The GC log text (from -Xlog:gc* or -verbose:gc)"),
@@ -14,6 +14,14 @@ const UNIFIED_CONCURRENT_RE = /\[(\d+[.,]\d+)s\].*?GC\(\d+\)\s+(Concurrent\s+\S+
14
14
  const LEGACY_GC_RE = /\[(Full )?GC\s*\(([^)]+)\)\s+(\d+)K->(\d+)K\((\d+)K\),\s+(\d+[.,]\d+)\s+secs\]/;
15
15
  // ZGC format: [0.123s][info][gc] GC(0) Garbage Collection (Warmup) 24M(1%)->8M(0%) 1.234ms
16
16
  const ZGC_RE = /\[(\d+[.,]\d+)s\].*?GC\(\d+\)\s+Garbage Collection\s+\([^)]+\)\s+(\d+)M\(\d+%\)->(\d+)M\(\d+%\)\s+(\d+[.,]\d+)ms/;
17
+ // Shenandoah pause format: [0.521s][info][gc] GC(0) Pause Init Mark 2.606ms
18
+ // Pauses include: Pause Init Mark, Pause Final Mark, Pause Init Update Refs, Pause Final Update Refs, Pause Full
19
+ const SHENANDOAH_PAUSE_RE = /\[(\d+[.,]\d+)s\].*?GC\(\d+\)\s+(Pause\s+(?:Init|Final)\s+\S+(?:\s+\S+)?|Pause Full(?:\s+\([^)]*\))?)\s+(\d+[.,]\d+)ms/;
20
+ // Parallel GC verbose:gc with region detail (-verbose:gc on Java 8):
21
+ // [GC (Allocation Failure) [PSYoungGen: 65536K->5432K(76288K)] 65536K->5432K(251392K), 0.0123456 secs]
22
+ // [Full GC (Ergonomics) [PSYoungGen: 5K->0K(76K)] [ParOldGen: 100K->50K(175K)] 105K->50K(251K), [Metaspace: 10K->10K(1056K)], 0.5679 secs]
23
+ // The (?:\s*\[[^\]]+\])+ skips one or more region detail blocks (PSYoungGen, ParOldGen, Metaspace before secs).
24
+ const PARALLEL_REGION_GC_RE = /\[(Full\s+)?GC\s*\([^)]+\)(?:\s*\[[^\]]+\])+\s*(\d+)K->(\d+)K\((\d+)K\),(?:\s*\[[^\]]+\],)?\s*(\d+[.,]\d+)\s+secs/;
17
25
  export function parseGcLog(text) {
18
26
  const lines = text.replace(/\r\n/g, "\n").split("\n");
19
27
  const events = [];
@@ -75,6 +83,19 @@ export function parseGcLog(text) {
75
83
  });
76
84
  continue;
77
85
  }
86
+ // Try Shenandoah pause format (no heap sizes in pause events)
87
+ const shenandoahMatch = line.match(SHENANDOAH_PAUSE_RE);
88
+ if (shenandoahMatch) {
89
+ events.push({
90
+ timestamp: parseFloat(shenandoahMatch[1].replace(",", ".")),
91
+ type: shenandoahMatch[2].trim(),
92
+ pauseMs: parseFloat(shenandoahMatch[3].replace(",", ".")),
93
+ heapBeforeMb: 0,
94
+ heapAfterMb: 0,
95
+ heapTotalMb: 0,
96
+ });
97
+ continue;
98
+ }
78
99
  // Try legacy format
79
100
  const legacyMatch = line.match(LEGACY_GC_RE);
80
101
  if (legacyMatch) {
@@ -87,6 +108,20 @@ export function parseGcLog(text) {
87
108
  heapAfterMb: Math.round(parseInt(legacyMatch[4], 10) / 1024),
88
109
  heapTotalMb: Math.round(parseInt(legacyMatch[5], 10) / 1024),
89
110
  });
111
+ continue;
112
+ }
113
+ // Try Parallel GC verbose:gc with PSYoungGen/ParOldGen region blocks
114
+ const parallelRegionMatch = line.match(PARALLEL_REGION_GC_RE);
115
+ if (parallelRegionMatch) {
116
+ const isFull = !!parallelRegionMatch[1];
117
+ events.push({
118
+ timestamp: 0,
119
+ type: isFull ? "Pause Full" : "Pause Young",
120
+ pauseMs: parseFloat(parallelRegionMatch[5].replace(",", ".")) * 1000,
121
+ heapBeforeMb: Math.round(parseInt(parallelRegionMatch[2], 10) / 1024),
122
+ heapAfterMb: Math.round(parseInt(parallelRegionMatch[3], 10) / 1024),
123
+ heapTotalMb: Math.round(parseInt(parallelRegionMatch[4], 10) / 1024),
124
+ });
90
125
  }
91
126
  }
92
127
  // Calculate time span
@@ -110,6 +110,11 @@ function analyzeJfrEvents(events, totalEvents, totalSize) {
110
110
  issues.push(`${monitorEnter.count.toLocaleString()} monitor enter events — significant lock contention.`);
111
111
  recommendations.push("Use `analyze_thread_dump` to identify contention hotspots and consider lock-free alternatives.");
112
112
  }
113
+ // Check for threads spending time in Object.wait() — indicates producer-consumer imbalance
114
+ if (monitorWait && monitorWait.count > 2000) {
115
+ issues.push(`${monitorWait.count.toLocaleString()} Object.wait() events — threads frequently waiting for notifications. Producers may be slower than consumers.`);
116
+ recommendations.push("Profile the producer side of producer-consumer queues. Consider bounded queues with backpressure or increasing producer thread count.");
117
+ }
113
118
  // Check for thread starts (churn)
114
119
  const threadStart = byName.get("jdk.ThreadStart");
115
120
  if (threadStart && threadStart.count > 200) {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "mcp-jvm-diagnostics",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "MCP server for JVM diagnostics — analyze thread dumps, detect deadlocks, parse GC logs, and get JVM tuning recommendations",
5
+ "mcpName": "io.github.dmitriusan/mcp-jvm-diagnostics",
5
6
  "author": "Dmytro Lisnichenko",
6
7
  "type": "module",
7
8
  "main": "./build/index.js",