sqlcipher-mcp-server 1.0.2 → 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/index.js CHANGED
@@ -1,241 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
- import {
6
- CallToolRequestSchema,
7
- ListToolsRequestSchema,
8
- } from '@modelcontextprotocol/sdk/types.js';
9
- import { connectDatabase, executeQuery, closeConnection } from './lib/database.js';
10
-
11
- /**
12
- * SQLCipher MCP Server
13
- * Provides read-only access to SQLCipher-encrypted SQLite databases
14
- */
15
-
16
- // Get database configuration from environment variables
17
- const DB_PASSWORD = process.env.SQLCIPHER_PASSWORD;
18
- const DB_PATH = process.env.SQLCIPHER_DATABASE_PATH;
19
-
20
- // Create MCP server instance
21
- const server = new Server(
22
- {
23
- name: 'sqlcipher-mcp-server',
24
- version: '1.0.0',
25
- },
26
- {
27
- capabilities: {
28
- tools: {},
29
- },
30
- }
31
- );
32
-
33
- /**
34
- * List available tools
35
- */
36
- server.setRequestHandler(ListToolsRequestSchema, async () => {
37
- return {
38
- tools: [
39
- {
40
- name: 'execute_query',
41
- description: 'Execute a SELECT query on a SQLCipher-encrypted SQLite database. Only read-only queries are allowed. Database path can be provided as parameter or via SQLCIPHER_DATABASE_PATH environment variable.',
42
- inputSchema: {
43
- type: 'object',
44
- properties: {
45
- database_path: {
46
- type: 'string',
47
- description: 'Path to the SQLCipher database file (optional if SQLCIPHER_DATABASE_PATH is set)',
48
- },
49
- query: {
50
- type: 'string',
51
- description: 'SQL SELECT query to execute (read-only)',
52
- },
53
- },
54
- required: ['query'],
55
- },
56
- },
57
- ],
58
- };
59
- });
60
-
61
- /**
62
- * Handle tool execution requests
63
- */
64
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
65
- const { name, arguments: args } = request.params;
66
-
67
- if (name === 'execute_query') {
68
- try {
69
- // Validate required parameters
70
- if (!args || typeof args !== 'object') {
71
- throw new Error('Invalid arguments: arguments must be an object');
72
- }
73
-
74
- const { database_path, query } = args;
75
-
76
- // Validate query
77
- if (!query || typeof query !== 'string') {
78
- throw new Error('query is required and must be a string');
79
- }
80
-
81
- // Determine database path: use parameter if provided, otherwise use environment variable
82
- const dbPath = database_path || DB_PATH;
83
-
84
- // Validate database_path
85
- if (!dbPath || typeof dbPath !== 'string') {
86
- throw new Error(
87
- 'database_path is required. Provide it as a parameter or set SQLCIPHER_DATABASE_PATH environment variable.'
88
- );
89
- }
90
-
91
- // Connect to database (password is optional - will work with unencrypted databases)
92
- let db = null;
93
- try {
94
- db = await connectDatabase(dbPath, DB_PASSWORD);
95
- } catch (error) {
96
- return {
97
- content: [
98
- {
99
- type: 'text',
100
- text: `Failed to connect to database: ${error.message}`,
101
- },
102
- ],
103
- isError: true,
104
- };
105
- }
106
-
107
- // Execute query
108
- try {
109
- const result = await executeQuery(db, query);
110
-
111
- // Format results for response
112
- const responseText = formatQueryResults(result);
113
-
114
- return {
115
- content: [
116
- {
117
- type: 'text',
118
- text: responseText,
119
- },
120
- ],
121
- };
122
- } catch (error) {
123
- return {
124
- content: [
125
- {
126
- type: 'text',
127
- text: `Query execution failed: ${error.message}`,
128
- },
129
- ],
130
- isError: true,
131
- };
132
- } finally {
133
- // Always close the database connection
134
- closeConnection(db);
135
- }
136
- } catch (error) {
137
- return {
138
- content: [
139
- {
140
- type: 'text',
141
- text: `Error: ${error.message}`,
142
- },
143
- ],
144
- isError: true,
145
- };
146
- }
147
- } else {
148
- return {
149
- content: [
150
- {
151
- type: 'text',
152
- text: `Unknown tool: ${name}`,
153
- },
154
- ],
155
- isError: true,
156
- };
157
- }
158
- });
159
-
160
3
  /**
161
- * Format query results as a readable string
162
- *
163
- * @param {Object} result - Query result object with columns, rows, and rowCount
164
- * @returns {string} Formatted result string
4
+ * SQLCipher MCP Server - Entry Point
5
+ * Provides read-only access to SQLCipher-encrypted SQLite databases via MCP
165
6
  */
