sqlcipher-mcp-server 1.0.4 → 2.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.
- package/README.md +433 -210
- package/package.json +1 -1
- package/src/config/constants.js +18 -40
- package/src/definitions/prompts.js +124 -0
- package/src/definitions/tools.js +363 -0
- package/src/handlers/http-handlers.js +576 -4
- package/src/handlers/mcp-handlers.js +558 -69
- package/src/handlers/prompt-handlers.js +601 -0
- package/src/server/http-server.js +52 -2
- package/src/server/mcp-server.js +208 -95
- package/src/services/database-service.js +395 -55
- package/src/utils/database-operations.js +967 -0
- package/src/utils/detectors.js +55 -0
- package/src/utils/formatters.js +470 -64
- package/src/utils/validators.js +147 -58
- package/lib/database.js +0 -216
|
@@ -1,55 +1,395 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Database Service
|
|
3
|
-
* Service layer that wraps database operations with error handling
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Database Service
|
|
3
|
+
* Service layer that wraps database operations with error handling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
connectDatabase,
|
|
8
|
+
executeQuery,
|
|
9
|
+
closeConnection,
|
|
10
|
+
getTableList,
|
|
11
|
+
getTableSchema,
|
|
12
|
+
getForeignKeys,
|
|
13
|
+
getIndexes,
|
|
14
|
+
getDatabaseInfo,
|
|
15
|
+
getTableInfo,
|
|
16
|
+
testConnection,
|
|
17
|
+
explainQueryPlan,
|
|
18
|
+
getTableStatistics,
|
|
19
|
+
sampleTableData,
|
|
20
|
+
getColumnStatistics,
|
|
21
|
+
searchTables,
|
|
22
|
+
searchColumns,
|
|
23
|
+
findRelatedTables
|
|
24
|
+
} from '../utils/database-operations.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Execute a query on a database
|
|
28
|
+
* Handles connection, query execution, and cleanup
|
|
29
|
+
* @param {string} dbPath - Path to the database file
|
|
30
|
+
* @param {string|undefined} password - Database password (optional)
|
|
31
|
+
* @param {string} query - SQL query to execute
|
|
32
|
+
* @returns {Promise<Object>} Query results
|
|
33
|
+
* @throws {Error} If connection or query execution fails
|
|
34
|
+
*/
|
|
35
|
+
export async function executeQueryOnDatabase(dbPath, password, query) {
|
|
36
|
+
let db = null;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Connect to database
|
|
40
|
+
db = await connectDatabase(dbPath, password);
|
|
41
|
+
|
|
42
|
+
// Execute query
|
|
43
|
+
const result = await executeQuery(db, query);
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
} finally {
|
|
47
|
+
// Always close the database connection
|
|
48
|
+
if (db) {
|
|
49
|
+
closeConnection(db);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Test database connection
|
|
56
|
+
* @param {string} dbPath - Path to the database file
|
|
57
|
+
* @param {string|undefined} password - Database password (optional)
|
|
58
|
+
* @returns {Promise<boolean>} True if connection successful
|
|
59
|
+
*/
|
|
60
|
+
export async function testDatabaseConnection(dbPath, password) {
|
|
61
|
+
let db = null;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
db = await connectDatabase(dbPath, password);
|
|
65
|
+
await testConnection(db);
|
|
66
|
+
return true;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Failed to connect to database: ${error.message}`);
|
|
69
|
+
} finally {
|
|
70
|
+
if (db) {
|
|
71
|
+
closeConnection(db);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get list of tables from database
|
|
78
|
+
* @param {string} dbPath - Path to the database file
|
|
79
|
+
* @param {string|undefined} password - Database password (optional)
|
|
80
|
+
* @param {string[]} tableNames - Optional array of table names to filter
|
|
81
|
+
* @returns {Promise<Array>} Array of table objects
|
|
82
|
+
*/
|
|
83
|
+
export async function getTableListFromDatabase(dbPath, password, tableNames = null) {
|
|
84
|
+
let db = null;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
db = await connectDatabase(dbPath, password);
|
|
88
|
+
const tables = await getTableList(db, tableNames);
|
|
89
|
+
|
|
90
|
+
// Get row counts for each table
|
|
91
|
+
const tablesWithCounts = await Promise.all(
|
|
92
|
+
tables.map(async (table) => {
|
|
93
|
+
try {
|
|
94
|
+
const info = await getTableInfo(db, table.name);
|
|
95
|
+
return {
|
|
96
|
+
...table,
|
|
97
|
+
row_count: info.row_count
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
...table,
|
|
102
|
+
row_count: 0
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return tablesWithCounts;
|
|
109
|
+
} finally {
|
|
110
|
+
if (db) {
|
|
111
|
+
closeConnection(db);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get table schema from database
|
|
118
|
+
* @param {string} dbPath - Path to the database file
|
|
119
|
+
* @param {string|undefined} password - Database password (optional)
|
|
120
|
+
* @param {string|string[]} tableName - Table name or array of table names
|
|
121
|
+
* @returns {Promise<Object|Array>} Table schema or array of schemas
|
|
122
|
+
*/
|
|
123
|
+
export async function getTableSchemaFromDatabase(dbPath, password, tableName) {
|
|
124
|
+
let db = null;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
db = await connectDatabase(dbPath, password);
|
|
128
|
+
|
|
129
|
+
// Handle batch operation
|
|
130
|
+
if (Array.isArray(tableName)) {
|
|
131
|
+
const schemas = await Promise.all(
|
|
132
|
+
tableName.map(async (name) => {
|
|
133
|
+
try {
|
|
134
|
+
const schema = await getTableSchema(db, name);
|
|
135
|
+
const fks = await getForeignKeys(db, name);
|
|
136
|
+
const indexes = await getIndexes(db, name);
|
|
137
|
+
return {
|
|
138
|
+
...schema,
|
|
139
|
+
foreign_keys: fks,
|
|
140
|
+
indexes: indexes
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
tableName: name,
|
|
145
|
+
error: error.message
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
return schemas;
|
|
151
|
+
} else {
|
|
152
|
+
// Single table
|
|
153
|
+
const schema = await getTableSchema(db, tableName);
|
|
154
|
+
const fks = await getForeignKeys(db, tableName);
|
|
155
|
+
const indexes = await getIndexes(db, tableName);
|
|
156
|
+
return {
|
|
157
|
+
...schema,
|
|
158
|
+
foreign_keys: fks,
|
|
159
|
+
indexes: indexes
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
} finally {
|
|
163
|
+
if (db) {
|
|
164
|
+
closeConnection(db);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get foreign keys from database
|
|
171
|
+
* @param {string} dbPath - Path to the database file
|
|
172
|
+
* @param {string|undefined} password - Database password (optional)
|
|
173
|
+
* @param {string} tableName - Optional table name
|
|
174
|
+
* @returns {Promise<Array>} Array of foreign key relationships
|
|
175
|
+
*/
|
|
176
|
+
export async function getForeignKeysFromDatabase(dbPath, password, tableName = null) {
|
|
177
|
+
let db = null;
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
db = await connectDatabase(dbPath, password);
|
|
181
|
+
return await getForeignKeys(db, tableName);
|
|
182
|
+
} finally {
|
|
183
|
+
if (db) {
|
|
184
|
+
closeConnection(db);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get indexes from database
|
|
191
|
+
* @param {string} dbPath - Path to the database file
|
|
192
|
+
* @param {string|undefined} password - Database password (optional)
|
|
193
|
+
* @param {string} tableName - Optional table name
|
|
194
|
+
* @returns {Promise<Array>} Array of index information
|
|
195
|
+
*/
|
|
196
|
+
export async function getIndexesFromDatabase(dbPath, password, tableName = null) {
|
|
197
|
+
let db = null;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
db = await connectDatabase(dbPath, password);
|
|
201
|
+
return await getIndexes(db, tableName);
|
|
202
|
+
} finally {
|
|
203
|
+
if (db) {
|
|
204
|
+
closeConnection(db);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get database info
|
|
211
|
+
* @param {string} dbPath - Path to the database file
|
|
212
|
+
* @param {string|undefined} password - Database password (optional)
|
|
213
|
+
* @returns {Promise<Object>} Database metadata
|
|
214
|
+
*/
|
|
215
|
+
export async function getDatabaseInfoFromDatabase(dbPath, password) {
|
|
216
|
+
let db = null;
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
db = await connectDatabase(dbPath, password);
|
|
220
|
+
return await getDatabaseInfo(db, dbPath);
|
|
221
|
+
} finally {
|
|
222
|
+
if (db) {
|
|
223
|
+
closeConnection(db);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get table info
|
|
230
|
+
* @param {string} dbPath - Path to the database file
|
|
231
|
+
* @param {string|undefined} password - Database password (optional)
|
|
232
|
+
* @param {string} tableName - Table name
|
|
233
|
+
* @returns {Promise<Object>} Table information
|
|
234
|
+
*/
|
|
235
|
+
export async function getTableInfoFromDatabase(dbPath, password, tableName) {
|
|
236
|
+
let db = null;
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
db = await connectDatabase(dbPath, password);
|
|
240
|
+
return await getTableInfo(db, tableName);
|
|
241
|
+
} finally {
|
|
242
|
+
if (db) {
|
|
243
|
+
closeConnection(db);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Explain query plan
|
|
250
|
+
* @param {string} dbPath - Path to the database file
|
|
251
|
+
* @param {string|undefined} password - Database password (optional)
|
|
252
|
+
* @param {string} query - SQL query
|
|
253
|
+
* @returns {Promise<Array>} Query execution plan
|
|
254
|
+
*/
|
|
255
|
+
export async function explainQueryPlanFromDatabase(dbPath, password, query) {
|
|
256
|
+
let db = null;
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
db = await connectDatabase(dbPath, password);
|
|
260
|
+
return await explainQueryPlan(db, query);
|
|
261
|
+
} finally {
|
|
262
|
+
if (db) {
|
|
263
|
+
closeConnection(db);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get table statistics
|
|
270
|
+
* @param {string} dbPath - Path to the database file
|
|
271
|
+
* @param {string|undefined} password - Database password (optional)
|
|
272
|
+
* @param {string} tableName - Table name
|
|
273
|
+
* @param {number} maxSampleSize - Maximum sample size
|
|
274
|
+
* @returns {Promise<Object>} Table statistics
|
|
275
|
+
*/
|
|
276
|
+
export async function getTableStatisticsFromDatabase(dbPath, password, tableName, maxSampleSize = 10000) {
|
|
277
|
+
let db = null;
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
db = await connectDatabase(dbPath, password);
|
|
281
|
+
return await getTableStatistics(db, tableName, maxSampleSize);
|
|
282
|
+
} finally {
|
|
283
|
+
if (db) {
|
|
284
|
+
closeConnection(db);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Sample table data
|
|
291
|
+
* @param {string} dbPath - Path to the database file
|
|
292
|
+
* @param {string|undefined} password - Database password (optional)
|
|
293
|
+
* @param {string} tableName - Table name
|
|
294
|
+
* @param {number} limit - Row limit
|
|
295
|
+
* @param {number} offset - Row offset
|
|
296
|
+
* @param {string[]} columns - Optional column filter
|
|
297
|
+
* @returns {Promise<Object>} Sample data
|
|
298
|
+
*/
|
|
299
|
+
export async function sampleTableDataFromDatabase(dbPath, password, tableName, limit = 10, offset = 0, columns = null) {
|
|
300
|
+
let db = null;
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
db = await connectDatabase(dbPath, password);
|
|
304
|
+
return await sampleTableData(db, tableName, limit, offset, columns);
|
|
305
|
+
} finally {
|
|
306
|
+
if (db) {
|
|
307
|
+
closeConnection(db);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get column statistics
|
|
314
|
+
* @param {string} dbPath - Path to the database file
|
|
315
|
+
* @param {string|undefined} password - Database password (optional)
|
|
316
|
+
* @param {string} tableName - Table name
|
|
317
|
+
* @param {string|string[]} columnName - Column name or array of column names
|
|
318
|
+
* @param {number} maxSampleSize - Maximum sample size
|
|
319
|
+
* @returns {Promise<Array>} Column statistics
|
|
320
|
+
*/
|
|
321
|
+
export async function getColumnStatisticsFromDatabase(dbPath, password, tableName, columnName, maxSampleSize = 10000) {
|
|
322
|
+
let db = null;
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
db = await connectDatabase(dbPath, password);
|
|
326
|
+
|
|
327
|
+
// Handle single column or array
|
|
328
|
+
const columnNames = Array.isArray(columnName) ? columnName : [columnName];
|
|
329
|
+
return await getColumnStatistics(db, tableName, columnNames, maxSampleSize);
|
|
330
|
+
} finally {
|
|
331
|
+
if (db) {
|
|
332
|
+
closeConnection(db);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Search tables
|
|
339
|
+
* @param {string} dbPath - Path to the database file
|
|
340
|
+
* @param {string|undefined} password - Database password (optional)
|
|
341
|
+
* @param {string} pattern - Search pattern
|
|
342
|
+
* @returns {Promise<Array>} Matching tables
|
|
343
|
+
*/
|
|
344
|
+
export async function searchTablesInDatabase(dbPath, password, pattern) {
|
|
345
|
+
let db = null;
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
db = await connectDatabase(dbPath, password);
|
|
349
|
+
return await searchTables(db, pattern);
|
|
350
|
+
} finally {
|
|
351
|
+
if (db) {
|
|
352
|
+
closeConnection(db);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Search columns
|
|
359
|
+
* @param {string} dbPath - Path to the database file
|
|
360
|
+
* @param {string|undefined} password - Database password (optional)
|
|
361
|
+
* @param {string} pattern - Search pattern
|
|
362
|
+
* @returns {Promise<Array>} Matching columns
|
|
363
|
+
*/
|
|
364
|
+
export async function searchColumnsInDatabase(dbPath, password, pattern) {
|
|
365
|
+
let db = null;
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
db = await connectDatabase(dbPath, password);
|
|
369
|
+
return await searchColumns(db, pattern);
|
|
370
|
+
} finally {
|
|
371
|
+
if (db) {
|
|
372
|
+
closeConnection(db);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Find related tables
|
|
379
|
+
* @param {string} dbPath - Path to the database file
|
|
380
|
+
* @param {string|undefined} password - Database password (optional)
|
|
381
|
+
* @param {string} tableName - Table name
|
|
382
|
+
* @returns {Promise<Object>} Related tables information
|
|
383
|
+
*/
|
|
384
|
+
export async function findRelatedTablesInDatabase(dbPath, password, tableName) {
|
|
385
|
+
let db = null;
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
db = await connectDatabase(dbPath, password);
|
|
389
|
+
return await findRelatedTables(db, tableName);
|
|
390
|
+
} finally {
|
|
391
|
+
if (db) {
|
|
392
|
+
closeConnection(db);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|