Skip to main content
Glama
mcp-server.ts70.8 kB
/** * Core MCP Server implementation for SQLite * Implements the Model Context Protocol for SQLite database operations */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, CallToolResult, TextContent } from '@modelcontextprotocol/sdk/types.js'; import { DatabaseManager } from '../database'; import { SecurityManager } from '../security'; import { ConfigManager } from '../config'; import { Logger } from 'winston'; import { PermissionType, QueryResult, SchemaInfo, MCPToolDefinition } from '../types'; import { safeIdentifier } from '../utils'; export class MCPSQLiteServer { private server: Server; private databaseManager: DatabaseManager; private securityManager: SecurityManager; private configManager: ConfigManager; private logger: Logger; private clientPermissions: Map<string, PermissionType[]> = new Map(); constructor( databaseManager: DatabaseManager, securityManager: SecurityManager, configManager: ConfigManager, logger: Logger ) { this.databaseManager = databaseManager; this.securityManager = securityManager; this.configManager = configManager; this.logger = logger; // Initialize MCP server this.server = new Server( { name: 'sqlite-mcp-server', version: '1.3.0', description: 'SQLite database server implementing the Model Context Protocol' }, { capabilities: { tools: {}, resources: {}, prompts: {} } } ); this.setupHandlers(); } /** * Set up MCP protocol handlers */ private setupHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: this.getAvailableTools() }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const clientId = this.extractClientId(request); this.logger.info('Tool call received', { tool: name, clientId, argsKeys: Object.keys(args || {}) }); try { return await this.handleToolCall(name, args || {}, clientId); } catch (error) { this.logger.error('Tool call failed', { tool: name, clientId, error: (error as Error).message }); return { content: [ { type: 'text', text: `Error executing tool ${name}: ${(error as Error).message}` } as TextContent ], isError: true }; } }); } /** * Extract client ID from request (simplified implementation) */ private extractClientId(request: any): string { // In a real implementation, this would extract the client ID from the request context return request.meta?.clientId || 'default'; } /** * Get available tools based on permissions */ private getAvailableTools(): Tool[] { const tools: MCPToolDefinition[] = [ { name: 'sqlite_query', description: 'Execute a raw SQL query on the SQLite database. Use this for SELECT queries, complex joins, aggregations, or any custom SQL. For simple CRUD operations, prefer the dedicated insert/update/delete tools. Returns query results as JSON array for SELECT, or affected row count for other statements.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'SQL query to execute. Use ? placeholders for parameters. Example: "SELECT * FROM users WHERE age > ? AND status = ?"' }, parameters: { type: 'array', description: 'Values for query placeholders in order. Example: [25, "active"] for the query above', items: { type: 'string' } } }, required: ['query'] }, requiredPermissions: ['read', 'execute'] }, { name: 'sqlite_insert', description: 'Insert a single row into a SQLite table. For inserting multiple rows efficiently, use sqlite_bulk_insert instead. Returns the lastInsertRowid and number of affected rows.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Target table name. Example: "users"' }, data: { type: 'object', description: 'Column-value pairs to insert. Example: {"name": "John", "email": "john@example.com", "age": 30}' } }, required: ['table', 'data'] }, requiredPermissions: ['create'] }, { name: 'sqlite_update', description: 'Update existing rows in a SQLite table matching the WHERE conditions. All conditions are combined with AND. For updating multiple rows with different values, use sqlite_bulk_update. Returns the number of affected rows.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Target table name. Example: "users"' }, data: { type: 'object', description: 'Column-value pairs to update. Example: {"status": "inactive", "updated_at": "2024-01-15"}' }, where: { type: 'object', description: 'WHERE conditions as column-value pairs (combined with AND). Example: {"id": 123} or {"status": "active", "role": "admin"}' } }, required: ['table', 'data', 'where'] }, requiredPermissions: ['update'] }, { name: 'sqlite_delete', description: 'Delete rows from a SQLite table matching the WHERE conditions. All conditions are combined with AND. For deleting multiple sets of conditions or with cascade support, use sqlite_bulk_delete. Returns the number of deleted rows.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Target table name. Example: "users"' }, where: { type: 'object', description: 'WHERE conditions as column-value pairs (combined with AND). Example: {"id": 123} or {"status": "deleted", "created_at": "2023-01-01"}' } }, required: ['table', 'where'] }, requiredPermissions: ['delete'] }, { name: 'sqlite_schema', description: 'Retrieve detailed schema information including tables, columns, data types, primary keys, foreign keys, indexes, views, and triggers. Use this to understand database structure before writing queries. Optionally filter by specific table name.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Filter schema info for a specific table name. If omitted, returns schema for all tables.' } } }, requiredPermissions: ['list'] }, { name: 'sqlite_tables', description: 'Get a quick list of all tables in the database with basic info (name, type, column count). Use this for a fast overview; use sqlite_schema for detailed column and constraint information.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['list'] }, { name: 'sqlite_transaction', description: 'Execute multiple SQL queries atomically in a single transaction. If any query fails, all changes are rolled back. Use this when you need to ensure data consistency across multiple operations (e.g., transferring funds between accounts).', inputSchema: { type: 'object', properties: { queries: { type: 'array', description: 'Array of queries to execute atomically. Example: [{"query": "UPDATE accounts SET balance = balance - 100 WHERE id = ?", "parameters": ["1"]}, {"query": "UPDATE accounts SET balance = balance + 100 WHERE id = ?", "parameters": ["2"]}]', items: { type: 'object', properties: { query: { type: 'string', description: 'SQL query with ? placeholders' }, parameters: { type: 'array', items: { type: 'string' }, description: 'Parameter values for placeholders' } }, required: ['query'] } } }, required: ['queries'] }, requiredPermissions: ['transaction'] }, { name: 'sqlite_backup', description: 'Create a complete backup copy of the database to a specified file path. The backup is consistent and can be used to restore the database later. Returns success status and timestamp.', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Full file path for the backup. Example: "/backups/mydb_2024-01-15.sqlite" or "C:\\backups\\mydb_backup.sqlite"' } }, required: ['path'] }, requiredPermissions: ['utility'] }, { name: 'sqlite_bulk_insert', description: 'Insert multiple rows efficiently in batches with progress tracking. Supports relational data insertion with automatic foreign key mapping. Use this instead of multiple sqlite_insert calls for better performance. Returns detailed progress with success/failure counts.', inputSchema: { type: 'object', properties: { mainTable: { type: 'string', description: 'Target table name for the main records. Example: "orders"' }, records: { type: 'array', description: 'Array of row objects to insert. Example: [{"customer_id": 1, "total": 99.99}, {"customer_id": 2, "total": 149.50}]', items: { type: 'object' } }, relatedData: { type: 'object', description: 'Optional related table data with foreign key mappings for hierarchical inserts' }, options: { type: 'object', description: 'Bulk operation options', properties: { batchSize: { type: 'number', description: 'Number of records per batch (default: 1000). Lower values use less memory.' }, continueOnError: { type: 'boolean', description: 'If true, continue processing remaining records when one fails (default: false)' }, validateForeignKeys: { type: 'boolean', description: 'Validate foreign key constraints before insert (default: false)' }, insertRelatedData: { type: 'boolean', description: 'Insert related table data first and map foreign keys (default: false)' } } } }, required: ['mainTable', 'records'] }, requiredPermissions: ['create'] }, { name: 'sqlite_bulk_update', description: 'Update multiple rows with different values efficiently in batches. Each update operation specifies its own data and WHERE conditions. Use this instead of multiple sqlite_update calls for better performance. Returns detailed progress with success/failure counts.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Target table name. Example: "products"' }, updates: { type: 'array', description: 'Array of update operations, each with data to set and WHERE conditions. Example: [{"data": {"price": 29.99}, "where": {"id": 1}}, {"data": {"price": 39.99}, "where": {"id": 2}}]', items: { type: 'object', properties: { data: { type: 'object', description: 'Column-value pairs to update' }, where: { type: 'object', description: 'WHERE conditions to identify the row(s)' } }, required: ['data', 'where'] } }, options: { type: 'object', description: 'Bulk operation options', properties: { batchSize: { type: 'number', description: 'Number of updates per batch (default: 1000)' }, continueOnError: { type: 'boolean', description: 'If true, continue processing when an update fails (default: false)' }, validateForeignKeys: { type: 'boolean', description: 'Validate foreign key constraints (default: false)' } } } }, required: ['table', 'updates'] }, requiredPermissions: ['update'] }, { name: 'sqlite_bulk_delete', description: 'Delete multiple sets of rows efficiently in batches with optional cascade support. Each condition set identifies rows to delete. Use this for mass deletion operations with progress tracking. Returns detailed progress with success/failure counts.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Target table name. Example: "logs"' }, conditions: { type: 'array', description: 'Array of WHERE condition sets. Each set deletes matching rows. Example: [{"user_id": 1}, {"user_id": 2}, {"created_at": "2023-01-01"}]', items: { type: 'object' } }, options: { type: 'object', description: 'Bulk operation options', properties: { batchSize: { type: 'number', description: 'Number of delete operations per batch (default: 1000)' }, continueOnError: { type: 'boolean', description: 'If true, continue processing when a delete fails (default: false)' }, cascadeDelete: { type: 'boolean', description: 'If true, also delete related records in child tables (default: false)' } } } }, required: ['table', 'conditions'] }, requiredPermissions: ['delete'] }, { name: 'sqlite_ddl', description: 'Execute Data Definition Language (DDL) operations to modify database schema. Supports: create_table (with columns, constraints, foreign keys), drop_table, alter_table (add/rename column, rename table), create_index, drop_index. Use sqlite_schema first to understand existing structure.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['create_table', 'drop_table', 'alter_table', 'create_index', 'drop_index'], description: 'DDL operation type: create_table, drop_table, alter_table, create_index, or drop_index' }, table: { type: 'string', description: 'Table name to operate on. Example: "users"' }, columns: { type: 'array', description: 'Column definitions for create_table. Required for create_table operation.', items: { type: 'object', properties: { name: { type: 'string', description: 'Column name. Example: "user_id"' }, type: { type: 'string', description: 'SQLite data type: TEXT, INTEGER, REAL, BLOB, or NUMERIC' }, primaryKey: { type: 'boolean', description: 'Set as PRIMARY KEY (default: false)' }, autoIncrement: { type: 'boolean', description: 'Enable AUTOINCREMENT (only valid for INTEGER PRIMARY KEY)' }, notNull: { type: 'boolean', description: 'Add NOT NULL constraint (default: false)' }, unique: { type: 'boolean', description: 'Add UNIQUE constraint (default: false)' }, defaultValue: { type: 'string', description: 'Default value expression. Example: "0" or "CURRENT_TIMESTAMP"' }, foreignKey: { type: 'object', description: 'Foreign key reference to another table', properties: { table: { type: 'string', description: 'Referenced table name' }, column: { type: 'string', description: 'Referenced column name' }, onDelete: { type: 'string', enum: ['CASCADE', 'SET NULL', 'RESTRICT', 'NO ACTION'], description: 'Action on parent delete' }, onUpdate: { type: 'string', enum: ['CASCADE', 'SET NULL', 'RESTRICT', 'NO ACTION'], description: 'Action on parent update' } } } }, required: ['name', 'type'] } }, alterAction: { type: 'object', description: 'Alter table action configuration. Required for alter_table operation.', properties: { action: { type: 'string', enum: ['add_column', 'rename_table', 'rename_column'], description: 'Type of alteration' }, column: { type: 'object', description: 'Column definition for add_column (same format as columns array items)' }, newName: { type: 'string', description: 'New name for rename_table or rename_column' }, oldColumnName: { type: 'string', description: 'Current column name for rename_column' } } }, index: { type: 'object', description: 'Index configuration for create_index or drop_index operations.', properties: { name: { type: 'string', description: 'Index name. Example: "idx_users_email"' }, columns: { type: 'array', items: { type: 'string' }, description: 'Columns to index. Example: ["email"] or ["last_name", "first_name"]' }, unique: { type: 'boolean', description: 'Create as UNIQUE index to enforce uniqueness (default: false)' } } }, ifNotExists: { type: 'boolean', description: 'Add IF NOT EXISTS clause to prevent errors if object already exists (default: false)', default: false }, ifExists: { type: 'boolean', description: 'Add IF EXISTS clause for drop operations to prevent errors if object does not exist (default: false)', default: false } }, required: ['operation', 'table'] }, requiredPermissions: ['ddl'] }, { name: 'sqlite_relations', description: 'Analyze table relationships and foreign key constraints. Shows incoming and outgoing foreign key relationships with cascade rules. Supports deep traversal to find all connected tables. Use this to understand table dependencies and relational structure.', inputSchema: { type: 'object', properties: { table: { type: 'string', description: 'Table name to analyze relationships for. Example: "users"' }, depth: { type: 'number', description: 'How deep to traverse relationships (1-5). Default: 1 for immediate relations, higher values show cascading relationships' }, analysisType: { type: 'string', enum: ['incoming', 'outgoing', 'both'], description: 'Type of relationships to analyze: "incoming" (tables that reference this table), "outgoing" (tables this table references), or "both" (default: both)' } }, required: ['table'] }, requiredPermissions: ['list'] }, { name: 'sqlite_views', description: 'Create, drop, list, and retrieve information about database views. Views are virtual tables based on SELECT queries. Use create_view to define a new view, drop_view to remove it, list_views to see all views, and get_view_info to inspect a specific view.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['create_view', 'drop_view', 'list_views', 'get_view_info'], description: 'View operation: create_view, drop_view, list_views, or get_view_info' }, viewName: { type: 'string', description: 'Name of the view (required for all operations except list_views). Example: "active_users"' }, selectQuery: { type: 'string', description: 'SQL SELECT query for create_view operation. Example: "SELECT * FROM users WHERE status = \'active\'"' }, ifNotExists: { type: 'boolean', description: 'Add IF NOT EXISTS clause for create_view to prevent errors if view already exists (default: false)' }, ifExists: { type: 'boolean', description: 'Add IF EXISTS clause for drop_view to prevent errors if view does not exist (default: false)' } }, required: ['operation'] }, requiredPermissions: ['ddl'] }, { name: 'sqlite_indexes', description: 'Manage indexes for performance optimization. List all indexes, analyze index statistics for query optimization, and get detailed index information including columns and uniqueness constraints.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['list_indexes', 'get_index_info', 'analyze_index'], description: 'Index operation: list_indexes (no parameters), get_index_info (requires indexName), or analyze_index (requires indexName)' }, indexName: { type: 'string', description: 'Name of the index for get_index_info and analyze_index operations. Example: "idx_users_email"' } }, required: ['operation'] }, requiredPermissions: ['list', 'ddl'] }, { name: 'sqlite_constraints', description: 'View and manage database constraints including primary keys, foreign keys, unique constraints, and check constraints. Use to list all constraints, view foreign keys with cascade rules, and understand data integrity requirements.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['list_constraints', 'list_foreign_keys'], description: 'Constraint operation: list_constraints or list_foreign_keys' }, tableName: { type: 'string', description: 'Optional table name to filter constraints/foreign keys. If omitted, shows all constraints across all tables. Example: "users"' } }, required: ['operation'] }, requiredPermissions: ['list'] }, { name: 'sqlite_migrate', description: 'Migrate and synchronize data between tables. Supports cloning tables (structure and data), comparing table structures, and copying data between tables with optional filtering.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['clone_table', 'compare_structure', 'copy_data'], description: 'Migration operation: clone_table, compare_structure, or copy_data' }, sourceTable: { type: 'string', description: 'Source table name for clone_table and copy_data operations. Example: "users_old"' }, targetTable: { type: 'string', description: 'Target/destination table name. Required for clone_table and copy_data operations. Example: "users_new"' }, includeData: { type: 'boolean', description: 'For clone_table: include data from source table (default: true). If false, clones only the structure.' }, whereClause: { type: 'string', description: 'Optional WHERE condition for copy_data to copy only matching rows. Example: "status = \'active\' AND created_at > \'2024-01-01\'"' } }, required: ['operation'] }, requiredPermissions: ['read', 'create', 'update'] }, { name: 'sqlite_backup_restore', description: 'Create backups and restore databases. Backup specific tables to SQL files with data and schema, retrieve CREATE TABLE statements, and restore databases from SQL files.', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['backup_table', 'restore_from_sql', 'get_create_statement'], description: 'Backup/restore operation: backup_table, restore_from_sql, or get_create_statement' }, tableName: { type: 'string', description: 'Table name for backup_table and get_create_statement operations. Example: "users"' }, backupPath: { type: 'string', description: 'File path for backup_table operation. Path where to save the SQL backup. Example: "/backups/users_backup.sql" or "C:\\backups\\users_backup.sql"' }, sqlPath: { type: 'string', description: 'Path to SQL file for restore_from_sql operation. Example: "/backups/restore.sql"' } }, required: ['operation'] }, requiredPermissions: ['utility', 'read', 'ddl'] }, { name: 'sqlite_column_statistics', description: 'Get detailed statistics about columns in a table including row counts, distinct values, null counts, min/max values, and averages. Useful for data profiling and quality analysis.', inputSchema: { type: 'object', properties: { tableName: { type: 'string', description: 'Name of the table to analyze. Example: "users"' } }, required: ['tableName'] }, requiredPermissions: ['read'] }, { name: 'sqlite_database_summary', description: 'Get a comprehensive summary of the database including file size, table/view counts, total rows, index count, and configuration settings like WAL mode.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['read'] }, { name: 'sqlite_schema_erd', description: 'Generate Entity Relationship Diagram (ERD) data showing all tables, columns, and foreign key relationships. Output can be used with visualization tools.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['read'] }, { name: 'sqlite_schema_rag_context', description: 'Generate schema context formatted for RAG (Retrieval-Augmented Generation) and AI models. Includes detailed table structures, columns, types, and relationships in markdown format.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['read'] }, { name: 'sqlite_analyze_query', description: 'Analyze a SQL query to understand its execution plan, complexity, and accessed tables. Returns the EXPLAIN QUERY PLAN output and query metrics.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'SQL query to analyze. Example: "SELECT * FROM orders WHERE user_id = 1"' } }, required: ['query'] }, requiredPermissions: ['read'] }, { name: 'sqlite_optimization_hints', description: 'Get optimization suggestions for a SQL query. Identifies common performance issues like SELECT *, leading wildcards, missing indexes, and suggests improvements.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'SQL query to get optimization hints for. Example: "SELECT * FROM users WHERE name LIKE \'%john%\'"' } }, required: ['query'] }, requiredPermissions: ['read'] }, { name: 'sqlite_database_health_check', description: 'Perform a comprehensive health check on the database. Checks integrity, foreign key consistency, and schema validity. Returns status and detailed check results.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['read'] }, { name: 'sqlite_unused_indexes', description: 'Identify potentially unused or redundant indexes in the database. Helps with performance optimization by finding indexes that can be safely removed.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['read'] }, { name: 'sqlite_connection_pool_stats', description: 'Get statistics about the connection pool including active connections, pool size, and connection availability.', inputSchema: { type: 'object', properties: {} }, requiredPermissions: ['read'] } ]; // Convert to MCP Tool format return tools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })); } /** * Handle tool calls */ private async handleToolCall( toolName: string, args: Record<string, any>, clientId: string ): Promise<CallToolResult> { // Check permissions - use 'default' client permissions if specific client not found let clientPermissions = this.clientPermissions.get(clientId); if (!clientPermissions) { // Fall back to default client permissions clientPermissions = this.clientPermissions.get('default') || []; this.logger.debug('Using default permissions for unknown client', { clientId }); } if (clientPermissions.length === 0) { throw new Error(`No permissions configured for client: ${clientId}`); } switch (toolName) { case 'sqlite_query': return this.handleQuery(args, clientId, clientPermissions); case 'sqlite_insert': return this.handleInsert(args, clientId, clientPermissions); case 'sqlite_update': return this.handleUpdate(args, clientId, clientPermissions); case 'sqlite_delete': return this.handleDelete(args, clientId, clientPermissions); case 'sqlite_schema': return this.handleSchema(args, clientId, clientPermissions); case 'sqlite_tables': return this.handleTables(args, clientId, clientPermissions); case 'sqlite_transaction': return this.handleTransaction(args, clientId, clientPermissions); case 'sqlite_backup': return this.handleBackup(args, clientId, clientPermissions); case 'sqlite_bulk_insert': return this.handleBulkInsert(args, clientId, clientPermissions); case 'sqlite_bulk_update': return this.handleBulkUpdate(args, clientId, clientPermissions); case 'sqlite_bulk_delete': return this.handleBulkDelete(args, clientId, clientPermissions); case 'sqlite_ddl': return this.handleDDL(args, clientId, clientPermissions); case 'sqlite_relations': return this.handleRelations(args, clientId, clientPermissions); case 'sqlite_views': return this.handleViews(args, clientId, clientPermissions); case 'sqlite_indexes': return this.handleIndexes(args, clientId, clientPermissions); case 'sqlite_constraints': return this.handleConstraints(args, clientId, clientPermissions); case 'sqlite_migrate': return this.handleMigrate(args, clientId, clientPermissions); case 'sqlite_backup_restore': return this.handleBackupRestore(args, clientId, clientPermissions); case 'sqlite_column_statistics': return this.handleColumnStatistics(args, clientId, clientPermissions); case 'sqlite_database_summary': return this.handleDatabaseSummary(args, clientId, clientPermissions); case 'sqlite_schema_erd': return this.handleSchemaERD(args, clientId, clientPermissions); case 'sqlite_schema_rag_context': return this.handleSchemaRAGContext(args, clientId, clientPermissions); case 'sqlite_analyze_query': return this.handleAnalyzeQuery(args, clientId, clientPermissions); case 'sqlite_optimization_hints': return this.handleOptimizationHints(args, clientId, clientPermissions); case 'sqlite_database_health_check': return this.handleDatabaseHealthCheck(args, clientId, clientPermissions); case 'sqlite_unused_indexes': return this.handleUnusedIndexes(args, clientId, clientPermissions); case 'sqlite_connection_pool_stats': return this.handleConnectionPoolStats(args, clientId, clientPermissions); default: throw new Error(`Unknown tool: ${toolName}`); } } /** * Handle SQL query execution */ private async handleQuery( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { query, parameters = [] } = args; // Check permissions if (!permissions.includes('read') && !permissions.includes('execute')) { throw new Error('Insufficient permissions for query execution'); } // Validate and sanitize query const validation = await this.securityManager.validateQuery(query, parameters, permissions, clientId); if (!validation.isValid) { throw new Error(`Query validation failed: ${validation.reason}`); } // Execute query const result = this.databaseManager.executeQuery(query, parameters, clientId); if (!result.success) { throw new Error(result.error || 'Query execution failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: result.data, rowsAffected: result.rowsAffected, executionTime: result.executionTime }, null, 2) } as TextContent ] }; } /** * Handle data insertion */ private async handleInsert( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { table, data } = args; if (!permissions.includes('create')) { throw new Error('Insufficient permissions for insert operation'); } // Validate and escape table name const safeTable = safeIdentifier(table, 'table name'); // Validate and escape column names const columns = Object.keys(data); const safeColumns = columns.map(col => safeIdentifier(col, 'column name')); // Build INSERT query with safe identifiers const placeholders = columns.map(() => '?').join(', '); const query = `INSERT INTO ${safeTable} (${safeColumns.join(', ')}) VALUES (${placeholders})`; const parameters = Object.values(data); // Validate query const validation = await this.securityManager.validateQuery(query, parameters, permissions, clientId); if (!validation.isValid) { throw new Error(`Insert validation failed: ${validation.reason}`); } // Execute insert const result = this.databaseManager.executeQuery(query, parameters, clientId); if (!result.success) { throw new Error(result.error || 'Insert operation failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, lastInsertRowid: result.lastInsertRowid, rowsAffected: result.rowsAffected, executionTime: result.executionTime }, null, 2) } as TextContent ] }; } /** * Handle data update */ private async handleUpdate( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { table, data, where } = args; if (!permissions.includes('update')) { throw new Error('Insufficient permissions for update operation'); } // Validate and escape table name const safeTable = safeIdentifier(table, 'table name'); // Validate and escape column names in SET clause const setClause = Object.keys(data).map(key => `${safeIdentifier(key, 'column name')} = ?`).join(', '); // Validate and escape column names in WHERE clause const whereClause = Object.keys(where).map(key => `${safeIdentifier(key, 'column name')} = ?`).join(' AND '); // Build UPDATE query with safe identifiers const query = `UPDATE ${safeTable} SET ${setClause} WHERE ${whereClause}`; const parameters = [...Object.values(data), ...Object.values(where)]; // Validate query const validation = await this.securityManager.validateQuery(query, parameters, permissions, clientId); if (!validation.isValid) { throw new Error(`Update validation failed: ${validation.reason}`); } // Execute update const result = this.databaseManager.executeQuery(query, parameters, clientId); if (!result.success) { throw new Error(result.error || 'Update operation failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, rowsAffected: result.rowsAffected, executionTime: result.executionTime }, null, 2) } as TextContent ] }; } /** * Handle data deletion */ private async handleDelete( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { table, where } = args; if (!permissions.includes('delete')) { throw new Error('Insufficient permissions for delete operation'); } // Validate and escape table name const safeTable = safeIdentifier(table, 'table name'); // Validate and escape column names in WHERE clause const whereClause = Object.keys(where).map(key => `${safeIdentifier(key, 'column name')} = ?`).join(' AND '); // Build DELETE query with safe identifiers const query = `DELETE FROM ${safeTable} WHERE ${whereClause}`; const parameters = Object.values(where); // Validate query const validation = await this.securityManager.validateQuery(query, parameters, permissions, clientId); if (!validation.isValid) { throw new Error(`Delete validation failed: ${validation.reason}`); } // Execute delete const result = this.databaseManager.executeQuery(query, parameters, clientId); if (!result.success) { throw new Error(result.error || 'Delete operation failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, rowsAffected: result.rowsAffected, executionTime: result.executionTime }, null, 2) } as TextContent ] }; } /** * Handle schema information request */ private async handleSchema( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('list')) { throw new Error('Insufficient permissions for schema access'); } const { table } = args; const schema = this.databaseManager.getSchemaInfo(); let result: any = schema; if (table) { // Filter for specific table result = { tables: schema.tables.filter(t => t.name === table), views: schema.views?.filter((v: any) => v.name === table) || [], indexes: schema.indexes?.filter((i: any) => i.tableName === table) || [], triggers: schema.triggers?.filter((t: any) => t.tableName === table) || [] }; } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } /** * Handle tables list request */ private async handleTables( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('list')) { throw new Error('Insufficient permissions for table listing'); } const schema = this.databaseManager.getSchemaInfo(); const tables = schema.tables.map(table => ({ name: table.name, type: table.type, columnCount: table.columns.length })); return { content: [ { type: 'text', text: JSON.stringify({ tables }, null, 2) } as TextContent ] }; } /** * Handle transaction execution */ private async handleTransaction( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { queries } = args; if (!permissions.includes('transaction')) { throw new Error('Insufficient permissions for transaction execution'); } // Validate all queries in the transaction for (const queryObj of queries) { const validation = await this.securityManager.validateQuery( queryObj.query, queryObj.parameters || [], permissions, clientId ); if (!validation.isValid) { throw new Error(`Transaction validation failed: ${validation.reason}`); } } // Execute transaction const result = this.databaseManager.executeTransaction(queries, clientId); if (!result.success) { throw new Error(result.error || 'Transaction execution failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, results: result.data, executionTime: result.executionTime }, null, 2) } as TextContent ] }; } /** * Handle database backup */ private async handleBackup( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { path } = args; if (!permissions.includes('utility')) { throw new Error('Insufficient permissions for backup operation'); } try { await this.databaseManager.backupDatabase(path); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Database backed up to ${path}`, timestamp: new Date().toISOString() }, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Backup failed: ${(error as Error).message}`); } } /** * Handle bulk insert operation */ private async handleBulkInsert( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { mainTable, records, relatedData = {}, options = {} } = args; // Check permissions if (!permissions.includes('create')) { throw new Error('Insufficient permissions for bulk insert operation'); } try { const result = await this.databaseManager.bulkInsert({ mainTable, records, relatedData, options }); return { content: [ { type: 'text', text: JSON.stringify({ success: result.success, executionTime: result.executionTime, summary: { totalRecords: result.summary.totalRecords, successfulRecords: result.summary.successfulRecords, failedRecords: result.summary.failedRecords, errors: result.progress.errors, affectedTables: result.summary.affectedTables }, progress: result.progress }, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Bulk insert failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Handle bulk update operation */ private async handleBulkUpdate( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { table, updates, options = {} } = args; // Check permissions if (!permissions.includes('update')) { throw new Error('Insufficient permissions for bulk update operation'); } try { const result = await this.databaseManager.bulkUpdate({ table, updates, options }); return { content: [ { type: 'text', text: JSON.stringify({ success: result.success, executionTime: result.executionTime, summary: { totalRecords: result.summary.totalRecords, successfulRecords: result.summary.successfulRecords, failedRecords: result.summary.failedRecords, errors: result.progress.errors }, progress: result.progress }, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Bulk update failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Handle bulk delete operation */ private async handleBulkDelete( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { table, conditions, options = {} } = args; // Check permissions if (!permissions.includes('delete')) { throw new Error('Insufficient permissions for bulk delete operation'); } try { const result = await this.databaseManager.bulkDelete({ table: table, conditions, options }); return { content: [ { type: 'text', text: JSON.stringify({ success: result.success, executionTime: result.executionTime, summary: { totalRecords: result.summary.totalRecords, successfulRecords: result.summary.successfulRecords, failedRecords: result.summary.failedRecords, errors: result.progress.errors }, progress: result.progress }, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Bulk delete failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Handle DDL (Data Definition Language) operations */ private async handleDDL( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { operation, table, columns, alterAction, index, ifNotExists, ifExists } = args; if (!permissions.includes('ddl')) { throw new Error('Insufficient permissions for DDL operation'); } // Validate table name const safeTable = safeIdentifier(table, 'table name'); let query = ''; switch (operation) { case 'create_table': if (!columns || columns.length === 0) { throw new Error('Columns are required for create_table operation'); } query = this.buildCreateTableQuery(safeTable, columns, ifNotExists); break; case 'drop_table': query = `DROP TABLE ${ifExists ? 'IF EXISTS ' : ''}${safeTable}`; break; case 'alter_table': if (!alterAction) { throw new Error('alterAction is required for alter_table operation'); } query = this.buildAlterTableQuery(safeTable, alterAction); break; case 'create_index': if (!index || !index.name || !index.columns) { throw new Error('Index name and columns are required for create_index operation'); } const safeIndexName = safeIdentifier(index.name, 'index name'); const safeIndexColumns = index.columns.map((col: string) => safeIdentifier(col, 'column name')); query = `CREATE ${index.unique ? 'UNIQUE ' : ''}INDEX ${ifNotExists ? 'IF NOT EXISTS ' : ''}${safeIndexName} ON ${safeTable} (${safeIndexColumns.join(', ')})`; break; case 'drop_index': if (!index || !index.name) { throw new Error('Index name is required for drop_index operation'); } const safeDropIndexName = safeIdentifier(index.name, 'index name'); query = `DROP INDEX ${ifExists ? 'IF EXISTS ' : ''}${safeDropIndexName}`; break; default: throw new Error(`Unknown DDL operation: ${operation}`); } // Validate query const validation = await this.securityManager.validateQuery(query, [], permissions, clientId); if (!validation.isValid) { throw new Error(`DDL validation failed: ${validation.reason}`); } // Execute DDL const result = this.databaseManager.executeQuery(query, [], clientId); if (!result.success) { throw new Error(result.error || 'DDL operation failed'); } return { content: [ { type: 'text', text: JSON.stringify({ success: true, operation, table, query, executionTime: result.executionTime }, null, 2) } as TextContent ] }; } /** * Build CREATE TABLE query from column definitions * Note: table parameter should already be sanitized by caller */ private buildCreateTableQuery( table: string, columns: any[], ifNotExists: boolean = false ): string { const columnDefs: string[] = []; const foreignKeys: string[] = []; for (const col of columns) { // Validate column name const safeColName = safeIdentifier(col.name, 'column name'); let colDef = `${safeColName} ${col.type}`; if (col.primaryKey) { colDef += ' PRIMARY KEY'; if (col.autoIncrement && col.type.toUpperCase() === 'INTEGER') { colDef += ' AUTOINCREMENT'; } } if (col.notNull) { colDef += ' NOT NULL'; } if (col.unique && !col.primaryKey) { colDef += ' UNIQUE'; } if (col.defaultValue !== undefined) { colDef += ` DEFAULT ${col.defaultValue}`; } columnDefs.push(colDef); if (col.foreignKey) { const fk = col.foreignKey; const safeFkTable = safeIdentifier(fk.table, 'foreign key table name'); const safeFkColumn = safeIdentifier(fk.column, 'foreign key column name'); let fkDef = `FOREIGN KEY (${safeColName}) REFERENCES ${safeFkTable}(${safeFkColumn})`; if (fk.onDelete) { fkDef += ` ON DELETE ${fk.onDelete}`; } if (fk.onUpdate) { fkDef += ` ON UPDATE ${fk.onUpdate}`; } foreignKeys.push(fkDef); } } const allDefs = [...columnDefs, ...foreignKeys]; return `CREATE TABLE ${ifNotExists ? 'IF NOT EXISTS ' : ''}${table} (${allDefs.join(', ')})`; } /** * Build ALTER TABLE query from action * Note: table parameter should already be sanitized by caller */ private buildAlterTableQuery(table: string, alterAction: any): string { switch (alterAction.action) { case 'add_column': if (!alterAction.column) { throw new Error('Column definition is required for add_column action'); } const col = alterAction.column; const safeColName = safeIdentifier(col.name, 'column name'); let colDef = `${safeColName} ${col.type}`; if (col.notNull) colDef += ' NOT NULL'; if (col.defaultValue !== undefined) colDef += ` DEFAULT ${col.defaultValue}`; return `ALTER TABLE ${table} ADD COLUMN ${colDef}`; case 'rename_table': if (!alterAction.newName) { throw new Error('newName is required for rename_table action'); } const safeNewTableName = safeIdentifier(alterAction.newName, 'new table name'); return `ALTER TABLE ${table} RENAME TO ${safeNewTableName}`; case 'rename_column': if (!alterAction.oldColumnName || !alterAction.newName) { throw new Error('oldColumnName and newName are required for rename_column action'); } const safeOldColName = safeIdentifier(alterAction.oldColumnName, 'old column name'); const safeNewColName = safeIdentifier(alterAction.newName, 'new column name'); return `ALTER TABLE ${table} RENAME COLUMN ${safeOldColName} TO ${safeNewColName}`; default: throw new Error(`Unknown alter action: ${alterAction.action}`); } } /** * Handle table relationship analysis */ private async handleRelations( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { table, depth = 1, analysisType = 'both' } = args; if (!permissions.includes('list')) { throw new Error('Insufficient permissions for relationship analysis'); } if (!table) { throw new Error('Table name is required for relationship analysis'); } // Validate depth const validatedDepth = Math.min(Math.max(1, depth), 5); // Validate analysisType if (!['incoming', 'outgoing', 'both'].includes(analysisType)) { throw new Error('analysisType must be one of: incoming, outgoing, both'); } try { const result = this.databaseManager.analyzeTableRelations(table, validatedDepth, analysisType); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Relationship analysis failed: ${(error as Error).message}`); } } /** * Handle view operations */ private async handleViews( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { operation, viewName, selectQuery, ifNotExists, ifExists } = args; if (!permissions.includes('ddl')) { throw new Error('Insufficient permissions for view operations'); } try { let result: any; switch (operation) { case 'create_view': if (!viewName || !selectQuery) { throw new Error('viewName and selectQuery are required for create_view'); } result = this.databaseManager.createView(viewName, selectQuery, ifNotExists); break; case 'drop_view': if (!viewName) { throw new Error('viewName is required for drop_view'); } result = this.databaseManager.dropView(viewName, ifExists); break; case 'list_views': result = { success: true, data: this.databaseManager.listViews(), executionTime: 0 }; break; case 'get_view_info': if (!viewName) { throw new Error('viewName is required for get_view_info'); } result = { success: true, data: this.databaseManager.getViewInfo(viewName), executionTime: 0 }; break; default: throw new Error(`Unknown view operation: ${operation}`); } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`View operation failed: ${(error as Error).message}`); } } /** * Handle index operations */ private async handleIndexes( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { operation, indexName } = args; if (!permissions.includes('list') && !permissions.includes('ddl')) { throw new Error('Insufficient permissions for index operations'); } try { let result: any; switch (operation) { case 'list_indexes': result = { success: true, data: this.databaseManager.listIndexes(), executionTime: 0 }; break; case 'get_index_info': if (!indexName) { throw new Error('indexName is required for get_index_info'); } result = { success: true, data: this.databaseManager.getIndexInfo(indexName), executionTime: 0 }; break; case 'analyze_index': if (!indexName) { throw new Error('indexName is required for analyze_index'); } result = this.databaseManager.analyzeIndex(indexName); break; default: throw new Error(`Unknown index operation: ${operation}`); } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Index operation failed: ${(error as Error).message}`); } } /** * Handle constraint operations */ private async handleConstraints( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { operation, tableName } = args; if (!permissions.includes('list')) { throw new Error('Insufficient permissions for constraint operations'); } try { let result: any; switch (operation) { case 'list_constraints': result = { success: true, data: this.databaseManager.listConstraints(tableName), executionTime: 0 }; break; case 'list_foreign_keys': result = { success: true, data: this.databaseManager.listForeignKeys(tableName), executionTime: 0 }; break; default: throw new Error(`Unknown constraint operation: ${operation}`); } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Constraint operation failed: ${(error as Error).message}`); } } /** * Handle data migration operations */ private async handleMigrate( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { operation, sourceTable, targetTable, includeData, whereClause } = args; if (!permissions.includes('read') || !permissions.includes('create')) { throw new Error('Insufficient permissions for migration operations'); } try { let result: any; switch (operation) { case 'clone_table': if (!sourceTable || !targetTable) { throw new Error('sourceTable and targetTable are required for clone_table'); } result = this.databaseManager.cloneTable(sourceTable, targetTable, includeData !== false); break; case 'compare_structure': if (!sourceTable || !targetTable) { throw new Error('sourceTable and targetTable are required for compare_structure'); } result = { success: true, data: this.databaseManager.compareTableStructure(sourceTable, targetTable), executionTime: 0 }; break; case 'copy_data': if (!sourceTable || !targetTable) { throw new Error('sourceTable and targetTable are required for copy_data'); } result = this.databaseManager.copyTableData(sourceTable, targetTable, whereClause); break; default: throw new Error(`Unknown migration operation: ${operation}`); } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Migration operation failed: ${(error as Error).message}`); } } /** * Handle backup and restore operations */ private async handleBackupRestore( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { operation, tableName, backupPath, sqlPath } = args; if (!permissions.includes('utility') && !permissions.includes('read')) { throw new Error('Insufficient permissions for backup/restore operations'); } try { let result: any; switch (operation) { case 'backup_table': if (!tableName || !backupPath) { throw new Error('tableName and backupPath are required for backup_table'); } result = this.databaseManager.backupTable(tableName, backupPath); break; case 'restore_from_sql': if (!sqlPath) { throw new Error('sqlPath is required for restore_from_sql'); } result = await this.databaseManager.restoreFromSQL(sqlPath); break; case 'get_create_statement': if (!tableName) { throw new Error('tableName is required for get_create_statement'); } result = { success: true, data: this.databaseManager.getCreateTableStatement(tableName), executionTime: 0 }; break; default: throw new Error(`Unknown backup/restore operation: ${operation}`); } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Backup/restore operation failed: ${(error as Error).message}`); } } /** * Handle column statistics request */ private async handleColumnStatistics( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { tableName } = args; if (!permissions.includes('read')) { throw new Error('Insufficient permissions for column statistics'); } if (!tableName) { throw new Error('tableName is required'); } try { const result = this.databaseManager.getColumnStatistics(tableName); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Column statistics request failed: ${(error as Error).message}`); } } /** * Handle database summary request */ private async handleDatabaseSummary( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('read')) { throw new Error('Insufficient permissions for database summary'); } try { const result = this.databaseManager.getDatabaseSummary(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Database summary request failed: ${(error as Error).message}`); } } /** * Handle schema ERD request */ private async handleSchemaERD( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('read')) { throw new Error('Insufficient permissions for schema ERD'); } try { const result = this.databaseManager.getSchemaERD(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Schema ERD request failed: ${(error as Error).message}`); } } /** * Handle schema RAG context request */ private async handleSchemaRAGContext( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('read')) { throw new Error('Insufficient permissions for schema RAG context'); } try { const result = this.databaseManager.getSchemaRAGContext(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Schema RAG context request failed: ${(error as Error).message}`); } } /** * Handle query analysis request */ private async handleAnalyzeQuery( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { query } = args; if (!permissions.includes('read')) { throw new Error('Insufficient permissions for query analysis'); } if (!query) { throw new Error('query is required'); } try { const result = this.databaseManager.analyzeQuery(query); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Query analysis request failed: ${(error as Error).message}`); } } /** * Handle optimization hints request */ private async handleOptimizationHints( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { const { query } = args; if (!permissions.includes('read')) { throw new Error('Insufficient permissions for optimization hints'); } if (!query) { throw new Error('query is required'); } try { const result = this.databaseManager.getOptimizationHints(query); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Optimization hints request failed: ${(error as Error).message}`); } } /** * Handle database health check request */ private async handleDatabaseHealthCheck( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('read')) { throw new Error('Insufficient permissions for health check'); } try { const result = this.databaseManager.getDatabaseHealthCheck(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Health check request failed: ${(error as Error).message}`); } } /** * Handle unused indexes request */ private async handleUnusedIndexes( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('read')) { throw new Error('Insufficient permissions for unused indexes'); } try { const result = this.databaseManager.getUnusedIndexes(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Unused indexes request failed: ${(error as Error).message}`); } } /** * Handle connection pool stats request */ private async handleConnectionPoolStats( args: Record<string, any>, clientId: string, permissions: PermissionType[] ): Promise<CallToolResult> { if (!permissions.includes('read')) { throw new Error('Insufficient permissions for connection pool stats'); } try { const result = this.databaseManager.getConnectionPoolStats(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } as TextContent ] }; } catch (error) { throw new Error(`Connection pool stats request failed: ${(error as Error).message}`); } } /** * Set client permissions */ public setClientPermissions(clientId: string, permissions: PermissionType[]): void { this.clientPermissions.set(clientId, permissions); this.logger.info('Client permissions updated', { clientId, permissions }); } /** * Start the MCP server */ public async start(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); this.logger.info('MCP SQLite server started'); } /** * Stop the MCP server */ public async stop(): Promise<void> { await this.server.close(); this.databaseManager.close(); this.logger.info('MCP SQLite server stopped'); } } export default MCPSQLiteServer;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/berthojoris/sqlite-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server