166
- function formatQueryResults(result) {
167
- const { columns, rows, rowCount } = result;
168
-
169
- if (rowCount === 0) {
170
- return `Query executed successfully. No rows returned.\nColumns: ${columns.join(', ')}`;
171
- }
172
-
173
- // Build table-like output
174
- let output = `Query executed successfully. ${rowCount} row(s) returned.\n\n`;
175
-
176
- // Add column headers
177
- output += `Columns: ${columns.join(' | ')}\n`;
178
- output += '-'.repeat(columns.join(' | ').length) + '\n';
179
-
180
- // Add rows (limit to first 1000 rows for display)
181
- const displayRows = rows.slice(0, 1000);
182
- for (const row of displayRows) {
183
- const values = columns.map(col => {
184
- const value = row[col];
185
- // Handle null/undefined
186
- if (value === null || value === undefined) {
187
- return 'NULL';
188
- }
189
- // Convert to string and truncate long values
190
- const str = String(value);
191
- return str.length > 50 ? str.substring(0, 47) + '...' : str;
192
- });
193
- output += values.join(' | ') + '\n';
194
- }
195
-
196
- if (rows.length > 1000) {
197
- output += `\n... (showing first 1000 of ${rowCount} rows)`;
198
- }
199
-
200
- // Add JSON representation for programmatic access
201
- output += '\n\nJSON representation:\n';
202
- output += JSON.stringify(result, null, 2);
203
-
204
- return output;
205
- }
206
-
207
- /**
208
- * Start the MCP server
209
- */
210
- async function main() {
211
- // Check if configuration is set (warn but don't fail - might be set later)
212
- const warnings = [];
213
-
214
- if (!DB_PASSWORD) {
215
- warnings.push('SQLCIPHER_PASSWORD environment variable is not set. Database connections may fail if password is required.');
216
- }
217
-
218
- if (!DB_PATH) {
219
- warnings.push('SQLCIPHER_DATABASE_PATH environment variable is not set. Database path must be provided in each query.');
220
- }
221
-
222
- if (warnings.length > 0) {
223
- console.error('Warning: ' + warnings.join(' '));
224
- } else {
225
- console.error('Configuration loaded: Database path and password set via environment variables.');
226
- }
227
-
228
- // Create stdio transport
229
- const transport = new StdioServerTransport();
230
-
231
- // Connect server to transport
232
- await server.connect(transport);
233
7
 
234
- console.error('SQLCipher MCP Server running on stdio');
235
- }
8
+ import { startMcpServer } from './src/server/mcp-server.js';
236
9
 
237
10
  // Start the server
