mcp-spring-boot-actuator 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 +4 -4
- package/build/analyzers/env-risk.js +1 -1
- package/build/index.js +218 -152
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -184,10 +184,10 @@ Once configured, try these prompts in Claude:
|
|
|
184
184
|
|
|
185
185
|
## Part of the MCP Java Backend Suite
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
- [mcp-db-analyzer](https://www.npmjs.com/package/mcp-db-analyzer) — Database performance analysis
|
|
187
|
+
- [mcp-db-analyzer](https://www.npmjs.com/package/mcp-db-analyzer) — PostgreSQL/MySQL/SQLite schema analysis
|
|
189
188
|
- [mcp-jvm-diagnostics](https://www.npmjs.com/package/mcp-jvm-diagnostics) — Thread dump and GC log analysis
|
|
190
|
-
- [mcp-
|
|
189
|
+
- [mcp-redis-diagnostics](https://www.npmjs.com/package/mcp-redis-diagnostics) — Redis memory, slowlog, and client diagnostics
|
|
190
|
+
- [mcp-migration-advisor](https://www.npmjs.com/package/mcp-migration-advisor) — Flyway/Liquibase migration risk analysis
|
|
191
191
|
|
|
192
192
|
## Limitations & Known Issues
|
|
193
193
|
|
|
@@ -199,7 +199,7 @@ This server works alongside:
|
|
|
199
199
|
- **Custom health indicators**: The tool recognizes standard health indicator patterns. Custom health indicators with non-standard status values may not trigger specific recommendations.
|
|
200
200
|
- **Cache analysis**: Supports ConcurrentMapCache, Caffeine, Redis, and EhCache. Other cache providers may show limited analysis.
|
|
201
201
|
- **Non-JSON responses**: Handles HTML error pages (401, 403, 500) gracefully with "Invalid JSON" warnings, but cannot extract useful data from them.
|
|
202
|
-
- **Circular dependency depth**: Detects
|
|
202
|
+
- **Circular dependency depth**: Detects cycles of any length, including multi-hop chains (A→B→C→A).
|
|
203
203
|
|
|
204
204
|
## License
|
|
205
205
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
const SECRET_PATTERNS = [
|
|
11
11
|
/password/i, /secret/i, /api[._-]?key/i, /token/i,
|
|
12
12
|
/credential/i, /private[._-]?key/i, /access[._-]?key/i,
|
|
13
|
-
/auth/i, /jwt/i,
|
|
13
|
+
/auth/i, /jwt/i, /client[._-]?secret/i,
|
|
14
14
|
];
|
|
15
15
|
// Properties that should differ between dev and production
|
|
16
16
|
const PRODUCTION_CHECKS = [
|
package/build/index.js
CHANGED
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
* analyze_env — Detect exposed secrets and risky configurations
|
|
9
9
|
* analyze_beans — Detect circular dependencies and scope mismatches
|
|
10
10
|
*/
|
|
11
|
+
import { readFileSync } from "fs";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
import { dirname, join } from "path";
|
|
11
14
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
15
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
16
|
import { z } from "zod";
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8"));
|
|
14
20
|
import { parseHealth } from "./parsers/health.js";
|
|
15
21
|
import { analyzeMetrics } from "./analyzers/metrics.js";
|
|
16
22
|
import { analyzeEnv } from "./analyzers/env-risk.js";
|
|
@@ -21,7 +27,7 @@ import { analyzeLoggers } from "./analyzers/loggers.js";
|
|
|
21
27
|
import { formatSeveritySummary } from "./format.js";
|
|
22
28
|
// Handle --help
|
|
23
29
|
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
24
|
-
console.log(`mcp-spring-boot-actuator
|
|
30
|
+
console.log(`mcp-spring-boot-actuator v${pkg.version} — MCP server for Spring Boot Actuator diagnostics
|
|
25
31
|
|
|
26
32
|
Usage:
|
|
27
33
|
mcp-spring-boot-actuator [options]
|
|
@@ -34,216 +40,276 @@ Tools provided:
|
|
|
34
40
|
analyze_metrics Analyze JVM, HTTP, and DB pool metrics
|
|
35
41
|
analyze_env Detect exposed secrets and risky configurations
|
|
36
42
|
analyze_beans Detect circular dependencies and scope mismatches
|
|
37
|
-
analyze_startup Parse /startup endpoint for bean init times
|
|
43
|
+
analyze_startup Parse /startup endpoint for bean init times (Spring Boot 2.4+)
|
|
38
44
|
analyze_caches Analyze /caches endpoint for cache health
|
|
39
45
|
analyze_loggers Detect verbose logging and misconfigurations`);
|
|
40
46
|
process.exit(0);
|
|
41
47
|
}
|
|
42
48
|
const server = new McpServer({
|
|
43
49
|
name: "mcp-spring-boot-actuator",
|
|
44
|
-
version:
|
|
50
|
+
version: pkg.version,
|
|
45
51
|
});
|
|
46
52
|
// Tool 1: analyze_health
|
|
47
53
|
server.tool("analyze_health", "Analyze a Spring Boot Actuator /health endpoint response. Diagnoses unhealthy components and provides recommendations.", {
|
|
48
54
|
json: z.string().describe("JSON response from the /health endpoint (curl http://localhost:8080/actuator/health)"),
|
|
49
55
|
}, async ({ json }) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
56
|
+
try {
|
|
57
|
+
const report = parseHealth(json);
|
|
58
|
+
let output = `## Health Analysis\n\n`;
|
|
59
|
+
output += `**Overall Status**: ${report.overallStatus}\n`;
|
|
60
|
+
output += `**Components**: ${report.components.length}\n\n`;
|
|
61
|
+
if (report.components.length > 0) {
|
|
62
|
+
output += "### Component Status\n\n";
|
|
63
|
+
output += "| Component | Status | Details |\n|-----------|--------|--------|\n";
|
|
64
|
+
for (const comp of report.components) {
|
|
65
|
+
const details = Object.entries(comp.details).slice(0, 3).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
66
|
+
output += `| ${comp.name} | ${comp.status} | ${details || "-"} |\n`;
|
|
67
|
+
}
|
|
68
|
+
output += "\n";
|
|
60
69
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
output += `**${issue.severity}** [${issue.component}]: ${issue.message}\n\n`;
|
|
70
|
+
if (report.issues.length > 0) {
|
|
71
|
+
output += "### Issues\n\n";
|
|
72
|
+
for (const issue of report.issues) {
|
|
73
|
+
output += `**${issue.severity}** [${issue.component}]: ${issue.message}\n\n`;
|
|
74
|
+
}
|
|
67
75
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
if (report.recommendations.length > 0) {
|
|
77
|
+
output += "### Recommendations\n\n";
|
|
78
|
+
for (const rec of report.recommendations) {
|
|
79
|
+
output += `- ${rec}\n`;
|
|
80
|
+
}
|
|
73
81
|
}
|
|
82
|
+
output += formatSeveritySummary(report.issues);
|
|
83
|
+
return { content: [{ type: "text", text: output }] };
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
return {
|
|
87
|
+
content: [{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: `Error analyzing health data: ${err instanceof Error ? err.message : String(err)}`,
|
|
90
|
+
}],
|
|
91
|
+
};
|
|
74
92
|
}
|
|
75
|
-
output += formatSeveritySummary(report.issues);
|
|
76
|
-
return { content: [{ type: "text", text: output }] };
|
|
77
93
|
});
|
|
78
94
|
// Tool 2: analyze_metrics
|
|
79
95
|
server.tool("analyze_metrics", "Analyze Spring Boot Actuator metrics data. Detects JVM memory pressure, high error rates, connection pool exhaustion, and GC issues.", {
|
|
80
96
|
json: z.string().describe("JSON object with metric names as keys and values (e.g., from /metrics endpoints)"),
|
|
81
97
|
}, async ({ json }) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (report.http) {
|
|
96
|
-
output += "### HTTP\n\n";
|
|
97
|
-
output += `| Metric | Value |\n|--------|-------|\n`;
|
|
98
|
-
output += `| Total Requests | ${report.http.totalRequests} |\n`;
|
|
99
|
-
output += `| Error Rate | ${(report.http.errorRate * 100).toFixed(1)}% |\n`;
|
|
100
|
-
output += `| Max Latency | ${report.http.maxLatency}ms |\n\n`;
|
|
101
|
-
}
|
|
102
|
-
if (report.issues.length > 0) {
|
|
103
|
-
output += "### Issues\n\n";
|
|
104
|
-
for (const issue of report.issues) {
|
|
105
|
-
output += `**${issue.severity}** [${issue.category}]: ${issue.message}\n\n`;
|
|
98
|
+
try {
|
|
99
|
+
const report = analyzeMetrics(json);
|
|
100
|
+
let output = `## Metrics Analysis\n\n`;
|
|
101
|
+
if (report.jvm) {
|
|
102
|
+
output += "### JVM\n\n";
|
|
103
|
+
output += `| Metric | Value |\n|--------|-------|\n`;
|
|
104
|
+
output += `| Heap Used | ${formatBytes(report.jvm.heapUsed)} |\n`;
|
|
105
|
+
output += `| Heap Max | ${formatBytes(report.jvm.heapMax)} |\n`;
|
|
106
|
+
output += `| Heap Utilization | ${(report.jvm.heapUtilization * 100).toFixed(1)}% |\n`;
|
|
107
|
+
output += `| Thread Count | ${report.jvm.threadCount} |\n`;
|
|
108
|
+
output += `| Thread Peak | ${report.jvm.threadPeak} |\n`;
|
|
109
|
+
output += `| GC Pauses | ${report.jvm.gcPauseCount} (${report.jvm.gcPauseTotal.toFixed(1)}s total) |\n`;
|
|
110
|
+
output += `| Loaded Classes | ${report.jvm.loadedClasses} |\n\n`;
|
|
106
111
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
output +=
|
|
112
|
+
if (report.http) {
|
|
113
|
+
output += "### HTTP\n\n";
|
|
114
|
+
output += `| Metric | Value |\n|--------|-------|\n`;
|
|
115
|
+
output += `| Total Requests | ${report.http.totalRequests} |\n`;
|
|
116
|
+
output += `| Error Rate | ${(report.http.errorRate * 100).toFixed(1)}% |\n`;
|
|
117
|
+
output += `| Max Latency | ${report.http.maxLatency}ms |\n\n`;
|
|
118
|
+
}
|
|
119
|
+
if (report.issues.length > 0) {
|
|
120
|
+
output += "### Issues\n\n";
|
|
121
|
+
for (const issue of report.issues) {
|
|
122
|
+
output += `**${issue.severity}** [${issue.category}]: ${issue.message}\n\n`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (report.recommendations.length > 0) {
|
|
126
|
+
output += "### Recommendations\n\n";
|
|
127
|
+
for (const rec of report.recommendations) {
|
|
128
|
+
output += `- ${rec}\n`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!report.jvm && !report.http && report.issues.length === 0) {
|
|
132
|
+
output += "No recognized metrics found. Provide metrics in the format: `{\"jvm.memory.used\": 1234567, ...}`\n";
|
|
112
133
|
}
|
|
134
|
+
output += formatSeveritySummary(report.issues);
|
|
135
|
+
return { content: [{ type: "text", text: output }] };
|
|
113
136
|
}
|
|
114
|
-
|
|
115
|
-
|
|
137
|
+
catch (err) {
|
|
138
|
+
return {
|
|
139
|
+
content: [{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: `Error analyzing metrics data: ${err instanceof Error ? err.message : String(err)}`,
|
|
142
|
+
}],
|
|
143
|
+
};
|
|
116
144
|
}
|
|
117
|
-
output += formatSeveritySummary(report.issues);
|
|
118
|
-
return { content: [{ type: "text", text: output }] };
|
|
119
145
|
});
|
|
120
146
|
// Tool 3: analyze_env
|
|
121
147
|
server.tool("analyze_env", "Analyze Spring Boot Actuator /env endpoint response. Detects exposed secrets, risky configurations, and missing production settings.", {
|
|
122
148
|
json: z.string().describe("JSON response from the /env endpoint (curl http://localhost:8080/actuator/env)"),
|
|
123
149
|
}, async ({ json }) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
for (const
|
|
135
|
-
|
|
136
|
-
|
|
150
|
+
try {
|
|
151
|
+
const report = analyzeEnv(json);
|
|
152
|
+
let output = `## Environment Analysis\n\n`;
|
|
153
|
+
output += `**Active Profiles**: ${report.activeProfiles.length > 0 ? report.activeProfiles.join(", ") : "none"}\n`;
|
|
154
|
+
output += `**Property Sources**: ${report.propertySources.length}\n\n`;
|
|
155
|
+
if (report.risks.length > 0) {
|
|
156
|
+
output += "### Risks\n\n";
|
|
157
|
+
const critical = report.risks.filter(r => r.severity === "CRITICAL");
|
|
158
|
+
const warning = report.risks.filter(r => r.severity === "WARNING");
|
|
159
|
+
const info = report.risks.filter(r => r.severity === "INFO");
|
|
160
|
+
for (const group of [critical, warning, info]) {
|
|
161
|
+
for (const risk of group) {
|
|
162
|
+
output += `**${risk.severity}** \`${risk.property}\`: ${risk.message}\n`;
|
|
163
|
+
output += `> ${risk.recommendation}\n\n`;
|
|
164
|
+
}
|
|
137
165
|
}
|
|
138
166
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
output += "### No risks detected.\n\n";
|
|
142
|
-
}
|
|
143
|
-
if (report.recommendations.length > 0) {
|
|
144
|
-
output += "### Recommendations\n\n";
|
|
145
|
-
for (const rec of report.recommendations) {
|
|
146
|
-
output += `- ${rec}\n`;
|
|
167
|
+
else {
|
|
168
|
+
output += "### No risks detected.\n\n";
|
|
147
169
|
}
|
|
170
|
+
if (report.recommendations.length > 0) {
|
|
171
|
+
output += "### Recommendations\n\n";
|
|
172
|
+
for (const rec of report.recommendations) {
|
|
173
|
+
output += `- ${rec}\n`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
output += formatSeveritySummary(report.risks);
|
|
177
|
+
return { content: [{ type: "text", text: output }] };
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
return {
|
|
181
|
+
content: [{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: `Error analyzing environment data: ${err instanceof Error ? err.message : String(err)}`,
|
|
184
|
+
}],
|
|
185
|
+
};
|
|
148
186
|
}
|
|
149
|
-
output += formatSeveritySummary(report.risks);
|
|
150
|
-
return { content: [{ type: "text", text: output }] };
|
|
151
187
|
});
|
|
152
188
|
// Tool 4: analyze_beans
|
|
153
189
|
server.tool("analyze_beans", "Analyze Spring Boot Actuator /beans endpoint response. Detects circular dependencies, scope mismatches, and bean architecture issues.", {
|
|
154
190
|
json: z.string().describe("JSON response from the /beans endpoint (curl http://localhost:8080/actuator/beans)"),
|
|
155
191
|
}, async ({ json }) => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
192
|
+
try {
|
|
193
|
+
const report = analyzeBeans(json);
|
|
194
|
+
let output = `## Bean Analysis\n\n`;
|
|
195
|
+
output += `**Total Beans**: ${report.totalBeans}\n`;
|
|
196
|
+
if (report.contexts.length > 0) {
|
|
197
|
+
output += `**Contexts**: ${report.contexts.join(", ")}\n`;
|
|
198
|
+
}
|
|
199
|
+
output += "\n";
|
|
200
|
+
if (report.issues.length > 0) {
|
|
201
|
+
output += "### Issues\n\n";
|
|
202
|
+
for (const issue of report.issues) {
|
|
203
|
+
output += `**${issue.severity}**: ${issue.message}\n`;
|
|
204
|
+
if (issue.beans.length > 0) {
|
|
205
|
+
output += `> Beans: ${issue.beans.join(", ")}\n`;
|
|
206
|
+
}
|
|
207
|
+
output += "\n";
|
|
169
208
|
}
|
|
170
|
-
output += "\n";
|
|
171
209
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
else {
|
|
211
|
+
output += "### No issues detected.\n\n";
|
|
212
|
+
}
|
|
213
|
+
if (report.recommendations.length > 0) {
|
|
214
|
+
output += "### Recommendations\n\n";
|
|
215
|
+
for (const rec of report.recommendations) {
|
|
216
|
+
output += `- ${rec}\n`;
|
|
217
|
+
}
|
|
180
218
|
}
|
|
219
|
+
output += formatSeveritySummary(report.issues);
|
|
220
|
+
return { content: [{ type: "text", text: output }] };
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
return {
|
|
224
|
+
content: [{
|
|
225
|
+
type: "text",
|
|
226
|
+
text: `Error analyzing beans data: ${err instanceof Error ? err.message : String(err)}`,
|
|
227
|
+
}],
|
|
228
|
+
};
|
|
181
229
|
}
|
|
182
|
-
output += formatSeveritySummary(report.issues);
|
|
183
|
-
return { content: [{ type: "text", text: output }] };
|
|
184
230
|
});
|
|
185
231
|
// Tool 5: analyze_startup
|
|
186
|
-
server.tool("analyze_startup", "Analyze Spring Boot Actuator /startup endpoint (Spring Boot
|
|
232
|
+
server.tool("analyze_startup", "Analyze Spring Boot Actuator /startup endpoint (Spring Boot 2.4+). Detects slow bean initialization, heavy auto-configurations, and startup bottlenecks.", {
|
|
187
233
|
json: z.string().describe("JSON response from the /startup endpoint (curl http://localhost:8080/actuator/startup)"),
|
|
188
234
|
}, async ({ json }) => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
235
|
+
try {
|
|
236
|
+
const report = analyzeStartup(json);
|
|
237
|
+
let output = `## Startup Analysis\n\n`;
|
|
238
|
+
output += `**Total Startup Time**: ${(report.totalDurationMs / 1000).toFixed(1)}s\n`;
|
|
239
|
+
output += `**Startup Steps**: ${report.steps.length}\n`;
|
|
240
|
+
output += `**Slow Steps**: ${report.slowSteps.length}\n\n`;
|
|
241
|
+
if (report.slowSteps.length > 0) {
|
|
242
|
+
output += "### Slowest Steps\n\n";
|
|
243
|
+
output += "| Step | Duration | Bean |\n|------|----------|------|\n";
|
|
244
|
+
for (const step of report.slowSteps.slice(0, 15)) {
|
|
245
|
+
const bean = step.tags.beanName || "-";
|
|
246
|
+
output += `| ${step.name} | ${(step.durationMs / 1000).toFixed(2)}s | ${bean} |\n`;
|
|
247
|
+
}
|
|
248
|
+
output += "\n";
|
|
200
249
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
output += `**${issue.severity}**: ${issue.message}\n\n`;
|
|
250
|
+
if (report.issues.length > 0) {
|
|
251
|
+
output += "### Issues\n\n";
|
|
252
|
+
for (const issue of report.issues) {
|
|
253
|
+
output += `**${issue.severity}**: ${issue.message}\n\n`;
|
|
254
|
+
}
|
|
207
255
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
256
|
+
if (report.recommendations.length > 0) {
|
|
257
|
+
output += "### Recommendations\n\n";
|
|
258
|
+
for (const rec of report.recommendations) {
|
|
259
|
+
output += `- ${rec}\n`;
|
|
260
|
+
}
|
|
213
261
|
}
|
|
262
|
+
output += formatSeveritySummary(report.issues);
|
|
263
|
+
return { content: [{ type: "text", text: output }] };
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
return {
|
|
267
|
+
content: [{
|
|
268
|
+
type: "text",
|
|
269
|
+
text: `Error analyzing startup data: ${err instanceof Error ? err.message : String(err)}`,
|
|
270
|
+
}],
|
|
271
|
+
};
|
|
214
272
|
}
|
|
215
|
-
output += formatSeveritySummary(report.issues);
|
|
216
|
-
return { content: [{ type: "text", text: output }] };
|
|
217
273
|
});
|
|
218
274
|
// Tool 6: analyze_caches
|
|
219
275
|
server.tool("analyze_caches", "Analyze Spring Boot Actuator /caches endpoint. Lists registered caches, detects unbounded caches (memory leak risk), and identifies cache configuration issues.", {
|
|
220
276
|
json: z.string().describe("JSON response from the /caches endpoint (curl http://localhost:8080/actuator/caches)"),
|
|
221
277
|
}, async ({ json }) => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
278
|
+
try {
|
|
279
|
+
const report = analyzeCaches(json);
|
|
280
|
+
let output = `## Cache Analysis\n\n`;
|
|
281
|
+
output += `**Registered Caches**: ${report.caches.length}\n\n`;
|
|
282
|
+
if (report.caches.length > 0) {
|
|
283
|
+
output += "### Cache Registry\n\n";
|
|
284
|
+
output += "| Cache | Manager | Implementation |\n|-------|---------|----------------|\n";
|
|
285
|
+
for (const cache of report.caches) {
|
|
286
|
+
output += `| ${cache.name} | ${cache.cacheManager} | ${cache.target} |\n`;
|
|
287
|
+
}
|
|
288
|
+
output += "\n";
|
|
230
289
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
output += `**${issue.severity}**${issue.cache ? ` [${issue.cache}]` : ""}: ${issue.message}\n\n`;
|
|
290
|
+
if (report.issues.length > 0) {
|
|
291
|
+
output += "### Issues\n\n";
|
|
292
|
+
for (const issue of report.issues) {
|
|
293
|
+
output += `**${issue.severity}**${issue.cache ? ` [${issue.cache}]` : ""}: ${issue.message}\n\n`;
|
|
294
|
+
}
|
|
237
295
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
296
|
+
if (report.recommendations.length > 0) {
|
|
297
|
+
output += "### Recommendations\n\n";
|
|
298
|
+
for (const rec of report.recommendations) {
|
|
299
|
+
output += `- ${rec}\n`;
|
|
300
|
+
}
|
|
243
301
|
}
|
|
302
|
+
output += formatSeveritySummary(report.issues);
|
|
303
|
+
return { content: [{ type: "text", text: output }] };
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
return {
|
|
307
|
+
content: [{
|
|
308
|
+
type: "text",
|
|
309
|
+
text: `Error analyzing cache data: ${err instanceof Error ? err.message : String(err)}`,
|
|
310
|
+
}],
|
|
311
|
+
};
|
|
244
312
|
}
|
|
245
|
-
output += formatSeveritySummary(report.issues);
|
|
246
|
-
return { content: [{ type: "text", text: output }] };
|
|
247
313
|
});
|
|
248
314
|
// Tool 7: analyze_loggers
|
|
249
315
|
server.tool("analyze_loggers", "Analyze Spring Boot /loggers endpoint response. Detects DEBUG/TRACE in production, inconsistent log levels across packages, and verbose framework logging.", {
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-spring-boot-actuator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "MCP server for Spring Boot Actuator analysis — health, metrics, environment, and bean diagnostics",
|
|
5
|
+
"mcpName": "io.github.dmitriusan/mcp-spring-boot-actuator",
|
|
5
6
|
"main": "build/index.js",
|
|
6
7
|
"bin": {
|
|
7
8
|
"mcp-spring-boot-actuator": "build/index.js"
|