mcp-jvm-diagnostics 0.1.6 → 0.1.8

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.
@@ -26,7 +26,16 @@ export function analyzeGcPressure(log) {
26
26
  result.maxPauseMs = pauses[pauses.length - 1];
27
27
  result.totalPauseMs = pauses.reduce((a, b) => a + b, 0);
28
28
  result.avgPauseMs = result.totalPauseMs / pauses.length;
29
- result.p95PauseMs = pauses[Math.floor(pauses.length * 0.95)] || result.maxPauseMs;
29
+ // Linear interpolation for P95: Math.floor(n * 0.95) returns index n-1 whenever
30
+ // n <= 20, making P95 equal the max. Using (n-1) * 0.95 as the fractional index
31
+ // and interpolating between adjacent elements gives accurate results at all sizes.
32
+ const p95Idx = (pauses.length - 1) * 0.95;
33
+ const p95Lo = Math.floor(p95Idx);
34
+ const p95Hi = Math.ceil(p95Idx);
35
+ result.p95PauseMs =
36
+ p95Lo === p95Hi
37
+ ? pauses[p95Lo]
38
+ : pauses[p95Lo] + (pauses[p95Hi] - pauses[p95Lo]) * (p95Idx - p95Lo);
30
39
  // GC overhead
31
40
  if (log.timeSpanMs > 0) {
32
41
  result.gcOverheadPct = (result.totalPauseMs / log.timeSpanMs) * 100;
@@ -70,8 +79,10 @@ function detectIssues(log, result) {
70
79
  result.issues.push(`Heap after GC is growing over time (${avgFirst.toFixed(0)}MB → ${avgSecond.toFixed(0)}MB) — possible memory leak.`);
71
80
  }
72
81
  }
73
- // Low reclaim ratio
74
- if (result.heapBeforeMb > 0 && result.heapAfterMb > 0) {
82
+ // Low reclaim ratio — only meaningful when heap actually shrank after GC.
83
+ // Guard heapAfterMb < heapBeforeMb to avoid a negative reclaim percentage when
84
+ // averaged heap-after exceeds heap-before (e.g. growing heap events skewing averages).
85
+ if (result.heapBeforeMb > 0 && result.heapAfterMb > 0 && result.heapAfterMb < result.heapBeforeMb) {
75
86
  const reclaimPct = ((result.heapBeforeMb - result.heapAfterMb) / result.heapBeforeMb) * 100;
76
87
  if (reclaimPct < 10) {
77
88
  result.issues.push(`GC reclaims only ${reclaimPct.toFixed(0)}% of heap per collection — most objects survive. Heap may be too small or there's a memory leak.`);
@@ -97,8 +108,8 @@ function generateRecommendations(log, result) {
97
108
  if (fullGcCount > 3) {
98
109
  result.recommendations.push("Frequent Full GCs indicate heap pressure. Increase -Xmx or tune -XX:InitiatingHeapOccupancyPercent (G1) to start concurrent marking earlier.");
99
110
  }
100
- // Low reclaim → check for leaks
101
- if (result.heapBeforeMb > 0) {
111
+ // Low reclaim → check for leaks (same guard as detectIssues: skip when averages are inverted)
112
+ if (result.heapBeforeMb > 0 && result.heapAfterMb < result.heapBeforeMb) {
102
113
  const reclaimPct = ((result.heapBeforeMb - result.heapAfterMb) / result.heapBeforeMb) * 100;
103
114
  if (reclaimPct < 10) {
104
115
  result.recommendations.push("Very low reclaim ratio suggests most objects are long-lived. Take a heap dump and analyze with Eclipse MAT or jmap -histo to identify memory leaks.");
package/build/index.js CHANGED
@@ -13,7 +13,7 @@ import { parseJfrSummary } from "./parsers/jfr-summary.js";
13
13
  import { generateReportFromThreadDump, analyzeThreadDumpMarkdown, } from "./reporting.js";
14
14
  // Handle --help
15
15
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
16
- console.log(`mcp-jvm-diagnostics v0.1.6 — MCP server for JVM diagnostics
16
+ console.log(`mcp-jvm-diagnostics v0.1.8 — MCP server for JVM diagnostics
17
17
 
18
18
  Usage:
19
19
  mcp-jvm-diagnostics [options]
@@ -33,10 +33,10 @@ Tools provided:
33
33
  }
34
34
  const server = new McpServer({
35
35
  name: "mcp-jvm-diagnostics",
36
- version: "0.1.6",
36
+ version: "0.1.8",
37
37
  });
38
38
  // --- Tool: analyze_thread_dump ---
39
- 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+).", {
39
+ 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+). Note: deadlock detection covers synchronized monitor locks only — java.util.concurrent.locks.ReentrantLock and other j.u.c lock types do not expose their waiters in thread dump lock info and will not be detected.", {
40
40
  thread_dump: z
41
41
  .string()
42
42
  .describe("The full thread dump text (from jstack, kill -3, or VisualVM)"),
@@ -170,7 +170,7 @@ server.tool("analyze_gc_log", "Parse a JVM GC log and analyze garbage collection
170
170
  server.tool("analyze_heap_histo", "Parse jmap -histo output and detect memory leak candidates, object creation hotspots, classloader leaks, and heap composition issues.", {
171
171
  histo: z
172
172
  .string()
173
- .describe("The jmap -histo output text (from jmap -histo <pid> or jmap -histo:live <pid>)"),
173
+ .describe("The jmap -histo output text. Use `jmap -histo:live <pid>` for leak detection — the :live flag forces a full GC first so only reachable objects appear, giving the clearest signal for leaks. Use `jmap -histo <pid>` (without :live) for a cheaper snapshot that includes unreachable objects not yet collected."),
174
174
  }, async ({ histo }) => {
175
175
  try {
176
176
  const report = parseHeapHisto(histo);
@@ -282,10 +282,10 @@ function formatBytes(bytes) {
282
282
  return `${sign}${abs} B`;
283
283
  }
284
284
  // --- Tool: analyze_jfr ---
285
- server.tool("analyze_jfr", "Parse JDK Flight Recorder summary output (from `jfr summary <file>`) and analyze event distribution, detect performance hotspots, GC pressure, lock contention, I/O patterns, and excessive allocations.", {
285
+ server.tool("analyze_jfr", "Parse JDK Flight Recorder summary output and analyze event distribution, detect performance hotspots, GC pressure, lock contention, I/O patterns, and excessive allocations. Input must be the text printed to stdout by `jfr summary <recording.jfr>` — not the binary .jfr file itself.", {
286
286
  jfr_summary: z
287
287
  .string()
288
- .describe("The output text from `jfr summary <recording.jfr>`"),
288
+ .describe("The stdout text from running `jfr summary <recording.jfr>` (a text table of event types, counts, and sizes — not the binary .jfr file)"),
289
289
  }, async ({ jfr_summary }) => {
290
290
  try {
291
291
  const summary = parseJfrSummary(jfr_summary);
@@ -6,7 +6,7 @@ import { analyzeContention } from "./analyzers/contention.js";
6
6
  import { detectDeadlocks } from "./analyzers/deadlock.js";
7
7
  import { parseThreadDump } from "./parsers/thread-dump.js";
8
8
  const PRODUCT_NAME = "jvm-diagnostics";
9
- const TOOL_VERSION = "0.1.6";
9
+ const TOOL_VERSION = "0.1.7";
10
10
  export const MISSING_LICENSE_INPUT_ERROR = "Missing required input: license_key";
11
11
  export const MISSING_THREAD_DUMP_ERROR = "Missing required input: thread_dump";
12
12
  export const MISSING_ENV_LICENSE_ERROR = "MCP_LICENSE_KEY environment variable is not set. generate_report requires a valid Pro license key. Free-tier analysis tools remain available without it.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-jvm-diagnostics",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "MCP server for JVM diagnostics — analyze thread dumps, detect deadlocks, parse GC logs, and get JVM tuning recommendations",
5
5
  "mcpName": "io.github.dmitriusan/mcp-jvm-diagnostics",
6
6
  "author": "Dmytro Lisnichenko",
@@ -42,7 +42,7 @@
42
42
  ],
43
43
  "license": "MIT",
44
44
  "engines": {
45
- "node": ">=18.0.0"
45
+ "node": ">=20.0.0"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",
@@ -53,13 +53,13 @@
53
53
  "url": "https://github.com/Dmitriusan/mcp-jvm-diagnostics/issues"
54
54
  },
55
55
  "dependencies": {
56
- "@mcp-java-suite/license": "file:../../mcp-suite-license/mcp-suite-license",
56
+ "@mcp-java-suite/license": "npm:mcp-java-suite-license@^0.1.0",
57
57
  "@modelcontextprotocol/sdk": "^1.27.1",
58
- "zod": "^3.24.2"
58
+ "zod": "^4.0.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/node": "^22.0.0",
62
- "typescript": "^5.8.2",
63
- "vitest": "^4.0.18"
62
+ "typescript": "^6.0.0",
63
+ "vitest": "^4.1.0"
64
64
  }
65
65
  }