238
- main().catch((error) => {
11
+ startMcpServer().catch((error) => {
239
12
  console.error('Fatal error starting server:', error);
240
13
  process.exit(1);
241
14
  });
package/package.json CHANGED
@@ -1,54 +1,56 @@
1
1
  {
2
2
  "name": "sqlcipher-mcp-server",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "MCP Server for querying SQLCipher-encrypted SQLite databases",
5
+ "keywords": [
6
+ "mcp",
7
+ "sqlcipher",
8
+ "sqlite",
9
+ "database",
10
+ "model-context-protocol",
11
+ "encrypted-database",
12
+ "cursor-ide"
13
+ ],
14
+ "homepage": "https://github.com/sathiraguruge/SQLLiteMCP",
15
+ "bugs": {
16
+ "url": "https://github.com/sathiraguruge/SQLLiteMCP/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/sathiraguruge/SQLLiteMCP.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Sathira Guruge",
5
24
  "main": "index.js",
6
- "type": "module",
7
25
  "bin": {
8
26
  "sqlcipher-mcp-server": "./index.js"
9
27
  },
28
+ "type": "module",
10
29
  "files": [
11
30
  "index.js",
31
+ "server-http.js",
32
+ "src/**/*",
12
33
  "lib/**/*",
13
34
  "README.md",
14
35
  "LICENSE"
15
36
  ],
16
37
  "scripts": {
17
38
  "start": "node index.js",
18
- "start:http": "node server-http.js",
19
- "test:http": "node server-http.js",
20
39
  "dev": "nodemon index.js",
40
+ "start:http": "node server-http.js",
21
41
  "dev:http": "nodemon server-http.js",
42
+ "test:http": "node server-http.js",
22
43
  "prepublishOnly": "npm test || exit 0"
23
44
  },
24
- "keywords": [
25
- "mcp",
26
- "sqlcipher",
27
- "sqlite",
28
- "database",
29
- "model-context-protocol",
30
- "encrypted-database",
31
- "cursor-ide"
32
- ],
33
- "author": "Sathira Guruge",
34
- "license": "MIT",
35
- "repository": {
36
- "type": "git",
37
- "url": "https://github.com/sathiraguruge/SQLLiteMCP.git"
38
- },
39
- "bugs": {
40
- "url": "https://github.com/sathiraguruge/SQLLiteMCP/issues"
41
- },
42
- "homepage": "https://github.com/sathiraguruge/SQLLiteMCP",
43
- "engines": {
44
- "node": ">=18.0.0"
45
- },
46
45
  "dependencies": {
47
46
  "@journeyapps/sqlcipher": "^5.1.1",
48
- "@modelcontextprotocol/sdk": "^0.5.0",
47
+ "@modelcontextprotocol/sdk": "^1.25.2",
49
48
  "express": "^4.22.1"
50
49
  },
51
50
  "devDependencies": {
52
51
  "nodemon": "^3.1.11"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
53
55
  }
54
56
  }
package/server-http.js ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SQLCipher MCP HTTP Server - Entry Point
5
+ * HTTP wrapper for testing the SQLCipher MCP Server with tools like Postman
6
+ */
7
+
8
+ import { startHttpServer } from './src/server/http-server.js';
9
+
10
+ // Start the server
11
+ startHttpServer().catch((error) => {
12
+ console.error('Fatal error starting HTTP server:', error);
13
+ process.exit(1);
14
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Application Constants
3
+ * Central location for all application constants
4
+ */
5
+
6
+ export const SERVER_CONFIG = {
7
+ name: 'sqlcipher-mcp-server',
8
+ version: '1.0.2',
9
+ description: 'MCP Server for querying SQLCipher-encrypted SQLite databases',
10
+ };
11
+
12
+ export const QUERY_CONFIG = {
13
+ maxDisplayRows: 1000,
14
+ maxValueLength: 50,
15
+ };
16
+
17
+ export const HTTP_CONFIG = {
18
+ defaultPort: 3000,
19
+ };
20
+
21
+ export const TOOL_DEFINITIONS = {
22
+ execute_query: {
23
+ name: 'execute_query',
24
+ description: 'Execute a SELECT query on a SQLCipher-encrypted SQLite database. Only read-only queries are allowed. Database path can be provided as parameter or via SQLCIPHER_DATABASE_PATH environment variable.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ database_path: {
29
+ type: 'string',
30
+ description: 'Path to the SQLCipher database file (optional if SQLCIPHER_DATABASE_PATH is set)',
31
+ },
32
+ query: {
33
+ type: 'string',
34
+ description: 'SQL SELECT query to execute (read-only)',
35
+ },
36
+ },
37
+ required: ['query'],
38
+ },
39
+ },
40
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Environment Configuration
3
+ * Manages environment variables and application configuration
4
+ */
5
+
6
+ /**
7
+ * Get database password from environment variable
8
+ * @returns {string|undefined} Database password or undefined if not set
9
+ */
10
+ export function getDatabasePassword() {
11
+ return process.env.SQLCIPHER_PASSWORD;
12
+ }
13
+
14
+ /**
15
+ * Get default database path from environment variable
16
+ * @returns {string|undefined} Database path or undefined if not set
17
+ */
18
+ export function getDatabasePath() {
19
+ return process.env.SQLCIPHER_DATABASE_PATH;
20
+ }
21
+
22
+ /**
23
+ * Get HTTP server port from environment variable
24
+ * @param {number} defaultPort - Default port to use if not set
25
+ * @returns {number} Port number
26
+ */
27
+ export function getPort(defaultPort = 3000) {
28
+ return process.env.PORT || defaultPort;
29
+ }
30
+
31
+ /**
32
+ * Check if password is configured
33
+ * @returns {boolean} True if password is set
34
+ */
35
+ export function isPasswordConfigured() {
36
+ return !!getDatabasePassword();
37
+ }
38
+
39
+ /**
40
+ * Check if database path is configured
41
+ * @returns {boolean} True if database path is set
42
+ */
43
+ export function isDatabasePathConfigured() {
44
+ return !!getDatabasePath();
45
+ }
46
+
47
+ /**
48
+ * Get configuration warnings for missing environment variables
49
+ * @returns {string[]} Array of warning messages
50
+ */
51
+ export function getConfigurationWarnings() {
52
+ const warnings = [];
53
+
54
+ if (!isPasswordConfigured()) {
55
+ warnings.push('SQLCIPHER_PASSWORD environment variable is not set. Database connections may fail if password is required.');
56
+ }
57
+
58
+ if (!isDatabasePathConfigured()) {
59
+ warnings.push('SQLCIPHER_DATABASE_PATH environment variable is not set. Database path must be provided in each query.');
60
+ }
61
+
62
+ return warnings;
63
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * HTTP Route Handlers
3
+ * Handlers for HTTP API endpoints
4
+ */
5
+
6
+ import { SERVER_CONFIG } from '../config/constants.js';
7
+ import { getDatabasePassword, isPasswordConfigured } from '../config/environment.js';
8
+ import { validateDatabasePath, validateQuery } from '../utils/validators.js';
9
+ import { executeQueryOnDatabase } from '../services/database-service.js';
10
+
11
+ /**
12
+ * Handle health check endpoint
13
+ * @param {Object} req - Express request object
14
+ * @param {Object} res - Express response object
15
+ */
16
+ export function handleHealthCheck(req, res) {
17
+ res.json({
18
+ status: 'ok',
19
+ message: 'SQLCipher MCP HTTP Server is running',
20
+ passwordConfigured: isPasswordConfigured(),
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Handle server info endpoint
26
+ * @param {Object} req - Express request object
27
+ * @param {Object} res - Express response object
28
+ */
29
+ export function handleInfo(req, res) {
30
+ res.json({
31
+ name: SERVER_CONFIG.name,
32
+ version: SERVER_CONFIG.version,
33
+ description: 'HTTP wrapper for SQLCipher MCP Server',
34
+ endpoints: {
35
+ health: 'GET /health',
36
+ query: 'POST /api/query',
37
+ info: 'GET /api/info',
38
+ },
39
+ passwordConfigured: isPasswordConfigured(),
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Handle query execution endpoint
45
+ * @param {Object} req - Express request object
46
+ * @param {Object} res - Express response object
47
+ */
48
+ export async function handleQuery(req, res) {
49
+ try {
50
+ const { database_path, query } = req.body;
51
+
52
+ // Validate database_path
53
+ try {
54
+ validateDatabasePath(database_path);
55
+ } catch (error) {
56
+ return res.status(400).json({ error: error.message });
57
+ }
58
+
59
+ // Validate query
60
+ try {
61
+ validateQuery(query);
62
+ } catch (error) {
63
+ return res.status(400).json({ error: error.message });
64
+ }
65
+
66
+ // Get database password
67
+ const password = getDatabasePassword();
68
+
69
+ // Execute query
70
+ try {
71
+ const result = await executeQueryOnDatabase(database_path, password, query);
72
+
73
+ // Return successful response
74
+ res.json({
75
+ success: true,
76
+ data: result,
77
+ message: `Query executed successfully. ${result.rowCount} row(s) returned.`,
78
+ });
79
+ } catch (error) {
80
+ res.status(400).json({
81
+ error: `Query execution failed: ${error.message}`,
82
+ });
83
+ }
84
+ } catch (error) {
85
+ res.status(500).json({
86
+ error: `Internal server error: ${error.message}`,
87
+ });
88
+ }
89
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * MCP Tool Handlers
3
+ * Handlers for MCP tool requests
4
+ */
5
+
6
+ import { TOOL_DEFINITIONS } from '../config/constants.js';
7
+ import { getDatabasePassword } from '../config/environment.js';
8
+ import { validateArguments, validateQuery, resolveDatabasePath } from '../utils/validators.js';
9
+ import { formatQueryResults } from '../utils/formatters.js';
10
+ import { createMcpErrorResponse, createMcpSuccessResponse } from '../utils/errors.js';
11
+ import { executeQueryOnDatabase } from '../services/database-service.js';
12
+
13
+ /**
14
+ * Handle list tools request
15
+ * @returns {Object} List of available tools
16
+ */
17
+ export function handleListTools() {
18
+ return {
19
+ tools: [TOOL_DEFINITIONS.execute_query],
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Handle execute_query tool request
25
+ * @param {Object} args - Tool arguments
26
+ * @param {string} [args.database_path] - Database path (optional if env var set)
27
+ * @param {string} args.query - SQL query to execute
28
+ * @returns {Promise<Object>} MCP response object
29
+ */
30
+ export async function handleExecuteQuery(args) {
31
+ try {
32
+ // Validate arguments
33
+ validateArguments(args);
34
+
35
+ const { database_path, query } = args;
36
+
37
+ // Validate query
38
+ validateQuery(query);
39
+
40
+ // Resolve database path
41
+ const dbPath = resolveDatabasePath(database_path);
42
+
43
+ // Get database password
44
+ const password = getDatabasePassword();
45
+
46
+ // Execute query
47
+ try {
48
+ const result = await executeQueryOnDatabase(dbPath, password, query);
49
+
50
+ // Format results for response
51
+ const responseText = formatQueryResults(result);
52
+
53
+ return createMcpSuccessResponse(responseText);
54
+ } catch (error) {
55
+ return createMcpErrorResponse(`Query execution failed: ${error.message}`);
56
+ }
57
+ } catch (error) {
58
+ return createMcpErrorResponse(`Error: ${error.message}`);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Handle unknown tool request
64
+ * @param {string} toolName - Name of the unknown tool
65
+ * @returns {Object} Error response
66
+ */
67
+ export function handleUnknownTool(toolName) {
68
+ return createMcpErrorResponse(`Unknown tool: ${toolName}`);
69
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * HTTP Server
3
+ * Express HTTP server setup and initialization
4
+ */
5
+
6
+ import express from 'express';
7
+ import { HTTP_CONFIG } from '../config/constants.js';
8
+ import { getPort, isPasswordConfigured } from '../config/environment.js';
9
+ import { handleHealthCheck, handleInfo, handleQuery } from '../handlers/http-handlers.js';
10
+
11
+ /**
12
+ * Create and configure Express app
13
+ * @returns {express.Application} Configured Express app
14
+ */
15
+ export function createHttpApp() {
16
+ const app = express();
17
+
18
+ // Middleware to parse JSON request bodies
19
+ app.use(express.json());
20
+
21
+ // Register routes
22
+ app.get('/health', handleHealthCheck);
23
+ app.get('/api/info', handleInfo);
24
+ app.post('/api/query', handleQuery);
25
+
26
+ return app;
27
+ }
28
+
29
+ /**
30
+ * Start the HTTP server
31
+ * @param {number} [port] - Port to listen on (defaults to env or 3000)
32
+ * @returns {Promise<void>}
33
+ */
34
+ export async function startHttpServer(port) {
35
+ const app = createHttpApp();
36
+ const serverPort = port || getPort(HTTP_CONFIG.defaultPort);
37
+
38
+ return new Promise((resolve) => {
39
+ app.listen(serverPort, () => {
40
+ console.log(`SQLCipher MCP HTTP Server running on http://localhost:${serverPort}`);
41
+ console.log(`Health check: http://localhost:${serverPort}/health`);
42
+ console.log(`API info: http://localhost:${serverPort}/api/info`);
43
+
44
+ if (!isPasswordConfigured()) {
45
+ console.warn('\n⚠️ Warning: SQLCIPHER_PASSWORD environment variable is not set.');
46
+ console.warn(' Database queries will fail until this is configured.\n');
47
+ } else {
48
+ console.log('✅ Database password is configured.\n');
49
+ }
50
+
51
+ resolve();
52
+ });
53
+ });
54
+ }
@@ -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
+ }