mcp-db-analyzer 0.2.0 → 0.2.2

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/build/db-mysql.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import mysql from "mysql2/promise";
2
+ import { getConnectionTimeoutMs } from "./db.js";
2
3
  function wrapConnectionError(err) {
3
4
  const msg = err instanceof Error ? err.message : String(err);
4
5
  const sanitized = msg.replace(/\/\/[^@]+@/g, "//****:****@");
@@ -11,9 +12,10 @@ export function createMysqlAdapter() {
11
12
  let pool = null;
12
13
  function getPool() {
13
14
  if (!pool) {
15
+ const timeoutMs = getConnectionTimeoutMs();
14
16
  const uri = process.env.DATABASE_URL;
15
17
  if (uri) {
16
- pool = mysql.createPool(uri);
18
+ pool = mysql.createPool({ uri, connectTimeout: timeoutMs });
17
19
  }
18
20
  else {
19
21
  pool = mysql.createPool({
@@ -22,6 +24,7 @@ export function createMysqlAdapter() {
22
24
  database: process.env.MYSQL_DATABASE || process.env.DB_NAME,
23
25
  user: process.env.MYSQL_USER || process.env.DB_USER,
24
26
  password: process.env.MYSQL_PASSWORD || process.env.DB_PASSWORD,
27
+ connectTimeout: timeoutMs,
25
28
  });
26
29
  }
27
30
  }
@@ -1,4 +1,5 @@
1
1
  import pg from "pg";
2
+ import { getConnectionTimeoutMs } from "./db.js";
2
3
  const { Pool } = pg;
3
4
  function wrapConnectionError(err) {
4
5
  const msg = err instanceof Error ? err.message : String(err);
@@ -12,9 +13,14 @@ export function createPostgresAdapter() {
12
13
  let pool = null;
13
14
  function getPool() {
14
15
  if (!pool) {
16
+ const timeoutMs = getConnectionTimeoutMs();
15
17
  const connectionString = process.env.DATABASE_URL;
16
18
  if (connectionString) {
17
- pool = new Pool({ connectionString });
19
+ pool = new Pool({
20
+ connectionString,
21
+ connectionTimeoutMillis: timeoutMs,
22
+ query_timeout: timeoutMs,
23
+ });
18
24
  }
19
25
  else {
20
26
  pool = new Pool({
@@ -23,6 +29,8 @@ export function createPostgresAdapter() {
23
29
  database: process.env.PGDATABASE || "postgres",
24
30
  user: process.env.PGUSER,
25
31
  password: process.env.PGPASSWORD,
32
+ connectionTimeoutMillis: timeoutMs,
33
+ query_timeout: timeoutMs,
26
34
  });
27
35
  }
28
36
  }
package/build/db.js CHANGED
@@ -1,8 +1,15 @@
1
1
  let adapter = null;
2
2
  let driverType = "postgres";
3
+ let connectionTimeoutMs = 30000;
3
4
  export function getDriverType() {
4
5
  return driverType;
5
6
  }
7
+ export function getConnectionTimeoutMs() {
8
+ return connectionTimeoutMs;
9
+ }
10
+ export function setConnectionTimeoutMs(ms) {
11
+ connectionTimeoutMs = ms;
12
+ }
6
13
  export function setAdapter(a) {
7
14
  adapter = a;
8
15
  driverType = a.driver;
package/build/index.js CHANGED
@@ -11,7 +11,7 @@ import { analyzeSlowQueries } from "./analyzers/slow-queries.js";
11
11
  import { analyzeConnections } from "./analyzers/connections.js";
12
12
  import { analyzeTableRelationships } from "./analyzers/relationships.js";
13
13
  import { analyzeVacuum } from "./analyzers/vacuum.js";
14
- import { closePool, initDriver } from "./db.js";
14
+ import { closePool, initDriver, setConnectionTimeoutMs } from "./db.js";
15
15
  import { formatToolError } from "./errors.js";
16
16
  import { validateLicense, formatUpgradePrompt } from "./license.js";
17
17
  // License check (reads MCP_LICENSE_KEY env var once at startup)
@@ -73,6 +73,15 @@ const server = new McpServer({
73
73
  name: "mcp-db-analyzer",
74
74
  version: "0.1.0",
75
75
  });
76
+ // Shared Zod parameter for connection timeout
77
+ const timeoutParam = z
78
+ .number()
79
+ .optional()
80
+ .default(30000)
81
+ .describe("Connection timeout in milliseconds (default: 30000). Increase for slow or remote databases.");
82
+ function applyTimeout(timeout_ms) {
83
+ setConnectionTimeoutMs(timeout_ms);
84
+ }
76
85
  // --- Tool: inspect_schema ---
77
86
  server.tool("inspect_schema", "List all tables in a schema with row counts and sizes, or inspect a specific table's columns, types, constraints, and foreign keys.", {
78
87
  table: z
@@ -83,7 +92,9 @@ server.tool("inspect_schema", "List all tables in a schema with row counts and s
83
92
  .string()
84
93
  .default("public")
85
94
  .describe("Database schema to inspect (default: public)"),
86
- }, async ({ table, schema }) => {
95
+ timeout_ms: timeoutParam,
96
+ }, async ({ table, schema, timeout_ms }) => {
97
+ applyTimeout(timeout_ms);
87
98
  try {
88
99
  const result = table
89
100
  ? await inspectTable(table, schema)
@@ -111,7 +122,9 @@ server.tool("analyze_indexes", "Analyze index usage statistics to find unused in
111
122
  .enum(["usage", "missing", "all"])
112
123
  .default("all")
113
124
  .describe("Analysis mode: 'usage' for unused index detection, 'missing' for missing index suggestions, 'all' for both"),
114
- }, async ({ schema, mode }) => {
125
+ timeout_ms: timeoutParam,
126
+ }, async ({ schema, mode, timeout_ms }) => {
127
+ applyTimeout(timeout_ms);
115
128
  try {
116
129
  const parts = [];
117
130
  if (mode === "usage" || mode === "all") {
@@ -140,7 +153,9 @@ server.tool("explain_query", "Run EXPLAIN on a SQL query and return a formatted
140
153
  .boolean()
141
154
  .default(false)
142
155
  .describe("Run EXPLAIN ANALYZE to get actual execution times (executes the query). Only allowed for SELECT queries."),
143
- }, async ({ sql, analyze }) => {
156
+ timeout_ms: timeoutParam,
157
+ }, async ({ sql, analyze, timeout_ms }) => {
158
+ applyTimeout(timeout_ms);
144
159
  try {
145
160
  const result = await explainQuery(sql, analyze);
146
161
  return { content: [{ type: "text", text: result }] };
@@ -162,7 +177,9 @@ server.tool("analyze_table_bloat", "Analyze table bloat by checking dead tuple r
162
177
  .string()
163
178
  .default("public")
164
179
  .describe("Database schema to analyze (default: public)"),
165
- }, async ({ schema }) => {
180
+ timeout_ms: timeoutParam,
181
+ }, async ({ schema, timeout_ms }) => {
182
+ applyTimeout(timeout_ms);
166
183
  try {
167
184
  const result = await analyzeTableBloat(schema);
168
185
  return { content: [{ type: "text", text: result }] };
@@ -184,7 +201,9 @@ server.tool("suggest_missing_indexes", "Find tables with high sequential scan co
184
201
  .string()
185
202
  .default("public")
186
203
  .describe("Database schema to analyze (default: public)"),
187
- }, async ({ schema }) => {
204
+ timeout_ms: timeoutParam,
205
+ }, async ({ schema, timeout_ms }) => {
206
+ applyTimeout(timeout_ms);
188
207
  if (!license.isPro) {
189
208
  return {
190
209
  content: [{
@@ -221,7 +240,9 @@ server.tool("analyze_slow_queries", "Find the slowest queries using pg_stat_stat
221
240
  .number()
222
241
  .default(10)
223
242
  .describe("Number of slow queries to return (default: 10)"),
224
- }, async ({ schema, limit }) => {
243
+ timeout_ms: timeoutParam,
244
+ }, async ({ schema, limit, timeout_ms }) => {
245
+ applyTimeout(timeout_ms);
225
246
  if (!license.isPro) {
226
247
  return {
227
248
  content: [{
@@ -249,7 +270,10 @@ server.tool("analyze_slow_queries", "Find the slowest queries using pg_stat_stat
249
270
  }
250
271
  });
251
272
  // Tool 7: analyze_connections
252
- server.tool("analyze_connections", "Analyze active database connections. Detects idle-in-transaction sessions, long-running queries, lock contention, and connection pool utilization. PostgreSQL and MySQL only.", {}, async () => {
273
+ server.tool("analyze_connections", "Analyze active database connections. Detects idle-in-transaction sessions, long-running queries, lock contention, and connection pool utilization. PostgreSQL and MySQL only.", {
274
+ timeout_ms: timeoutParam,
275
+ }, async ({ timeout_ms }) => {
276
+ applyTimeout(timeout_ms);
253
277
  if (!license.isPro) {
254
278
  return {
255
279
  content: [{
@@ -283,7 +307,9 @@ server.tool("analyze_table_relationships", "Analyze foreign key relationships be
283
307
  .string()
284
308
  .default("public")
285
309
  .describe("Database schema to analyze (default: public)"),
286
- }, async ({ schema }) => {
310
+ timeout_ms: timeoutParam,
311
+ }, async ({ schema, timeout_ms }) => {
312
+ applyTimeout(timeout_ms);
287
313
  if (!license.isPro) {
288
314
  return {
289
315
  content: [{
@@ -317,7 +343,9 @@ server.tool("analyze_vacuum", "Analyze PostgreSQL VACUUM maintenance status. Che
317
343
  .string()
318
344
  .default("public")
319
345
  .describe("Database schema to analyze (default: public)"),
320
- }, async ({ schema }) => {
346
+ timeout_ms: timeoutParam,
347
+ }, async ({ schema, timeout_ms }) => {
348
+ applyTimeout(timeout_ms);
321
349
  try {
322
350
  const result = await analyzeVacuum(schema);
323
351
  return { content: [{ type: "text", text: result }] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-db-analyzer",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "MCP server for PostgreSQL, MySQL, and SQLite schema analysis, index optimization, and query plan inspection",
5
5
  "author": "Dmytro Lisnichenko",
6
6
  "type": "module",
@@ -24,16 +24,22 @@
24
24
  ],
25
25
  "keywords": [
26
26
  "mcp",
27
+ "mcp-server",
27
28
  "model-context-protocol",
29
+ "ai",
30
+ "claude",
31
+ "anthropic",
32
+ "database",
33
+ "sql",
34
+ "schema",
35
+ "database-analyzer",
36
+ "database-diagnostics",
28
37
  "postgresql",
29
38
  "mysql",
30
39
  "sqlite",
31
- "database",
32
40
  "schema-analyzer",
33
41
  "index-optimization",
34
- "query-plan",
35
- "ai",
36
- "claude"
42
+ "query-plan"
37
43
  ],
38
44
  "license": "MIT",
39
45
  "engines": {