javaperf 1.0.0

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.
Files changed (57) hide show
  1. package/.cursor/mcp.json +12 -0
  2. package/README.md +185 -0
  3. package/README_RU.md +185 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +255 -0
  6. package/dist/tools/analyze_threads.d.ts +13 -0
  7. package/dist/tools/analyze_threads.js +26 -0
  8. package/dist/tools/heap_dump.d.ts +10 -0
  9. package/dist/tools/heap_dump.js +29 -0
  10. package/dist/tools/heap_histogram.d.ts +16 -0
  11. package/dist/tools/heap_histogram.js +30 -0
  12. package/dist/tools/heap_info.d.ts +10 -0
  13. package/dist/tools/heap_info.js +10 -0
  14. package/dist/tools/list_procs.d.ts +10 -0
  15. package/dist/tools/list_procs.js +11 -0
  16. package/dist/tools/parse_jfr.d.ts +16 -0
  17. package/dist/tools/parse_jfr.js +64 -0
  18. package/dist/tools/profile_frequency.d.ts +13 -0
  19. package/dist/tools/profile_frequency.js +39 -0
  20. package/dist/tools/profile_memory.d.ts +13 -0
  21. package/dist/tools/profile_memory.js +67 -0
  22. package/dist/tools/profile_time.d.ts +13 -0
  23. package/dist/tools/profile_time.js +38 -0
  24. package/dist/tools/start_profiling.d.ts +13 -0
  25. package/dist/tools/start_profiling.js +36 -0
  26. package/dist/tools/stop_profiling.d.ts +13 -0
  27. package/dist/tools/stop_profiling.js +30 -0
  28. package/dist/tools/trace_method.d.ts +22 -0
  29. package/dist/tools/trace_method.js +58 -0
  30. package/dist/tools/vm_info.d.ts +10 -0
  31. package/dist/tools/vm_info.js +19 -0
  32. package/dist/utils/jdk.d.ts +17 -0
  33. package/dist/utils/jdk.js +133 -0
  34. package/dist/utils/jfr-json.d.ts +28 -0
  35. package/dist/utils/jfr-json.js +46 -0
  36. package/dist/utils/paths.d.ts +5 -0
  37. package/dist/utils/paths.js +13 -0
  38. package/eslint.config.js +23 -0
  39. package/package.json +29 -0
  40. package/src/index.ts +322 -0
  41. package/src/tools/analyze_threads.ts +30 -0
  42. package/src/tools/heap_dump.ts +42 -0
  43. package/src/tools/heap_histogram.ts +43 -0
  44. package/src/tools/heap_info.ts +14 -0
  45. package/src/tools/list_procs.ts +15 -0
  46. package/src/tools/parse_jfr.ts +75 -0
  47. package/src/tools/profile_frequency.ts +48 -0
  48. package/src/tools/profile_memory.ts +82 -0
  49. package/src/tools/profile_time.ts +47 -0
  50. package/src/tools/start_profiling.ts +54 -0
  51. package/src/tools/stop_profiling.ts +42 -0
  52. package/src/tools/trace_method.ts +72 -0
  53. package/src/tools/vm_info.ts +26 -0
  54. package/src/utils/jdk.ts +149 -0
  55. package/src/utils/jfr-json.ts +55 -0
  56. package/src/utils/paths.ts +13 -0
  57. package/tsconfig.json +15 -0
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "javaperf": {
4
+ "command": "node",
5
+ "args": ["dist/index.js"],
6
+ "cwd": "${workspaceFolder}",
7
+ "env": {
8
+ "JAVA_HOME": "/home/sharque/.sdkman/candidates/java/current"
9
+ }
10
+ }
11
+ }
12
+ }
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # javaperf
2
+
3
+ [![npm version](https://img.shields.io/npm/v/javaperf.svg)](https://www.npmjs.com/package/javaperf)
4
+
5
+ > MCP (Model Context Protocol) server for profiling Java applications via JDK utilities (jcmd, jfr, jps)
6
+
7
+ Enables AI assistants to diagnose performance, analyze threads, and inspect JFR recordings without manual CLI usage.
8
+
9
+ 📦 **Install**: `npm install -g javaperf` or use via npx
10
+ 🌐 **npm**: https://www.npmjs.com/package/javaperf
11
+
12
+ ## Requirements
13
+
14
+ - **Node.js** v18+
15
+ - **JDK** 8u262+ or 11+ with JFR support
16
+
17
+ JDK tools (`jps`, `jcmd`, `jfr`) are auto-detected via `JAVA_HOME` or `which java`. If not found, set `JAVA_HOME` to your JDK root.
18
+
19
+ ## Quick Start
20
+
21
+ ### For Users (using npm package)
22
+
23
+ ```bash
24
+ # No installation needed - use directly in Cursor/Claude Desktop
25
+ # Just configure it as described in Integration section below
26
+ ```
27
+
28
+ ### For Developers
29
+
30
+ 1. Clone the repository:
31
+ ```bash
32
+ git clone <repo-url>
33
+ cd mcp-jperf
34
+ ```
35
+
36
+ 2. Install dependencies:
37
+ ```bash
38
+ npm install
39
+ ```
40
+
41
+ 3. Build the project:
42
+ ```bash
43
+ npm run build
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Development Mode
49
+
50
+ ```bash
51
+ npm run dev
52
+ ```
53
+
54
+ ### Production Mode
55
+
56
+ ```bash
57
+ npm start
58
+ ```
59
+
60
+ ### MCP Inspector
61
+
62
+ Debug and test with MCP Inspector:
63
+ ```bash
64
+ npx @modelcontextprotocol/inspector node dist/index.js
65
+ ```
66
+
67
+ ## Integration
68
+
69
+ ### Cursor IDE
70
+
71
+ 1. Open Cursor Settings → Features → Model Context Protocol
72
+ 2. Click "Edit Config" button
73
+ 3. Add one of the configurations below
74
+
75
+ #### Option 1: Via npm (Recommended)
76
+
77
+ Installs from npm registry automatically:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "javaperf": {
83
+ "command": "npx",
84
+ "args": ["-y", "javaperf"]
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ #### Option 2: Via npm link (Development)
91
+
92
+ For local development with live changes:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "javaperf": {
98
+ "command": "javaperf"
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ Requires: `cd /path/to/mcp-jperf && npm link -g`
105
+
106
+ #### Option 3: Direct path
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "javaperf": {
112
+ "command": "node",
113
+ "args": ["dist/index.js"],
114
+ "cwd": "${workspaceFolder}",
115
+ "env": {
116
+ "JAVA_HOME": "/path/to/your/jdk"
117
+ }
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ If `list_java_processes` fails with "jps not found", the MCP server may not inherit your shell's `JAVA_HOME`. Add the `env` block above with your JDK root path (e.g. `/usr/lib/jvm/java-17` or `~/.sdkman/candidates/java/current`).
124
+
125
+ ### Claude Desktop
126
+
127
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
128
+
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "javaperf": {
133
+ "command": "npx",
134
+ "args": ["-y", "javaperf"]
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Continue.dev
141
+
142
+ Edit `.continue/config.json`:
143
+
144
+ ```json
145
+ {
146
+ "mcpServers": {
147
+ "javaperf": {
148
+ "command": "npx",
149
+ "args": ["-y", "javaperf"]
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## Tools
156
+
157
+ | Tool | Description |
158
+ |------|-------------|
159
+ | `list_java_processes` | List running Java processes (pid, mainClass, args). Use `topN` (default 10) to limit. |
160
+ | `start_profiling` | Start JFR recording with `settings=profile`. Pass `pid`, `duration` (seconds), optional `recordingName`. |
161
+ | `stop_profiling` | Stop recording and save to file. Requires `pid` and `recordingId` from start_profiling. |
162
+ | `analyze_threads` | Thread dump (jstack). Pass `pid`, optional `topN` (default 10) to limit threads. |
163
+ | `heap_histogram` | Class histogram (GC.class_histogram). Top classes by instances/bytes. Pass `pid`, optional `topN` (20), `all` (include unreachable). |
164
+ | `heap_dump` | Create .hprof heap dump for MAT/VisualVM. Pass `pid`. Saved to recordings/heap_dump.hprof. |
165
+ | `heap_info` | Brief heap summary. Pass `pid`. |
166
+ | `vm_info` | JVM info: uptime, version, flags. Pass `pid`. |
167
+ | `trace_method` | Build call tree for a method from a .jfr file. Pass `filepath`, `className`, `methodName`, optional `topN`. |
168
+ | `parse_jfr_summary` | Parse .jfr into summary: top methods, GC stats, anomalies. Pass `filepath`, optional `events`, `topN`. |
169
+ | `profile_memory` | Memory profile: top allocators, GC, potential leaks. Pass `filepath`, optional `topN`. |
170
+ | `profile_time` | CPU bottleneck profile (bottom-up). Pass `filepath`, optional `topN`. |
171
+ | `profile_frequency` | Call frequency profile (leaf frames). Pass `filepath`, optional `topN`. |
172
+
173
+ ## Example Workflow
174
+
175
+ 1. **List processes** → `list_java_processes`
176
+ 2. **Start recording** → `start_profiling` with `pid` and `duration` (e.g. 60)
177
+ 3. Wait for `duration` seconds (or let it run)
178
+ 4. **Stop and save** → `stop_profiling` with `pid` and `recordingId`
179
+ 5. **Analyze** → Use `parse_jfr_summary`, `profile_memory`, `profile_time`, `profile_frequency`, or `trace_method` with the saved .jfr path
180
+
181
+ ## Limitations
182
+
183
+ - **Sampling**: JFR samples ~10ms; fast methods may not appear in ExecutionSample
184
+ - **Local only**: Runs on the machine where MCP is started
185
+ - **Permissions**: Must run as same user as target JVM for jcmd access
package/README_RU.md ADDED
@@ -0,0 +1,185 @@
1
+ # javaperf
2
+
3
+ [![npm version](https://img.shields.io/npm/v/javaperf.svg)](https://www.npmjs.com/package/javaperf)
4
+
5
+ > MCP-сервер для профилирования Java-приложений через утилиты JDK (jcmd, jfr, jps)
6
+
7
+ Позволяет AI-ассистентам диагностировать производительность, анализировать потоки и просматривать JFR-записи без ручного использования CLI.
8
+
9
+ 📦 **Установка**: `npm install -g javaperf` или через npx
10
+ 🌐 **npm**: https://www.npmjs.com/package/javaperf
11
+
12
+ ## Требования
13
+
14
+ - **Node.js** v18+
15
+ - **JDK** 8u262+ или 11+ с поддержкой JFR
16
+
17
+ Утилиты JDK (`jps`, `jcmd`, `jfr`) находятся автоматически через `JAVA_HOME` или `which java`. Если не найдены — задайте `JAVA_HOME` на корень JDK.
18
+
19
+ ## Быстрый старт
20
+
21
+ ### Для пользователей (через npm)
22
+
23
+ ```bash
24
+ # Установка не требуется — можно использовать прямо в Cursor/Claude Desktop
25
+ # Настройте по инструкции в разделе Интеграция ниже
26
+ ```
27
+
28
+ ### Для разработчиков
29
+
30
+ 1. Клонируйте репозиторий:
31
+ ```bash
32
+ git clone <repo-url>
33
+ cd mcp-jperf
34
+ ```
35
+
36
+ 2. Установите зависимости:
37
+ ```bash
38
+ npm install
39
+ ```
40
+
41
+ 3. Соберите проект:
42
+ ```bash
43
+ npm run build
44
+ ```
45
+
46
+ ## Использование
47
+
48
+ ### Режим разработки
49
+
50
+ ```bash
51
+ npm run dev
52
+ ```
53
+
54
+ ### Production
55
+
56
+ ```bash
57
+ npm start
58
+ ```
59
+
60
+ ### MCP Inspector
61
+
62
+ Отладка и тестирование:
63
+ ```bash
64
+ npx @modelcontextprotocol/inspector node dist/index.js
65
+ ```
66
+
67
+ ## Интеграция
68
+
69
+ ### Cursor IDE
70
+
71
+ 1. Откройте Cursor Settings → Features → Model Context Protocol
72
+ 2. Нажмите "Edit Config"
73
+ 3. Добавьте одну из конфигураций ниже
74
+
75
+ #### Вариант 1: Через npm (рекомендуется)
76
+
77
+ Устанавливается из npm автоматически:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "javaperf": {
83
+ "command": "npx",
84
+ "args": ["-y", "javaperf"]
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ #### Вариант 2: Через npm link (для разработки)
91
+
92
+ Для локальной разработки с живыми изменениями:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "javaperf": {
98
+ "command": "javaperf"
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ Требуется: `cd /путь/к/mcp-jperf && npm link -g`
105
+
106
+ #### Вариант 3: Прямой путь
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "javaperf": {
112
+ "command": "node",
113
+ "args": ["dist/index.js"],
114
+ "cwd": "${workspaceFolder}",
115
+ "env": {
116
+ "JAVA_HOME": "/путь/к/вашему/jdk"
117
+ }
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ Если `list_java_processes` выдаёт "jps not found", MCP-сервер может не наследовать `JAVA_HOME` из shell. Добавьте блок `env` с путём к корню JDK (например `/usr/lib/jvm/java-17` или `~/.sdkman/candidates/java/current`).
124
+
125
+ ### Claude Desktop
126
+
127
+ Редактировать `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) или `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
128
+
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "javaperf": {
133
+ "command": "npx",
134
+ "args": ["-y", "javaperf"]
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Continue.dev
141
+
142
+ Редактировать `.continue/config.json`:
143
+
144
+ ```json
145
+ {
146
+ "mcpServers": {
147
+ "javaperf": {
148
+ "command": "npx",
149
+ "args": ["-y", "javaperf"]
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## Инструменты
156
+
157
+ | Инструмент | Описание |
158
+ |------------|----------|
159
+ | `list_java_processes` | Список Java-процессов (pid, mainClass, args). Параметр `topN` (по умолчанию 10) ограничивает вывод. |
160
+ | `start_profiling` | Запуск JFR-записи с `settings=profile`. Параметры: `pid`, `duration` (сек), опционально `recordingName`. |
161
+ | `stop_profiling` | Остановка записи и сохранение в файл. Требует `pid` и `recordingId` из start_profiling. |
162
+ | `analyze_threads` | Дамп потоков (jstack). Параметры: `pid`, опционально `topN` (по умолчанию 10). |
163
+ | `heap_histogram` | Гистограмма классов (GC.class_histogram). Топ классов по количеству объектов и памяти. Параметры: `pid`, опционально `topN` (20), `all`. |
164
+ | `heap_dump` | Создание .hprof дампа кучи для MAT/VisualVM. Параметр: `pid`. Сохраняется в recordings/heap_dump.hprof. |
165
+ | `heap_info` | Краткая сводка по куче. Параметр: `pid`. |
166
+ | `vm_info` | Информация о JVM: uptime, version, flags. Параметр: `pid`. |
167
+ | `trace_method` | Построение дерева вызовов метода из .jfr. Параметры: `filepath`, `className`, `methodName`, опционально `topN`. |
168
+ | `parse_jfr_summary` | Разбор .jfr в сводку: топ методов, GC, аномалии. Параметры: `filepath`, опционально `events`, `topN`. |
169
+ | `profile_memory` | Профиль по памяти: топ аллокаторов, GC, утечки. Параметры: `filepath`, опционально `topN`. |
170
+ | `profile_time` | Профиль по времени (узкие места CPU). Параметры: `filepath`, опционально `topN`. |
171
+ | `profile_frequency` | Профиль по частоте вызовов. Параметры: `filepath`, опционально `topN`. |
172
+
173
+ ## Пример работы
174
+
175
+ 1. **Список процессов** → `list_java_processes`
176
+ 2. **Старт записи** → `start_profiling` с `pid` и `duration` (например 60)
177
+ 3. Подождать `duration` секунд
178
+ 4. **Остановка и сохранение** → `stop_profiling` с `pid` и `recordingId`
179
+ 5. **Анализ** → Использовать `parse_jfr_summary`, `profile_memory`, `profile_time`, `profile_frequency` или `trace_method` с путём к сохранённому .jfr
180
+
181
+ ## Ограничения
182
+
183
+ - **Семплинг**: JFR делает снимки ~10 мс; быстрые методы могут не попасть в ExecutionSample
184
+ - **Локальность**: Работает только на машине, где запущен MCP
185
+ - **Права**: Нужен доступ к целевой JVM (пользователь MCP = пользователь JVM)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { listJavaProcesses } from "./tools/list_procs.js";
6
+ import { startProfiling } from "./tools/start_profiling.js";
7
+ import { stopProfiling } from "./tools/stop_profiling.js";
8
+ import { analyzeThreads } from "./tools/analyze_threads.js";
9
+ import { traceMethod } from "./tools/trace_method.js";
10
+ import { parseJfrSummary } from "./tools/parse_jfr.js";
11
+ import { profileMemory } from "./tools/profile_memory.js";
12
+ import { profileTime } from "./tools/profile_time.js";
13
+ import { profileFrequency } from "./tools/profile_frequency.js";
14
+ import { heapHistogram } from "./tools/heap_histogram.js";
15
+ import { heapDump } from "./tools/heap_dump.js";
16
+ import { heapInfo } from "./tools/heap_info.js";
17
+ import { vmInfo } from "./tools/vm_info.js";
18
+ const server = new McpServer({
19
+ name: "javaperf",
20
+ version: "1.0.0",
21
+ });
22
+ server.registerTool("list_java_processes", {
23
+ description: "Lists all running Java processes on the machine. Returns an array of objects with pid, mainClass, and args. Use this tool first to discover the target process PID before calling start_profiling or analyze_threads. Data is obtained via jps -l -m.",
24
+ inputSchema: z.object({
25
+ topN: z
26
+ .number()
27
+ .int()
28
+ .min(1)
29
+ .max(100)
30
+ .optional()
31
+ .default(10)
32
+ .describe("Maximum number of processes to return in the list. Default: 10. Use higher values if many Java processes are running."),
33
+ }),
34
+ }, async ({ topN }) => ({
35
+ content: [{ type: "text", text: await listJavaProcesses({ topN }) }],
36
+ }));
37
+ server.registerTool("start_profiling", {
38
+ description: "Starts a Java Flight Recorder (JFR) recording on the specified Java process. Uses settings=profile for a full dump. Before starting, rotates files: deletes old_profile.jfr, renames new_profile.jfr → old_profile.jfr. This keeps only 2 files for before/after comparison. Call stop_profiling after duration to save to recordings/new_profile.jfr.",
39
+ inputSchema: z.object({
40
+ pid: z
41
+ .number()
42
+ .int()
43
+ .positive()
44
+ .describe("Process ID of the Java application to profile. Get this from list_java_processes."),
45
+ duration: z
46
+ .number()
47
+ .int()
48
+ .positive()
49
+ .describe("Recording duration in seconds. Typical values: 10–60 for quick checks, 300+ for load testing."),
50
+ }),
51
+ }, async (args) => ({
52
+ content: [{ type: "text", text: await startProfiling(args) }],
53
+ }));
54
+ server.registerTool("stop_profiling", {
55
+ description: "Stops an active JFR recording and saves it to recordings/new_profile.jfr. Use recordings/new_profile.jfr for current data, recordings/old_profile.jfr for previous (before/after comparison).",
56
+ inputSchema: z.object({
57
+ pid: z
58
+ .number()
59
+ .int()
60
+ .positive()
61
+ .describe("Process ID of the Java process that has the active recording. Must match the pid used in start_profiling."),
62
+ recordingId: z
63
+ .string()
64
+ .describe("ID of the recording to stop. This is the recordingId returned by start_profiling (e.g. '1' or '2')."),
65
+ }),
66
+ }, async (args) => ({
67
+ content: [{ type: "text", text: await stopProfiling(args) }],
68
+ }));
69
+ server.registerTool("analyze_threads", {
70
+ description: "Produces a thread dump of the specified Java process (equivalent to jstack -l). Shows each thread's name, state, and full stack trace with lock information. Use for diagnosing deadlocks, blocked threads, or high thread counts.",
71
+ inputSchema: z.object({
72
+ pid: z
73
+ .number()
74
+ .int()
75
+ .positive()
76
+ .describe("Process ID of the Java application. Get this from list_java_processes."),
77
+ topN: z
78
+ .number()
79
+ .int()
80
+ .min(1)
81
+ .max(500)
82
+ .optional()
83
+ .default(10)
84
+ .describe("Maximum number of threads to include in the output. Default: 10. Increase for applications with many threads."),
85
+ }),
86
+ }, async (args) => ({
87
+ content: [{ type: "text", text: await analyzeThreads(args) }],
88
+ }));
89
+ server.registerTool("heap_histogram", {
90
+ description: "Class histogram of live objects in the heap (jcmd GC.class_histogram). Returns top classes by memory usage — useful for memory leak investigation. Classes with unusually high instance count or bytes may indicate a leak.",
91
+ inputSchema: z.object({
92
+ pid: z
93
+ .number()
94
+ .int()
95
+ .positive()
96
+ .describe("Process ID of the Java application. Get this from list_java_processes."),
97
+ topN: z
98
+ .number()
99
+ .int()
100
+ .min(1)
101
+ .max(200)
102
+ .optional()
103
+ .default(20)
104
+ .describe("Maximum number of top classes to return. Default: 20."),
105
+ all: z
106
+ .boolean()
107
+ .optional()
108
+ .default(false)
109
+ .describe("Include unreachable objects (full GC). Use with caution — can cause pause."),
110
+ }),
111
+ }, async (args) => ({
112
+ content: [{ type: "text", text: await heapHistogram(args) }],
113
+ }));
114
+ server.registerTool("heap_dump", {
115
+ description: "Creates a heap dump (.hprof file) for offline analysis in Eclipse MAT, VisualVM, or JProfiler. Saved to recordings/heap_dump.hprof (overwritten each call). Warning: file can be large (hundreds of MB to GB).",
116
+ inputSchema: z.object({
117
+ pid: z
118
+ .number()
119
+ .int()
120
+ .positive()
121
+ .describe("Process ID of the Java application. Get this from list_java_processes."),
122
+ }),
123
+ }, async (args) => ({
124
+ content: [{ type: "text", text: await heapDump(args) }],
125
+ }));
126
+ server.registerTool("heap_info", {
127
+ description: "Brief heap usage summary: capacities, used, committed regions. Quick snapshot without full dump.",
128
+ inputSchema: z.object({
129
+ pid: z
130
+ .number()
131
+ .int()
132
+ .positive()
133
+ .describe("Process ID of the Java application. Get this from list_java_processes."),
134
+ }),
135
+ }, async (args) => ({
136
+ content: [{ type: "text", text: await heapInfo(args) }],
137
+ }));
138
+ server.registerTool("vm_info", {
139
+ description: "JVM information: uptime, version, and flags. Useful for environment verification.",
140
+ inputSchema: z.object({
141
+ pid: z
142
+ .number()
143
+ .int()
144
+ .positive()
145
+ .describe("Process ID of the Java application. Get this from list_java_processes."),
146
+ }),
147
+ }, async (args) => ({
148
+ content: [{ type: "text", text: await vmInfo(args) }],
149
+ }));
150
+ server.registerTool("trace_method", {
151
+ description: "Builds a call tree for a specific method from a .jfr file. Filters ExecutionSample events to find stack traces containing the given class and method, then aggregates call paths. Use when you want to see who calls a particular method and from where. Limitation: JFR sampling (~10 ms) may miss very fast methods.",
152
+ inputSchema: z.object({
153
+ filepath: z
154
+ .string()
155
+ .describe("Path to .jfr file. Shortcuts: 'new_profile' (current) or 'old_profile' (previous). Or full path e.g. recordings/new_profile.jfr."),
156
+ className: z
157
+ .string()
158
+ .describe("Fully qualified class name (e.g. com.example.MyService) or a substring to match. Used to filter stack frames."),
159
+ methodName: z
160
+ .string()
161
+ .describe("Method name to search for (e.g. processRequest). Matches the method in the stack trace."),
162
+ events: z
163
+ .array(z.string())
164
+ .optional()
165
+ .describe("Optional list of JFR event types to parse. Default: jdk.ExecutionSample. Advanced users can specify other event types."),
166
+ topN: z
167
+ .number()
168
+ .int()
169
+ .min(1)
170
+ .max(100)
171
+ .optional()
172
+ .default(10)
173
+ .describe("Maximum number of call paths (branches) to return in the call tree. Default: 10."),
174
+ }),
175
+ }, async (args) => ({
176
+ content: [{ type: "text", text: await traceMethod(args) }],
177
+ }));
178
+ server.registerTool("parse_jfr_summary", {
179
+ description: "Parses a .jfr file and returns a structured summary: top methods by CPU samples, GC statistics, thread allocation stats, and anomaly hints (e.g. high GC count). Use for a quick high-level overview of the recording before diving into specific profiles.",
180
+ inputSchema: z.object({
181
+ filepath: z
182
+ .string()
183
+ .describe("Path to .jfr file. Shortcuts: 'new_profile' (current) or 'old_profile' (previous). Or full path e.g. recordings/new_profile.jfr."),
184
+ events: z
185
+ .array(z.string())
186
+ .optional()
187
+ .describe("Optional list of JFR event types to include. Default: jdk.ExecutionSample, jdk.GarbageCollection, jdk.JavaThreadStatistics, jdk.ThreadAllocationStatistics."),
188
+ topN: z
189
+ .number()
190
+ .int()
191
+ .min(1)
192
+ .max(100)
193
+ .optional()
194
+ .default(10)
195
+ .describe("Maximum number of top methods to include in the summary. Default: 10."),
196
+ }),
197
+ }, async (args) => ({
198
+ content: [{ type: "text", text: await parseJfrSummary(args) }],
199
+ }));
200
+ server.registerTool("profile_memory", {
201
+ description: "Memory-focused profile from a .jfr file. Returns top memory allocators (class+method), GC statistics, and potential leak candidates from OldObjectSample events. Use when the goal is to find who allocates the most memory or identify memory leaks. Requires a recording made with settings=profile (which start_profiling uses by default).",
202
+ inputSchema: z.object({
203
+ filepath: z
204
+ .string()
205
+ .describe("Path to .jfr file. Shortcuts: 'new_profile' (current) or 'old_profile' (previous). Or full path e.g. recordings/new_profile.jfr."),
206
+ topN: z
207
+ .number()
208
+ .int()
209
+ .min(1)
210
+ .max(100)
211
+ .optional()
212
+ .default(10)
213
+ .describe("Maximum number of top allocators to return. Default: 10."),
214
+ }),
215
+ }, async (args) => ({
216
+ content: [{ type: "text", text: await profileMemory(args) }],
217
+ }));
218
+ server.registerTool("profile_time", {
219
+ description: "CPU time (bottleneck) profile from a .jfr file. Uses bottom-up aggregation: each method is counted in every sample where it appears in the stack, including time spent in callees. Returns methods consuming the most CPU time. Use when the goal is to find performance bottlenecks and slow code paths.",
220
+ inputSchema: z.object({
221
+ filepath: z
222
+ .string()
223
+ .describe("Path to .jfr file. Shortcuts: 'new_profile' (current) or 'old_profile' (previous). Or full path e.g. recordings/new_profile.jfr."),
224
+ topN: z
225
+ .number()
226
+ .int()
227
+ .min(1)
228
+ .max(100)
229
+ .optional()
230
+ .default(10)
231
+ .describe("Maximum number of top methods by CPU time to return. Default: 10."),
232
+ }),
233
+ }, async (args) => ({
234
+ content: [{ type: "text", text: await profileTime(args) }],
235
+ }));
236
+ server.registerTool("profile_frequency", {
237
+ description: "Call frequency profile from a .jfr file. Counts methods that appear at the leaf (top) of the stack in ExecutionSample events — i.e. methods that were actively executing when sampled. Returns the most frequently sampled methods (exclusive, not cumulative). Use when looking for hot spots or the most often executed code paths.",
238
+ inputSchema: z.object({
239
+ filepath: z
240
+ .string()
241
+ .describe("Path to .jfr file. Shortcuts: 'new_profile' (current) or 'old_profile' (previous). Or full path e.g. recordings/new_profile.jfr."),
242
+ topN: z
243
+ .number()
244
+ .int()
245
+ .min(1)
246
+ .max(100)
247
+ .optional()
248
+ .default(10)
249
+ .describe("Maximum number of top methods by call frequency to return. Default: 10."),
250
+ }),
251
+ }, async (args) => ({
252
+ content: [{ type: "text", text: await profileFrequency(args) }],
253
+ }));
254
+ const transport = new StdioServerTransport();
255
+ await server.connect(transport);
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ export declare const analyzeThreadsSchema: z.ZodObject<{
3
+ pid: z.ZodNumber;
4
+ topN: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ topN: number;
7
+ pid: number;
8
+ }, {
9
+ pid: number;
10
+ topN?: number | undefined;
11
+ }>;
12
+ export type AnalyzeThreadsInput = z.infer<typeof analyzeThreadsSchema>;
13
+ export declare function analyzeThreads(input: AnalyzeThreadsInput): Promise<string>;
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ import { runJcmd } from "../utils/jdk.js";
3
+ export const analyzeThreadsSchema = z.object({
4
+ pid: z.number().int().positive(),
5
+ topN: z.number().int().min(1).max(500).optional().default(10),
6
+ });
7
+ export async function analyzeThreads(input) {
8
+ const { pid, topN } = input;
9
+ const output = runJcmd(pid, "Thread.print -l");
10
+ const threadSections = [];
11
+ let current = "";
12
+ for (const line of output.split("\n")) {
13
+ if (line.match(/^"\S/)) {
14
+ if (current.trim())
15
+ threadSections.push(current.trim());
16
+ current = line + "\n";
17
+ }
18
+ else {
19
+ current += line + "\n";
20
+ }
21
+ }
22
+ if (current.trim())
23
+ threadSections.push(current.trim());
24
+ const limited = threadSections.slice(0, topN);
25
+ return limited.join("\n\n");
26
+ }