@bytebase/dbhub 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  <p align="center">
2
2
  <a href="https://dbhub.ai/" target="_blank">
3
3
  <picture>
4
- <img src="https://raw.githubusercontent.com/bytebase/dbhub/main/assets/logo-full.svg" width="50%">
4
+ <img src="https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/logo-full.svg" width="50%">
5
5
  </picture>
6
6
  </a>
7
7
  </p>
@@ -14,14 +14,16 @@ DBHub is a universal database gateway implementing the Model Context Protocol (M
14
14
  | | | | | |
15
15
  | Claude Desktop +--->+ +--->+ PostgreSQL |
16
16
  | | | | | |
17
- | Cursor +--->+ DBHub +--->+ MySQL |
17
+ | Cursor +--->+ DBHub +--->+ SQL Server |
18
18
  | | | | | |
19
19
  | Other MCP +--->+ +--->+ SQLite |
20
20
  | Clients | | | | |
21
- | | | +--->+ DuckDB |
21
+ | | | +--->+ MySQL |
22
22
  | | | | | |
23
+ | | | +--->+ DuckDB |
24
+ | | | | | (soon) |
23
25
  | | | +--->+ Other Databases |
24
- | | | | | |
26
+ | | | | | (coming) |
25
27
  +------------------+ +--------------+ +------------------+
26
28
  MCP Clients MCP Server Databases
27
29
  ```
@@ -38,6 +40,7 @@ DBHub is a universal database gateway implementing the Model Context Protocol (M
38
40
  ### Docker
39
41
 
40
42
  ```bash
43
+ # PostgreSQL example
41
44
  docker run --rm --init \
42
45
  --name dbhub \
43
46
  --publish 8080:8080 \
@@ -45,12 +48,37 @@ docker run --rm --init \
45
48
  --transport sse \
46
49
  --port 8080 \
47
50
  --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
51
+
52
+ # SQLite in-memory example
53
+ docker run --rm --init \
54
+ --name dbhub \
55
+ --publish 8080:8080 \
56
+ bytebase/dbhub \
57
+ --transport sse \
58
+ --port 8080 \
59
+ --dsn "sqlite::memory:"
60
+
61
+ # MySQL example
62
+ docker run --rm --init \
63
+ --name dbhub \
64
+ --publish 8080:8080 \
65
+ bytebase/dbhub \
66
+ --transport sse \
67
+ --port 8080 \
68
+ --dsn "mysql://user:password@localhost:3306/dbname"
48
69
  ```
49
70
 
50
71
  ### NPM
51
72
 
52
73
  ```bash
74
+ # PostgreSQL example
53
75
  npx @bytebase/dbhub --transport sse --port 8080 --dsn "postgres://user:password@localhost:5432/dbname"
76
+
77
+ # SQLite example
78
+ npx @bytebase/dbhub --transport sse --port 8080 --dsn "sqlite:///path/to/database.db"
79
+
80
+ # MySQL example
81
+ npx @bytebase/dbhub --transport sse --port 8080 --dsn "mysql://user:password@localhost:3306/dbname"
54
82
  ```
55
83
 
56
84
  ## Usage
@@ -79,6 +107,17 @@ Database Source Name (DSN) is required to connect to your database. You can prov
79
107
  DSN=postgres://user:password@localhost:5432/dbname?sslmode=disable
80
108
  ```
81
109
 
110
+ ### Supported DSN formats
111
+
112
+ DBHub supports the following database connection string formats:
113
+
114
+ | Database | DSN Format | Example |
115
+ |------------|-----------------------------------------------------------|--------------------------------------------------------|
116
+ | PostgreSQL | `postgres://[user]:[password]@[host]:[port]/[database]` | `postgres://user:password@localhost:5432/dbname?sslmode=disable` |
117
+ | SQLite | `sqlite:///[path/to/file]` or `sqlite::memory:` | `sqlite:///path/to/database.db` or `sqlite::memory:` |
118
+ | SQL Server | `sqlserver://[user]:[password]@[host]:[port]/[database]` | `sqlserver://user:password@localhost:1433/dbname` |
119
+ | MySQL | `mysql://[user]:[password]@[host]:[port]/[database]` | `mysql://user:password@localhost:3306/dbname` |
120
+
82
121
  ### Transport
83
122
 
84
123
  - **stdio** (default) - for direct integration with tools like Claude Desktop:
@@ -102,17 +141,17 @@ Database Source Name (DSN) is required to connect to your database. You can prov
102
141
 
103
142
  ### Claude Desktop
104
143
 
105
- ![claude-desktop](https://raw.githubusercontent.com/bytebase/dbhub/main/assets/claude-desktop.webp)
144
+ ![claude-desktop](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/claude-desktop.webp)
106
145
 
107
146
  - Claude Desktop only supports `stdio` transport https://github.com/orgs/modelcontextprotocol/discussions/16
108
147
 
109
148
  #### Docker
110
149
 
111
150
  ```json
112
- // claude_desktop_config.json
151
+ // claude_desktop_config.json - PostgreSQL example
113
152
  {
114
153
  "mcpServers": {
115
- "dbhub": {
154
+ "dbhub-postgres": {
116
155
  "command": "docker",
117
156
  "args": [
118
157
  "run",
@@ -130,13 +169,34 @@ Database Source Name (DSN) is required to connect to your database. You can prov
130
169
  }
131
170
  ```
132
171
 
172
+ ```json
173
+ // claude_desktop_config.json - SQLite example (in-memory)
174
+ {
175
+ "mcpServers": {
176
+ "dbhub-sqlite": {
177
+ "command": "docker",
178
+ "args": [
179
+ "run",
180
+ "-i",
181
+ "--rm",
182
+ "bytebase/dbhub",
183
+ "--transport",
184
+ "stdio",
185
+ "--dsn",
186
+ "sqlite::memory:"
187
+ ]
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
133
193
  #### NPX
134
194
 
135
195
  ```json
136
- // claude_desktop_config.json
196
+ // claude_desktop_config.json - PostgreSQL example
137
197
  {
138
198
  "mcpServers": {
139
- "dbhub": {
199
+ "dbhub-postgres": {
140
200
  "command": "npx",
141
201
  "args": [
142
202
  "-y",
@@ -151,6 +211,44 @@ Database Source Name (DSN) is required to connect to your database. You can prov
151
211
  }
152
212
  ```
153
213
 
214
+ ```json
215
+ // claude_desktop_config.json - SQLite example
216
+ {
217
+ "mcpServers": {
218
+ "dbhub-sqlite": {
219
+ "command": "npx",
220
+ "args": [
221
+ "-y",
222
+ "@bytebase/dbhub",
223
+ "--transport",
224
+ "stdio",
225
+ "--dsn",
226
+ "sqlite:///path/to/database.db"
227
+ ]
228
+ }
229
+ }
230
+ }
231
+ ```
232
+
233
+ ```json
234
+ // claude_desktop_config.json - MySQL example
235
+ {
236
+ "mcpServers": {
237
+ "dbhub-mysql": {
238
+ "command": "npx",
239
+ "args": [
240
+ "-y",
241
+ "@bytebase/dbhub",
242
+ "--transport",
243
+ "stdio",
244
+ "--dsn",
245
+ "mysql://user:password@localhost:3306/dbname"
246
+ ]
247
+ }
248
+ }
249
+ }
250
+ ```
251
+
154
252
  ## Development
155
253
 
156
254
  1. Install dependencies:
@@ -176,7 +274,17 @@ Database Source Name (DSN) is required to connect to your database. You can prov
176
274
  #### stdio
177
275
 
178
276
  ```bash
277
+ # PostgreSQL example
179
278
  TRANSPORT=stdio DSN="postgres://user:password@localhost:5432/dbname?sslmode=disable" npx @modelcontextprotocol/inspector node /path/to/dbhub/dist/index.js
279
+
280
+ # SQLite example
281
+ TRANSPORT=stdio DSN="sqlite:///path/to/database.db" npx @modelcontextprotocol/inspector node /path/to/dbhub/dist/index.js
282
+
283
+ # SQLite in-memory example
284
+ TRANSPORT=stdio DSN="sqlite::memory:" npx @modelcontextprotocol/inspector node /path/to/dbhub/dist/index.js
285
+
286
+ # MySQL example
287
+ TRANSPORT=stdio DSN="mysql://user:password@localhost:3306/dbname" npx @modelcontextprotocol/inspector node /path/to/dbhub/dist/index.js
180
288
  ```
181
289
 
182
290
  #### SSE
@@ -191,4 +299,4 @@ npx @modelcontextprotocol/inspector
191
299
 
192
300
  Connect to the DBHub server `/sse` endpoint
193
301
 
194
- ![mcp-inspector](https://raw.githubusercontent.com/bytebase/dbhub/main/assets/mcp-inspector.webp)
302
+ ![mcp-inspector](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/mcp-inspector.webp)
@@ -0,0 +1,169 @@
1
+ import mysql from 'mysql2/promise';
2
+ import { ConnectorRegistry } from '../interface.js';
3
+ /**
4
+ * MySQL DSN Parser
5
+ * Handles DSN strings like: mysql://user:password@localhost:3306/dbname
6
+ */
7
+ class MySQLDSNParser {
8
+ parse(dsn) {
9
+ // Basic validation
10
+ if (!this.isValidDSN(dsn)) {
11
+ throw new Error(`Invalid MySQL DSN: ${dsn}`);
12
+ }
13
+ try {
14
+ const url = new URL(dsn);
15
+ const config = {
16
+ host: url.hostname,
17
+ port: url.port ? parseInt(url.port) : 3306,
18
+ database: url.pathname.substring(1), // Remove leading '/'
19
+ user: url.username,
20
+ password: url.password,
21
+ };
22
+ // Handle query parameters
23
+ url.searchParams.forEach((value, key) => {
24
+ if (key === 'ssl') {
25
+ config.ssl = value === 'true' ? {} : undefined;
26
+ }
27
+ // Add other parameters as needed
28
+ });
29
+ return config;
30
+ }
31
+ catch (error) {
32
+ throw new Error(`Failed to parse MySQL DSN: ${error instanceof Error ? error.message : String(error)}`);
33
+ }
34
+ }
35
+ getSampleDSN() {
36
+ return 'mysql://root:password@localhost:3306/mysql';
37
+ }
38
+ isValidDSN(dsn) {
39
+ try {
40
+ const url = new URL(dsn);
41
+ return url.protocol === 'mysql:';
42
+ }
43
+ catch (error) {
44
+ return false;
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * MySQL Connector Implementation
50
+ */
51
+ export class MySQLConnector {
52
+ constructor() {
53
+ this.id = 'mysql';
54
+ this.name = 'MySQL';
55
+ this.dsnParser = new MySQLDSNParser();
56
+ this.pool = null;
57
+ }
58
+ async connect(dsn) {
59
+ try {
60
+ const config = this.dsnParser.parse(dsn);
61
+ this.pool = mysql.createPool(config);
62
+ // Test the connection
63
+ const [rows] = await this.pool.query('SELECT 1');
64
+ console.error("Successfully connected to MySQL database");
65
+ }
66
+ catch (err) {
67
+ console.error("Failed to connect to MySQL database:", err);
68
+ throw err;
69
+ }
70
+ }
71
+ async disconnect() {
72
+ if (this.pool) {
73
+ await this.pool.end();
74
+ this.pool = null;
75
+ }
76
+ }
77
+ async getTables() {
78
+ if (!this.pool) {
79
+ throw new Error('Not connected to database');
80
+ }
81
+ try {
82
+ // Get all tables from the current database
83
+ const [rows] = await this.pool.query(`
84
+ SELECT table_name
85
+ FROM information_schema.tables
86
+ WHERE table_schema = DATABASE()
87
+ ORDER BY table_name
88
+ `);
89
+ return rows.map(row => row.table_name);
90
+ }
91
+ catch (error) {
92
+ console.error("Error getting tables:", error);
93
+ throw error;
94
+ }
95
+ }
96
+ async tableExists(tableName) {
97
+ if (!this.pool) {
98
+ throw new Error('Not connected to database');
99
+ }
100
+ try {
101
+ const [rows] = await this.pool.query(`
102
+ SELECT COUNT(*) as count
103
+ FROM information_schema.tables
104
+ WHERE table_schema = DATABASE()
105
+ AND table_name = ?
106
+ `, [tableName]);
107
+ return rows[0].count > 0;
108
+ }
109
+ catch (error) {
110
+ console.error("Error checking if table exists:", error);
111
+ throw error;
112
+ }
113
+ }
114
+ async getTableSchema(tableName) {
115
+ if (!this.pool) {
116
+ throw new Error('Not connected to database');
117
+ }
118
+ try {
119
+ // Get table columns
120
+ const [rows] = await this.pool.query(`
121
+ SELECT
122
+ column_name,
123
+ data_type,
124
+ is_nullable,
125
+ column_default
126
+ FROM information_schema.columns
127
+ WHERE table_schema = DATABASE()
128
+ AND table_name = ?
129
+ ORDER BY ordinal_position
130
+ `, [tableName]);
131
+ return rows;
132
+ }
133
+ catch (error) {
134
+ console.error("Error getting table schema:", error);
135
+ throw error;
136
+ }
137
+ }
138
+ async executeQuery(query) {
139
+ if (!this.pool) {
140
+ throw new Error('Not connected to database');
141
+ }
142
+ const safetyCheck = this.validateQuery(query);
143
+ if (!safetyCheck.isValid) {
144
+ throw new Error(safetyCheck.message || "Query validation failed");
145
+ }
146
+ try {
147
+ const [rows, fields] = await this.pool.query(query);
148
+ return { rows, fields };
149
+ }
150
+ catch (error) {
151
+ console.error("Error executing query:", error);
152
+ throw error;
153
+ }
154
+ }
155
+ validateQuery(query) {
156
+ // Basic check to prevent non-SELECT queries
157
+ const normalizedQuery = query.trim().toLowerCase();
158
+ if (!normalizedQuery.startsWith('select')) {
159
+ return {
160
+ isValid: false,
161
+ message: "Only SELECT queries are allowed for security reasons."
162
+ };
163
+ }
164
+ return { isValid: true };
165
+ }
166
+ }
167
+ // Create and register the connector
168
+ const mysqlConnector = new MySQLConnector();
169
+ ConnectorRegistry.register(mysqlConnector);
@@ -1,12 +1,13 @@
1
1
  /**
2
- * SQLite Connector Implementation (Template)
2
+ * SQLite Connector Implementation
3
3
  *
4
- * This is a template showing how to implement a new database connector.
4
+ * Implements SQLite database connectivity for DBHub
5
5
  * To use this connector:
6
- * 1. Install the required dependencies: npm install sqlite3
7
- * 2. Implement the methods below
8
- * 3. Set DSN=sqlite:///path/to/database.db in your .env file
6
+ * 1. Set DSN=sqlite:///path/to/database.db in your .env file
7
+ * 2. Or set DB_CONNECTOR_TYPE=sqlite for default in-memory database
9
8
  */
9
+ import { ConnectorRegistry } from '../interface.js';
10
+ import sqlite3 from 'sqlite3';
10
11
  /**
11
12
  * SQLite DSN Parser
12
13
  * Handles DSN strings like:
@@ -63,129 +64,117 @@ export class SQLiteConnector {
63
64
  this.id = 'sqlite';
64
65
  this.name = 'SQLite';
65
66
  this.dsnParser = new SQLiteDSNParser();
66
- this.dbPath = null;
67
+ this.db = null;
68
+ this.dbPath = ':memory:'; // Default to in-memory database
67
69
  }
68
70
  async connect(dsn) {
69
71
  const config = this.dsnParser.parse(dsn);
70
72
  this.dbPath = config.dbPath;
71
- // Example implementation (requires sqlite3 package)
72
- /*
73
73
  return new Promise((resolve, reject) => {
74
- const sqlite3 = require('sqlite3').verbose();
75
- this.db = new sqlite3.Database(this.dbPath, (err) => {
76
- if (err) {
77
- console.error("Failed to connect to SQLite database:", err);
78
- reject(err);
79
- } else {
80
- console.error("Successfully connected to SQLite database");
81
- resolve();
82
- }
83
- });
74
+ this.db = new sqlite3.Database(this.dbPath, (err) => {
75
+ if (err) {
76
+ console.error("Failed to connect to SQLite database:", err);
77
+ reject(err);
78
+ }
79
+ else {
80
+ // Can't use console.log here because it will break the stdio transport
81
+ console.error("Successfully connected to SQLite database");
82
+ resolve();
83
+ }
84
+ });
84
85
  });
85
- */
86
- throw new Error('SQLite connector not implemented yet');
87
86
  }
88
87
  async disconnect() {
89
88
  // Close the SQLite connection
90
- /*
91
89
  if (this.db) {
92
- return new Promise((resolve, reject) => {
93
- this.db.close((err) => {
94
- if (err) {
95
- reject(err);
96
- } else {
97
- this.db = null;
98
- resolve();
99
- }
90
+ return new Promise((resolve, reject) => {
91
+ this.db.close((err) => {
92
+ if (err) {
93
+ reject(err);
94
+ }
95
+ else {
96
+ this.db = null;
97
+ resolve();
98
+ }
99
+ });
100
100
  });
101
- });
102
101
  }
103
- */
104
- throw new Error('SQLite connector not implemented yet');
102
+ return Promise.resolve();
105
103
  }
106
104
  async getTables() {
107
- // Get all tables from SQLite
108
- /*
105
+ if (!this.db) {
106
+ throw new Error("Not connected to SQLite database");
107
+ }
109
108
  return new Promise((resolve, reject) => {
110
- this.db.all(
111
- `SELECT name FROM sqlite_master
112
- WHERE type='table' AND name NOT LIKE 'sqlite_%'
113
- ORDER BY name`,
114
- (err, rows) => {
115
- if (err) {
116
- reject(err);
117
- } else {
118
- resolve(rows.map(row => row.name));
119
- }
120
- }
121
- );
109
+ this.db.all(`SELECT name FROM sqlite_master
110
+ WHERE type='table' AND name NOT LIKE 'sqlite_%'
111
+ ORDER BY name`, (err, rows) => {
112
+ if (err) {
113
+ reject(err);
114
+ }
115
+ else {
116
+ resolve(rows.map(row => row.name));
117
+ }
118
+ });
122
119
  });
123
- */
124
- throw new Error('SQLite connector not implemented yet');
125
120
  }
126
121
  async tableExists(tableName) {
127
- // Check if a table exists in SQLite
128
- /*
122
+ if (!this.db) {
123
+ throw new Error("Not connected to SQLite database");
124
+ }
129
125
  return new Promise((resolve, reject) => {
130
- this.db.get(
131
- `SELECT name FROM sqlite_master
132
- WHERE type='table' AND name = ?`,
133
- [tableName],
134
- (err, row) => {
135
- if (err) {
136
- reject(err);
137
- } else {
138
- resolve(!!row);
139
- }
140
- }
141
- );
126
+ this.db.get(`SELECT name FROM sqlite_master
127
+ WHERE type='table' AND name = ?`, [tableName], (err, row) => {
128
+ if (err) {
129
+ reject(err);
130
+ }
131
+ else {
132
+ resolve(!!row);
133
+ }
134
+ });
142
135
  });
143
- */
144
- throw new Error('SQLite connector not implemented yet');
145
136
  }
146
137
  async getTableSchema(tableName) {
147
- // Get schema for a specific table
148
- /*
138
+ if (!this.db) {
139
+ throw new Error("Not connected to SQLite database");
140
+ }
149
141
  return new Promise((resolve, reject) => {
150
- this.db.all(
151
- `PRAGMA table_info(${tableName})`,
152
- (err, rows) => {
153
- if (err) {
154
- reject(err);
155
- } else {
156
- // Convert SQLite schema format to our standard TableColumn format
157
- const columns = rows.map(row => ({
158
- column_name: row.name,
159
- data_type: row.type,
160
- is_nullable: row.notnull ? 'NO' : 'YES',
161
- column_default: row.dflt_value
162
- }));
163
- resolve(columns);
164
- }
165
- }
166
- );
142
+ this.db.all(`PRAGMA table_info(${tableName})`, (err, rows) => {
143
+ if (err) {
144
+ reject(err);
145
+ }
146
+ else {
147
+ // Convert SQLite schema format to our standard TableColumn format
148
+ const columns = rows.map(row => ({
149
+ column_name: row.name,
150
+ data_type: row.type,
151
+ is_nullable: row.notnull === 0 ? 'YES' : 'NO', // In SQLite, 0 means nullable
152
+ column_default: row.dflt_value
153
+ }));
154
+ resolve(columns);
155
+ }
156
+ });
167
157
  });
168
- */
169
- throw new Error('SQLite connector not implemented yet');
170
158
  }
171
159
  async executeQuery(query) {
172
- // Execute a query against SQLite
160
+ if (!this.db) {
161
+ throw new Error("Not connected to SQLite database");
162
+ }
163
+ // Validate query for safety
173
164
  const safetyCheck = this.validateQuery(query);
174
165
  if (!safetyCheck.isValid) {
175
166
  throw new Error(safetyCheck.message || "Query validation failed");
176
167
  }
177
- /*
178
168
  return new Promise((resolve, reject) => {
179
- this.db.all(query, (err, rows) => {
180
- if (err) {
181
- reject(err);
182
- } else {
183
- resolve({ rows });
184
- }
185
- });
169
+ this.db.all(query, (err, rows) => {
170
+ if (err) {
171
+ reject(err);
172
+ }
173
+ else {
174
+ resolve({ rows });
175
+ }
176
+ });
186
177
  });
187
- */
188
- throw new Error('SQLite connector not implemented yet');
189
178
  }
190
179
  validateQuery(query) {
191
180
  // Basic check to prevent non-SELECT queries
@@ -199,6 +188,6 @@ export class SQLiteConnector {
199
188
  return { isValid: true };
200
189
  }
201
190
  }
202
- // To enable this connector, uncomment the following lines and install sqlite3:
203
- // const sqliteConnector = new SQLiteConnector();
204
- // ConnectorRegistry.register(sqliteConnector);
191
+ // Register the SQLite connector
192
+ const sqliteConnector = new SQLiteConnector();
193
+ ConnectorRegistry.register(sqliteConnector);
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  // Import connector modules to register them
3
3
  import './connectors/postgres/index.js'; // Register PostgreSQL connector
4
4
  import './connectors/sqlserver/index.js'; // Register SQL Server connector
5
- // import './connectors/sqlite/index.js'; // Uncomment to enable SQLite
5
+ import './connectors/sqlite/index.js'; // SQLite connector
6
+ import './connectors/mysql/index.js'; // MySQL connector
6
7
  // Import main function from server.ts
7
8
  import { main } from './server.js';
8
9
  /**
@@ -5,7 +5,7 @@ import { dbExplainerPromptHandler, dbExplainerSchema } from './db-explainer.js';
5
5
  */
6
6
  export function registerPrompts(server) {
7
7
  // Register SQL Generator prompt
8
- server.prompt("generate-sql", "Generate SQL queries from natural language descriptions", sqlGeneratorSchema, sqlGeneratorPromptHandler);
8
+ server.prompt("generate_sql", "Generate SQL queries from natural language descriptions", sqlGeneratorSchema, sqlGeneratorPromptHandler);
9
9
  // Register Database Explainer prompt
10
- server.prompt("explain-db", "Get explanations about database tables, columns, and structures", dbExplainerSchema, dbExplainerPromptHandler);
10
+ server.prompt("explain_db", "Get explanations about database tables, columns, and structures", dbExplainerSchema, dbExplainerPromptHandler);
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebase/dbhub",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Universal Database MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -17,10 +17,13 @@
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
19
  "@modelcontextprotocol/sdk": "^1.6.1",
20
+ "@types/sqlite3": "^5.1.0",
20
21
  "dotenv": "^16.4.7",
21
22
  "express": "^4.18.2",
22
23
  "mssql": "^11.0.1",
24
+ "mysql2": "^3.13.0",
23
25
  "pg": "^8.13.3",
26
+ "sqlite3": "^5.1.7",
24
27
  "zod": "^3.24.2"
25
28
  },
26
29
  "devDependencies": {