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,69 +1,558 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Tool Handlers
|
|
3
|
-
* Handlers for MCP tool requests
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { TOOL_DEFINITIONS } from '../
|
|
7
|
-
import { getDatabasePassword } from '../config/environment.js';
|
|
8
|
-
import {
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*
|
|
64
|
-
* @param {string}
|
|
65
|
-
* @
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Handlers
|
|
3
|
+
* Handlers for MCP tool requests
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { TOOL_DEFINITIONS } from '../definitions/tools.js';
|
|
7
|
+
import { getDatabasePassword } from '../config/environment.js';
|
|
8
|
+
import {
|
|
9
|
+
validateArguments,
|
|
10
|
+
validateQuery,
|
|
11
|
+
resolveDatabasePath,
|
|
12
|
+
validateTableName,
|
|
13
|
+
validateColumnName,
|
|
14
|
+
validatePattern,
|
|
15
|
+
validateNumericParameter
|
|
16
|
+
} from '../utils/validators.js';
|
|
17
|
+
import {
|
|
18
|
+
formatQueryResults,
|
|
19
|
+
formatTableList,
|
|
20
|
+
formatTableSchema,
|
|
21
|
+
formatForeignKeys,
|
|
22
|
+
formatIndexes,
|
|
23
|
+
formatDatabaseInfo,
|
|
24
|
+
formatTableInfo,
|
|
25
|
+
formatQueryPlan,
|
|
26
|
+
formatTableStatistics,
|
|
27
|
+
formatSampleData,
|
|
28
|
+
formatColumnStatistics,
|
|
29
|
+
formatSearchResults,
|
|
30
|
+
formatRelatedTables
|
|
31
|
+
} from '../utils/formatters.js';
|
|
32
|
+
import { createMcpErrorResponse, createMcpSuccessResponse } from '../utils/errors.js';
|
|
33
|
+
import {
|
|
34
|
+
executeQueryOnDatabase,
|
|
35
|
+
testDatabaseConnection,
|
|
36
|
+
getTableListFromDatabase,
|
|
37
|
+
getTableSchemaFromDatabase,
|
|
38
|
+
getForeignKeysFromDatabase,
|
|
39
|
+
getIndexesFromDatabase,
|
|
40
|
+
getDatabaseInfoFromDatabase,
|
|
41
|
+
getTableInfoFromDatabase,
|
|
42
|
+
explainQueryPlanFromDatabase,
|
|
43
|
+
getTableStatisticsFromDatabase,
|
|
44
|
+
sampleTableDataFromDatabase,
|
|
45
|
+
getColumnStatisticsFromDatabase,
|
|
46
|
+
searchTablesInDatabase,
|
|
47
|
+
searchColumnsInDatabase,
|
|
48
|
+
findRelatedTablesInDatabase
|
|
49
|
+
} from '../services/database-service.js';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Handle list tools request
|
|
53
|
+
* @returns {Object} List of available tools
|
|
54
|
+
*/
|
|
55
|
+
export function handleListTools() {
|
|
56
|
+
return {
|
|
57
|
+
tools: Object.values(TOOL_DEFINITIONS),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Handle execute_query tool request
|
|
63
|
+
* @param {Object} args - Tool arguments
|
|
64
|
+
* @param {string} [args.database_path] - Database path (optional if env var set)
|
|
65
|
+
* @param {string} args.query - SQL query to execute
|
|
66
|
+
* @returns {Promise<Object>} MCP response object
|
|
67
|
+
*/
|
|
68
|
+
export async function handleExecuteQuery(args) {
|
|
69
|
+
try {
|
|
70
|
+
// Validate arguments
|
|
71
|
+
validateArguments(args);
|
|
72
|
+
|
|
73
|
+
const { database_path, query } = args;
|
|
74
|
+
|
|
75
|
+
// Validate query
|
|
76
|
+
validateQuery(query);
|
|
77
|
+
|
|
78
|
+
// Resolve database path
|
|
79
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
80
|
+
|
|
81
|
+
// Get database password
|
|
82
|
+
const password = getDatabasePassword();
|
|
83
|
+
|
|
84
|
+
// Execute query
|
|
85
|
+
try {
|
|
86
|
+
const result = await executeQueryOnDatabase(dbPath, password, query);
|
|
87
|
+
|
|
88
|
+
// Format results for response
|
|
89
|
+
const responseText = formatQueryResults(result);
|
|
90
|
+
|
|
91
|
+
return createMcpSuccessResponse(responseText);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return createMcpErrorResponse(`Query execution failed: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Handle list_tables tool request
|
|
102
|
+
* @param {Object} args - Tool arguments
|
|
103
|
+
* @returns {Promise<Object>} MCP response object
|
|
104
|
+
*/
|
|
105
|
+
export async function handleListTables(args) {
|
|
106
|
+
try {
|
|
107
|
+
validateArguments(args);
|
|
108
|
+
|
|
109
|
+
const { database_path, table_names } = args;
|
|
110
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
111
|
+
const password = getDatabasePassword();
|
|
112
|
+
|
|
113
|
+
const tables = await getTableListFromDatabase(dbPath, password, table_names);
|
|
114
|
+
const responseText = formatTableList(tables);
|
|
115
|
+
|
|
116
|
+
return createMcpSuccessResponse(responseText);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Handle get_table_schema tool request
|
|
124
|
+
* @param {Object} args - Tool arguments
|
|
125
|
+
* @returns {Promise<Object>} MCP response object
|
|
126
|
+
*/
|
|
127
|
+
export async function handleGetTableSchema(args) {
|
|
128
|
+
try {
|
|
129
|
+
validateArguments(args);
|
|
130
|
+
validateTableName(args.table_name);
|
|
131
|
+
|
|
132
|
+
const { database_path, table_name } = args;
|
|
133
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
134
|
+
const password = getDatabasePassword();
|
|
135
|
+
|
|
136
|
+
const schema = await getTableSchemaFromDatabase(dbPath, password, table_name);
|
|
137
|
+
const responseText = formatTableSchema(schema);
|
|
138
|
+
|
|
139
|
+
return createMcpSuccessResponse(responseText);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Handle list_columns tool request
|
|
147
|
+
* @param {Object} args - Tool arguments
|
|
148
|
+
* @returns {Promise<Object>} MCP response object
|
|
149
|
+
*/
|
|
150
|
+
export async function handleListColumns(args) {
|
|
151
|
+
try {
|
|
152
|
+
validateArguments(args);
|
|
153
|
+
validateTableName(args.table_name);
|
|
154
|
+
|
|
155
|
+
const { database_path, table_name } = args;
|
|
156
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
157
|
+
const password = getDatabasePassword();
|
|
158
|
+
|
|
159
|
+
const schema = await getTableSchemaFromDatabase(dbPath, password, table_name);
|
|
160
|
+
const responseText = formatTableSchema(schema);
|
|
161
|
+
|
|
162
|
+
return createMcpSuccessResponse(responseText);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Handle get_foreign_keys tool request
|
|
170
|
+
* @param {Object} args - Tool arguments
|
|
171
|
+
* @returns {Promise<Object>} MCP response object
|
|
172
|
+
*/
|
|
173
|
+
export async function handleGetForeignKeys(args) {
|
|
174
|
+
try {
|
|
175
|
+
validateArguments(args);
|
|
176
|
+
|
|
177
|
+
const { database_path, table_name } = args;
|
|
178
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
179
|
+
const password = getDatabasePassword();
|
|
180
|
+
|
|
181
|
+
const foreignKeys = await getForeignKeysFromDatabase(dbPath, password, table_name);
|
|
182
|
+
const responseText = formatForeignKeys(foreignKeys);
|
|
183
|
+
|
|
184
|
+
return createMcpSuccessResponse(responseText);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Handle get_indexes tool request
|
|
192
|
+
* @param {Object} args - Tool arguments
|
|
193
|
+
* @returns {Promise<Object>} MCP response object
|
|
194
|
+
*/
|
|
195
|
+
export async function handleGetIndexes(args) {
|
|
196
|
+
try {
|
|
197
|
+
validateArguments(args);
|
|
198
|
+
|
|
199
|
+
const { database_path, table_name } = args;
|
|
200
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
201
|
+
const password = getDatabasePassword();
|
|
202
|
+
|
|
203
|
+
const indexes = await getIndexesFromDatabase(dbPath, password, table_name);
|
|
204
|
+
const responseText = formatIndexes(indexes);
|
|
205
|
+
|
|
206
|
+
return createMcpSuccessResponse(responseText);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Handle get_database_info tool request
|
|
214
|
+
* @param {Object} args - Tool arguments
|
|
215
|
+
* @returns {Promise<Object>} MCP response object
|
|
216
|
+
*/
|
|
217
|
+
export async function handleGetDatabaseInfo(args) {
|
|
218
|
+
try {
|
|
219
|
+
validateArguments(args);
|
|
220
|
+
|
|
221
|
+
const { database_path } = args;
|
|
222
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
223
|
+
const password = getDatabasePassword();
|
|
224
|
+
|
|
225
|
+
const info = await getDatabaseInfoFromDatabase(dbPath, password);
|
|
226
|
+
const responseText = formatDatabaseInfo(info);
|
|
227
|
+
|
|
228
|
+
return createMcpSuccessResponse(responseText);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Handle get_table_info tool request
|
|
236
|
+
* @param {Object} args - Tool arguments
|
|
237
|
+
* @returns {Promise<Object>} MCP response object
|
|
238
|
+
*/
|
|
239
|
+
export async function handleGetTableInfo(args) {
|
|
240
|
+
try {
|
|
241
|
+
validateArguments(args);
|
|
242
|
+
validateTableName(args.table_name);
|
|
243
|
+
|
|
244
|
+
const { database_path, table_name } = args;
|
|
245
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
246
|
+
const password = getDatabasePassword();
|
|
247
|
+
|
|
248
|
+
const info = await getTableInfoFromDatabase(dbPath, password, table_name);
|
|
249
|
+
const responseText = formatTableInfo(info);
|
|
250
|
+
|
|
251
|
+
return createMcpSuccessResponse(responseText);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Handle test_connection tool request
|
|
259
|
+
* @param {Object} args - Tool arguments
|
|
260
|
+
* @returns {Promise<Object>} MCP response object
|
|
261
|
+
*/
|
|
262
|
+
export async function handleTestConnection(args) {
|
|
263
|
+
try {
|
|
264
|
+
validateArguments(args);
|
|
265
|
+
|
|
266
|
+
const { database_path } = args;
|
|
267
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
268
|
+
const password = getDatabasePassword();
|
|
269
|
+
|
|
270
|
+
await testDatabaseConnection(dbPath, password);
|
|
271
|
+
const responseText = 'Database connection successful.';
|
|
272
|
+
|
|
273
|
+
return createMcpSuccessResponse(responseText);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
return createMcpErrorResponse(`Connection test failed: ${error.message}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Handle explain_query tool request
|
|
281
|
+
* @param {Object} args - Tool arguments
|
|
282
|
+
* @returns {Promise<Object>} MCP response object
|
|
283
|
+
*/
|
|
284
|
+
export async function handleExplainQuery(args) {
|
|
285
|
+
try {
|
|
286
|
+
validateArguments(args);
|
|
287
|
+
validateQuery(args.query);
|
|
288
|
+
|
|
289
|
+
const { database_path, query } = args;
|
|
290
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
291
|
+
const password = getDatabasePassword();
|
|
292
|
+
|
|
293
|
+
const plan = await explainQueryPlanFromDatabase(dbPath, password, query);
|
|
294
|
+
const responseText = formatQueryPlan(plan);
|
|
295
|
+
|
|
296
|
+
return createMcpSuccessResponse(responseText);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Handle validate_query_syntax tool request
|
|
304
|
+
* @param {Object} args - Tool arguments
|
|
305
|
+
* @returns {Promise<Object>} MCP response object
|
|
306
|
+
*/
|
|
307
|
+
export async function handleValidateQuerySyntax(args) {
|
|
308
|
+
try {
|
|
309
|
+
validateArguments(args);
|
|
310
|
+
validateQuery(args.query);
|
|
311
|
+
|
|
312
|
+
const { database_path, query } = args;
|
|
313
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
314
|
+
const password = getDatabasePassword();
|
|
315
|
+
|
|
316
|
+
// Try to explain the query - if it succeeds, syntax is valid
|
|
317
|
+
await explainQueryPlanFromDatabase(dbPath, password, query);
|
|
318
|
+
const responseText = 'Query syntax is valid.';
|
|
319
|
+
|
|
320
|
+
return createMcpSuccessResponse(responseText);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
return createMcpErrorResponse(`Query syntax validation failed: ${error.message}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Handle suggest_query tool request
|
|
328
|
+
* @param {Object} args - Tool arguments
|
|
329
|
+
* @returns {Promise<Object>} MCP response object
|
|
330
|
+
*/
|
|
331
|
+
export async function handleSuggestQuery(args) {
|
|
332
|
+
try {
|
|
333
|
+
validateArguments(args);
|
|
334
|
+
|
|
335
|
+
const { database_path, table_name, intent } = args;
|
|
336
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
337
|
+
const password = getDatabasePassword();
|
|
338
|
+
|
|
339
|
+
let suggestions = [];
|
|
340
|
+
|
|
341
|
+
if (table_name) {
|
|
342
|
+
// Get table schema to build suggestions
|
|
343
|
+
const schema = await getTableSchemaFromDatabase(dbPath, password, table_name);
|
|
344
|
+
const columns = schema.columns.map(c => c.name).join(', ');
|
|
345
|
+
|
|
346
|
+
switch (intent) {
|
|
347
|
+
case 'count':
|
|
348
|
+
suggestions.push(`SELECT COUNT(*) FROM "${table_name}"`);
|
|
349
|
+
break;
|
|
350
|
+
case 'sample':
|
|
351
|
+
suggestions.push(`SELECT ${columns} FROM "${table_name}" LIMIT 10`);
|
|
352
|
+
break;
|
|
353
|
+
case 'aggregate':
|
|
354
|
+
const numericCols = schema.columns.filter(c =>
|
|
355
|
+
c.type && (c.type.toUpperCase().includes('INT') ||
|
|
356
|
+
c.type.toUpperCase().includes('REAL') ||
|
|
357
|
+
c.type.toUpperCase().includes('NUMERIC'))
|
|
358
|
+
);
|
|
359
|
+
if (numericCols.length > 0) {
|
|
360
|
+
const col = numericCols[0].name;
|
|
361
|
+
suggestions.push(`SELECT MIN("${col}"), MAX("${col}"), AVG("${col}") FROM "${table_name}"`);
|
|
362
|
+
}
|
|
363
|
+
break;
|
|
364
|
+
case 'search':
|
|
365
|
+
const textCols = schema.columns.filter(c =>
|
|
366
|
+
!c.type || c.type.toUpperCase().includes('TEXT') ||
|
|
367
|
+
c.type.toUpperCase().includes('VARCHAR') ||
|
|
368
|
+
c.type.toUpperCase().includes('CHAR')
|
|
369
|
+
);
|
|
370
|
+
if (textCols.length > 0) {
|
|
371
|
+
const col = textCols[0].name;
|
|
372
|
+
suggestions.push(`SELECT ${columns} FROM "${table_name}" WHERE "${col}" LIKE '%search_term%'`);
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
375
|
+
case 'join':
|
|
376
|
+
if (schema.foreign_keys && schema.foreign_keys.length > 0) {
|
|
377
|
+
const fk = schema.foreign_keys[0];
|
|
378
|
+
suggestions.push(`SELECT * FROM "${table_name}" t1 JOIN "${fk.table}" t2 ON t1."${fk.from}" = t2."${fk.to}"`);
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
default:
|
|
382
|
+
suggestions.push(`SELECT ${columns} FROM "${table_name}"`);
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
// General suggestions
|
|
386
|
+
suggestions.push('SELECT * FROM table_name LIMIT 10');
|
|
387
|
+
suggestions.push('SELECT COUNT(*) FROM table_name');
|
|
388
|
+
suggestions.push('SELECT column_name FROM table_name WHERE condition');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
let responseText = 'Query Suggestions:\n\n';
|
|
392
|
+
suggestions.forEach((s, i) => {
|
|
393
|
+
responseText += `${i + 1}. ${s}\n`;
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
responseText += '\n\nJSON representation:\n';
|
|
397
|
+
responseText += JSON.stringify({ suggestions }, null, 2);
|
|
398
|
+
|
|
399
|
+
return createMcpSuccessResponse(responseText);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Handle get_table_statistics tool request
|
|
407
|
+
* @param {Object} args - Tool arguments
|
|
408
|
+
* @returns {Promise<Object>} MCP response object
|
|
409
|
+
*/
|
|
410
|
+
export async function handleGetTableStatistics(args) {
|
|
411
|
+
try {
|
|
412
|
+
validateArguments(args);
|
|
413
|
+
validateTableName(args.table_name);
|
|
414
|
+
|
|
415
|
+
const { database_path, table_name, max_sample_size, timeout_ms } = args;
|
|
416
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
417
|
+
const password = getDatabasePassword();
|
|
418
|
+
|
|
419
|
+
const maxSample = validateNumericParameter(max_sample_size, 'max_sample_size', 1, 1000000) || 10000;
|
|
420
|
+
|
|
421
|
+
const stats = await getTableStatisticsFromDatabase(dbPath, password, table_name, maxSample);
|
|
422
|
+
const responseText = formatTableStatistics(stats);
|
|
423
|
+
|
|
424
|
+
return createMcpSuccessResponse(responseText);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Handle sample_table_data tool request
|
|
432
|
+
* @param {Object} args - Tool arguments
|
|
433
|
+
* @returns {Promise<Object>} MCP response object
|
|
434
|
+
*/
|
|
435
|
+
export async function handleSampleTableData(args) {
|
|
436
|
+
try {
|
|
437
|
+
validateArguments(args);
|
|
438
|
+
validateTableName(args.table_name);
|
|
439
|
+
|
|
440
|
+
const { database_path, table_name, limit, offset, columns } = args;
|
|
441
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
442
|
+
const password = getDatabasePassword();
|
|
443
|
+
|
|
444
|
+
const limitNum = validateNumericParameter(limit, 'limit', 1, 10000) || 10;
|
|
445
|
+
const offsetNum = validateNumericParameter(offset, 'offset', 0, Number.MAX_SAFE_INTEGER) || 0;
|
|
446
|
+
|
|
447
|
+
const sample = await sampleTableDataFromDatabase(dbPath, password, table_name, limitNum, offsetNum, columns);
|
|
448
|
+
const responseText = formatSampleData(sample);
|
|
449
|
+
|
|
450
|
+
return createMcpSuccessResponse(responseText);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Handle get_column_statistics tool request
|
|
458
|
+
* @param {Object} args - Tool arguments
|
|
459
|
+
* @returns {Promise<Object>} MCP response object
|
|
460
|
+
*/
|
|
461
|
+
export async function handleGetColumnStatistics(args) {
|
|
462
|
+
try {
|
|
463
|
+
validateArguments(args);
|
|
464
|
+
validateTableName(args.table_name);
|
|
465
|
+
validateColumnName(args.column_name);
|
|
466
|
+
|
|
467
|
+
const { database_path, table_name, column_name, max_sample_size } = args;
|
|
468
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
469
|
+
const password = getDatabasePassword();
|
|
470
|
+
|
|
471
|
+
const maxSample = validateNumericParameter(max_sample_size, 'max_sample_size', 1, 1000000) || 10000;
|
|
472
|
+
|
|
473
|
+
const stats = await getColumnStatisticsFromDatabase(dbPath, password, table_name, column_name, maxSample);
|
|
474
|
+
const responseText = formatColumnStatistics(stats);
|
|
475
|
+
|
|
476
|
+
return createMcpSuccessResponse(responseText);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Handle search_tables tool request
|
|
484
|
+
* @param {Object} args - Tool arguments
|
|
485
|
+
* @returns {Promise<Object>} MCP response object
|
|
486
|
+
*/
|
|
487
|
+
export async function handleSearchTables(args) {
|
|
488
|
+
try {
|
|
489
|
+
validateArguments(args);
|
|
490
|
+
validatePattern(args.pattern);
|
|
491
|
+
|
|
492
|
+
const { database_path, pattern } = args;
|
|
493
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
494
|
+
const password = getDatabasePassword();
|
|
495
|
+
|
|
496
|
+
const results = await searchTablesInDatabase(dbPath, password, pattern);
|
|
497
|
+
const responseText = formatSearchResults(results, 'tables');
|
|
498
|
+
|
|
499
|
+
return createMcpSuccessResponse(responseText);
|
|
500
|
+
} catch (error) {
|
|
501
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Handle search_columns tool request
|
|
507
|
+
* @param {Object} args - Tool arguments
|
|
508
|
+
* @returns {Promise<Object>} MCP response object
|
|
509
|
+
*/
|
|
510
|
+
export async function handleSearchColumns(args) {
|
|
511
|
+
try {
|
|
512
|
+
validateArguments(args);
|
|
513
|
+
validatePattern(args.pattern);
|
|
514
|
+
|
|
515
|
+
const { database_path, pattern } = args;
|
|
516
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
517
|
+
const password = getDatabasePassword();
|
|
518
|
+
|
|
519
|
+
const results = await searchColumnsInDatabase(dbPath, password, pattern);
|
|
520
|
+
const responseText = formatSearchResults(results, 'columns');
|
|
521
|
+
|
|
522
|
+
return createMcpSuccessResponse(responseText);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Handle find_related_tables tool request
|
|
530
|
+
* @param {Object} args - Tool arguments
|
|
531
|
+
* @returns {Promise<Object>} MCP response object
|
|
532
|
+
*/
|
|
533
|
+
export async function handleFindRelatedTables(args) {
|
|
534
|
+
try {
|
|
535
|
+
validateArguments(args);
|
|
536
|
+
validateTableName(args.table_name);
|
|
537
|
+
|
|
538
|
+
const { database_path, table_name } = args;
|
|
539
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
540
|
+
const password = getDatabasePassword();
|
|
541
|
+
|
|
542
|
+
const related = await findRelatedTablesInDatabase(dbPath, password, table_name);
|
|
543
|
+
const responseText = formatRelatedTables(related);
|
|
544
|
+
|
|
545
|
+
return createMcpSuccessResponse(responseText);
|
|
546
|
+
} catch (error) {
|
|
547
|
+
return createMcpErrorResponse(`Error: ${error.message}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Handle unknown tool request
|
|
553
|
+
* @param {string} toolName - Name of the unknown tool
|
|
554
|
+
* @returns {Object} Error response
|
|
555
|
+
*/
|
|
556
|
+
export function handleUnknownTool(toolName) {
|
|
557
|
+
return createMcpErrorResponse(`Unknown tool: ${toolName}`);
|
|
558
|
+
}
|