mcp-db-analyzer 0.2.3 → 0.2.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 +7 -0
- package/build/analyzers/query.js +13 -0
- package/build/analyzers/relationships.js +1 -1
- package/build/index.js +5 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -376,6 +376,13 @@ Analyze PostgreSQL VACUUM maintenance status. Checks dead tuple ratios, vacuum s
|
|
|
376
376
|
- **Connection analysis**: `analyze_connections` is PostgreSQL/MySQL only. Not available for SQLite databases.
|
|
377
377
|
- **Vacuum analysis**: `analyze_vacuum` is PostgreSQL only. For MySQL, use `OPTIMIZE TABLE` or `analyze_table_bloat`.
|
|
378
378
|
|
|
379
|
+
## Part of the MCP Java Backend Suite
|
|
380
|
+
|
|
381
|
+
- [mcp-spring-boot-actuator](https://www.npmjs.com/package/mcp-spring-boot-actuator) — Spring Boot health, metrics, and bean analysis
|
|
382
|
+
- [mcp-jvm-diagnostics](https://www.npmjs.com/package/mcp-jvm-diagnostics) — Thread dump and GC log analysis
|
|
383
|
+
- [mcp-redis-diagnostics](https://www.npmjs.com/package/mcp-redis-diagnostics) — Redis memory, slowlog, and client diagnostics
|
|
384
|
+
- [mcp-migration-advisor](https://www.npmjs.com/package/mcp-migration-advisor) — Flyway/Liquibase migration risk analysis
|
|
385
|
+
|
|
379
386
|
## License
|
|
380
387
|
|
|
381
388
|
MIT
|
package/build/analyzers/query.js
CHANGED
|
@@ -3,6 +3,9 @@ import { queryUnsafe, getDriverType } from "../db.js";
|
|
|
3
3
|
* Run EXPLAIN on a query and return a formatted analysis.
|
|
4
4
|
*/
|
|
5
5
|
export async function explainQuery(sql, analyze = false) {
|
|
6
|
+
if (!sql || !sql.trim()) {
|
|
7
|
+
return "**Error**: SQL query cannot be empty.";
|
|
8
|
+
}
|
|
6
9
|
// Safety: in ANALYZE mode, only allow pure SELECT statements.
|
|
7
10
|
// EXPLAIN ANALYZE actually executes the query, so we must reject anything
|
|
8
11
|
// that could modify data — including CTEs with write operations.
|
|
@@ -232,6 +235,16 @@ function collectWarnings(node) {
|
|
|
232
235
|
if (node["Sort Method"] === "external merge") {
|
|
233
236
|
warnings.push(`**Disk sort** detected. Increase \`work_mem\` or add an index to avoid sorting.`);
|
|
234
237
|
}
|
|
238
|
+
// Stale statistics: actual rows deviate significantly from planner estimate
|
|
239
|
+
if (node["Actual Rows"] !== undefined &&
|
|
240
|
+
node["Plan Rows"] > 0 &&
|
|
241
|
+
node["Actual Rows"] > 0) {
|
|
242
|
+
const ratio = node["Actual Rows"] / node["Plan Rows"];
|
|
243
|
+
if (ratio > 10 || ratio < 0.1) {
|
|
244
|
+
const relation = node["Relation Name"] ?? node["Node Type"];
|
|
245
|
+
warnings.push(`**Stale statistics** on \`${relation}\`: planner estimated ${node["Plan Rows"]} rows but got ${node["Actual Rows"]} (${ratio > 1 ? ratio.toFixed(0) + "× over" : (1 / ratio).toFixed(0) + "× under"}estimate). Run \`ANALYZE ${node["Relation Name"] ?? ""}\` to refresh table statistics.`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
235
248
|
if (node.Plans) {
|
|
236
249
|
for (const child of node.Plans) {
|
|
237
250
|
warnings.push(...collectWarnings(child));
|
|
@@ -84,7 +84,7 @@ async function analyzeSqliteRelationships() {
|
|
|
84
84
|
// Get FK info per table
|
|
85
85
|
const fks = [];
|
|
86
86
|
for (const table of tablesResult.rows) {
|
|
87
|
-
const fkInfo = await query(`PRAGMA foreign_key_list(
|
|
87
|
+
const fkInfo = await query(`PRAGMA foreign_key_list("${table.name.replace(/"/g, '""')}")`);
|
|
88
88
|
for (const fk of fkInfo.rows) {
|
|
89
89
|
fks.push({
|
|
90
90
|
source_table: table.name,
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "module";
|
|
2
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
5
|
import { z } from "zod";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { version: PKG_VERSION } = require("../package.json");
|
|
5
8
|
import { listTables, inspectTable } from "./analyzers/schema.js";
|
|
6
9
|
import { analyzeIndexUsage, findMissingIndexes } from "./analyzers/indexes.js";
|
|
7
10
|
import { explainQuery } from "./analyzers/query.js";
|
|
@@ -15,7 +18,7 @@ import { closePool, initDriver, setConnectionTimeoutMs } from "./db.js";
|
|
|
15
18
|
import { formatToolError } from "./errors.js";
|
|
16
19
|
// Handle --help
|
|
17
20
|
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
18
|
-
console.log(`mcp-db-analyzer
|
|
21
|
+
console.log(`mcp-db-analyzer v${PKG_VERSION} — MCP server for database analysis
|
|
19
22
|
|
|
20
23
|
Usage:
|
|
21
24
|
mcp-db-analyzer [options]
|
|
@@ -68,7 +71,7 @@ function detectDriver() {
|
|
|
68
71
|
}
|
|
69
72
|
const server = new McpServer({
|
|
70
73
|
name: "mcp-db-analyzer",
|
|
71
|
-
version:
|
|
74
|
+
version: PKG_VERSION,
|
|
72
75
|
});
|
|
73
76
|
// Shared Zod parameter for connection timeout
|
|
74
77
|
const timeoutParam = z
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-db-analyzer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "MCP server for PostgreSQL, MySQL, and SQLite schema analysis, index optimization, and query plan inspection",
|
|
5
|
+
"mcpName": "io.github.dmitriusan/mcp-db-analyzer",
|
|
5
6
|
"author": "Dmytro Lisnichenko",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"main": "./build/index.js",
|