sqlcipher-mcp-server 1.0.0 → 1.0.3
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 +210 -282
- package/index.js +14 -226
- package/package.json +29 -27
- package/server-http.js +14 -0
- package/src/config/constants.js +40 -0
- package/src/config/environment.js +63 -0
- package/src/handlers/http-handlers.js +89 -0
- package/src/handlers/mcp-handlers.js +69 -0
- package/src/server/http-server.js +54 -0
- package/src/server/mcp-server.js +76 -0
- package/src/services/database-service.js +55 -0
- package/src/utils/errors.js +69 -0
- package/src/utils/formatters.js +64 -0
- package/src/utils/validators.js +58 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server
|
|
3
|
+
* Model Context Protocol server setup and initialization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import {
|
|
9
|
+
CallToolRequestSchema,
|
|
10
|
+
ListToolsRequestSchema,
|
|
11
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
12
|
+
|
|
13
|
+
import { SERVER_CONFIG } from '../config/constants.js';
|
|
14
|
+
import { getConfigurationWarnings } from '../config/environment.js';
|
|
15
|
+
import { handleListTools, handleExecuteQuery, handleUnknownTool } from '../handlers/mcp-handlers.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create and configure MCP server
|
|
19
|
+
* @returns {Server} Configured MCP server instance
|
|
20
|
+
*/
|
|
21
|
+
export function createMcpServer() {
|
|
22
|
+
const server = new Server(
|
|
23
|
+
{
|
|
24
|
+
name: SERVER_CONFIG.name,
|
|
25
|
+
version: SERVER_CONFIG.version,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
capabilities: {
|
|
29
|
+
tools: {},
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Register list tools handler
|
|
35
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
36
|
+
return handleListTools();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Register tool execution handler
|
|
40
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
41
|
+
const { name, arguments: args } = request.params;
|
|
42
|
+
|
|
43
|
+
if (name === 'execute_query') {
|
|
44
|
+
return await handleExecuteQuery(args);
|
|
45
|
+
} else {
|
|
46
|
+
return handleUnknownTool(name);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return server;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Start the MCP server
|
|
55
|
+
* @returns {Promise<void>}
|
|
56
|
+
*/
|
|
57
|
+
export async function startMcpServer() {
|
|
58
|
+
// Check configuration and log warnings
|
|
59
|
+
const warnings = getConfigurationWarnings();
|
|
60
|
+
if (warnings.length > 0) {
|
|
61
|
+
console.error('Warning: ' + warnings.join(' '));
|
|
62
|
+
} else {
|
|
63
|
+
console.error('Configuration loaded: Database path and password set via environment variables.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Create server
|
|
67
|
+
const server = createMcpServer();
|
|
68
|
+
|
|
69
|
+
// Create stdio transport
|
|
70
|
+
const transport = new StdioServerTransport();
|
|
71
|
+
|
|
72
|
+
// Connect server to transport
|
|
73
|
+
await server.connect(transport);
|
|
74
|
+
|
|
75
|
+
console.error('SQLCipher MCP Server running on stdio');
|
|
76
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Service
|
|
3
|
+
* Service layer that wraps database operations with error handling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { connectDatabase, executeQuery, closeConnection } from '../../lib/database.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Execute a query on a database
|
|
10
|
+
* Handles connection, query execution, and cleanup
|
|
11
|
+
* @param {string} dbPath - Path to the database file
|
|
12
|
+
* @param {string|undefined} password - Database password (optional)
|
|
13
|
+
* @param {string} query - SQL query to execute
|
|
14
|
+
* @returns {Promise<Object>} Query results
|
|
15
|
+
* @throws {Error} If connection or query execution fails
|
|
16
|
+
*/
|
|
17
|
+
export async function executeQueryOnDatabase(dbPath, password, query) {
|
|
18
|
+
let db = null;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Connect to database
|
|
22
|
+
db = await connectDatabase(dbPath, password);
|
|
23
|
+
|
|
24
|
+
// Execute query
|
|
25
|
+
const result = await executeQuery(db, query);
|
|
26
|
+
|
|
27
|
+
return result;
|
|
28
|
+
} finally {
|
|
29
|
+
// Always close the database connection
|
|
30
|
+
if (db) {
|
|
31
|
+
closeConnection(db);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Test database connection
|
|
38
|
+
* @param {string} dbPath - Path to the database file
|
|
39
|
+
* @param {string|undefined} password - Database password (optional)
|
|
40
|
+
* @returns {Promise<boolean>} True if connection successful
|
|
41
|
+
*/
|
|
42
|
+
export async function testDatabaseConnection(dbPath, password) {
|
|
43
|
+
let db = null;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
db = await connectDatabase(dbPath, password);
|
|
47
|
+
return true;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw new Error(`Failed to connect to database: ${error.message}`);
|
|
50
|
+
} finally {
|
|
51
|
+
if (db) {
|
|
52
|
+
closeConnection(db);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
* Standardized error response creation for MCP and HTTP
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create MCP error response
|
|
8
|
+
* @param {string} message - Error message
|
|
9
|
+
* @returns {Object} MCP error response object
|
|
10
|
+
*/
|
|
11
|
+
export function createMcpErrorResponse(message) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: message,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
isError: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create MCP success response
|
|
25
|
+
* @param {string} text - Response text
|
|
26
|
+
* @returns {Object} MCP success response object
|
|
27
|
+
*/
|
|
28
|
+
export function createMcpSuccessResponse(text) {
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: text,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create HTTP error response
|
|
41
|
+
* @param {number} statusCode - HTTP status code
|
|
42
|
+
* @param {string} message - Error message
|
|
43
|
+
* @returns {Object} HTTP error response object
|
|
44
|
+
*/
|
|
45
|
+
export function createHttpErrorResponse(statusCode, message) {
|
|
46
|
+
return {
|
|
47
|
+
statusCode,
|
|
48
|
+
body: {
|
|
49
|
+
error: message,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create HTTP success response
|
|
56
|
+
* @param {Object} data - Response data
|
|
57
|
+
* @param {string} message - Success message
|
|
58
|
+
* @returns {Object} HTTP success response object
|
|
59
|
+
*/
|
|
60
|
+
export function createHttpSuccessResponse(data, message) {
|
|
61
|
+
return {
|
|
62
|
+
statusCode: 200,
|
|
63
|
+
body: {
|
|
64
|
+
success: true,
|
|
65
|
+
data: data,
|
|
66
|
+
message: message,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting Utilities
|
|
3
|
+
* Functions for formatting query results and other data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { QUERY_CONFIG } from '../config/constants.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Format query results as a readable string
|
|
10
|
+
* @param {Object} result - Query result object with columns, rows, and rowCount
|
|
11
|
+
* @param {string[]} result.columns - Array of column names
|
|
12
|
+
* @param {Object[]} result.rows - Array of row objects
|
|
13
|
+
* @param {number} result.rowCount - Number of rows returned
|
|
14
|
+
* @returns {string} Formatted result string
|
|
15
|
+
*/
|
|
16
|
+
export function formatQueryResults(result) {
|
|
17
|
+
const { columns, rows, rowCount } = result;
|
|
18
|
+
|
|
19
|
+
if (rowCount === 0) {
|
|
20
|
+
return `Query executed successfully. No rows returned.\nColumns: ${columns.join(', ')}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Build table-like output
|
|
24
|
+
let output = `Query executed successfully. ${rowCount} row(s) returned.\n\n`;
|
|
25
|
+
|
|
26
|
+
// Add column headers
|
|
27
|
+
output += `Columns: ${columns.join(' | ')}\n`;
|
|
28
|
+
output += '-'.repeat(columns.join(' | ').length) + '\n';
|
|
29
|
+
|
|
30
|
+
// Add rows (limit to first maxDisplayRows for display)
|
|
31
|
+
const displayRows = rows.slice(0, QUERY_CONFIG.maxDisplayRows);
|
|
32
|
+
for (const row of displayRows) {
|
|
33
|
+
const values = columns.map(col => formatCellValue(row[col]));
|
|
34
|
+
output += values.join(' | ') + '\n';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (rows.length > QUERY_CONFIG.maxDisplayRows) {
|
|
38
|
+
output += `\n... (showing first ${QUERY_CONFIG.maxDisplayRows} of ${rowCount} rows)`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Add JSON representation for programmatic access
|
|
42
|
+
output += '\n\nJSON representation:\n';
|
|
43
|
+
output += JSON.stringify(result, null, 2);
|
|
44
|
+
|
|
45
|
+
return output;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format a single cell value for display
|
|
50
|
+
* @param {any} value - Cell value to format
|
|
51
|
+
* @returns {string} Formatted cell value
|
|
52
|
+
*/
|
|
53
|
+
function formatCellValue(value) {
|
|
54
|
+
// Handle null/undefined
|
|
55
|
+
if (value === null || value === undefined) {
|
|
56
|
+
return 'NULL';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Convert to string and truncate long values
|
|
60
|
+
const str = String(value);
|
|
61
|
+
return str.length > QUERY_CONFIG.maxValueLength
|
|
62
|
+
? str.substring(0, QUERY_CONFIG.maxValueLength - 3) + '...'
|
|
63
|
+
: str;
|
|
64
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
* Input validation and sanitization functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getDatabasePath } from '../config/environment.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate that arguments is a valid object
|
|
10
|
+
* @param {any} args - Arguments to validate
|
|
11
|
+
* @throws {Error} If args is not a valid object
|
|
12
|
+
*/
|
|
13
|
+
export function validateArguments(args) {
|
|
14
|
+
if (!args || typeof args !== 'object') {
|
|
15
|
+
throw new Error('Invalid arguments: arguments must be an object');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validate query parameter
|
|
21
|
+
* @param {any} query - Query to validate
|
|
22
|
+
* @throws {Error} If query is invalid
|
|
23
|
+
*/
|
|
24
|
+
export function validateQuery(query) {
|
|
25
|
+
if (!query || typeof query !== 'string') {
|
|
26
|
+
throw new Error('query is required and must be a string');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate and resolve database path
|
|
32
|
+
* Uses provided path or falls back to environment variable
|
|
33
|
+
* @param {string|undefined} providedPath - Database path from arguments
|
|
34
|
+
* @returns {string} Resolved database path
|
|
35
|
+
* @throws {Error} If no valid path is available
|
|
36
|
+
*/
|
|
37
|
+
export function resolveDatabasePath(providedPath) {
|
|
38
|
+
const dbPath = providedPath || getDatabasePath();
|
|
39
|
+
|
|
40
|
+
if (!dbPath || typeof dbPath !== 'string') {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'database_path is required. Provide it as a parameter or set SQLCIPHER_DATABASE_PATH environment variable.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return dbPath;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validate database path parameter for HTTP requests
|
|
51
|
+
* @param {any} database_path - Database path to validate
|
|
52
|
+
* @throws {Error} If database_path is invalid
|
|
53
|
+
*/
|
|
54
|
+
export function validateDatabasePath(database_path) {
|
|
55
|
+
if (!database_path || typeof database_path !== 'string') {
|
|
56
|
+
throw new Error('database_path is required and must be a string');
|
|
57
|
+
}
|
|
58
|
+
}
|