@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 +118 -10
- package/dist/connectors/mysql/index.js +169 -0
- package/dist/connectors/sqlite/index.js +88 -99
- package/dist/index.js +2 -1
- package/dist/prompts/index.js +2 -2
- package/package.json +4 -1
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/
|
|
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 +--->+
|
|
17
|
+
| Cursor +--->+ DBHub +--->+ SQL Server |
|
|
18
18
|
| | | | | |
|
|
19
19
|
| Other MCP +--->+ +--->+ SQLite |
|
|
20
20
|
| Clients | | | | |
|
|
21
|
-
| | | +--->+
|
|
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
|
-

|
|
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
|
-

|
|
@@ -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
|
|
2
|
+
* SQLite Connector Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Implements SQLite database connectivity for DBHub
|
|
5
5
|
* To use this connector:
|
|
6
|
-
* 1.
|
|
7
|
-
* 2.
|
|
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.
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
108
|
-
|
|
105
|
+
if (!this.db) {
|
|
106
|
+
throw new Error("Not connected to SQLite database");
|
|
107
|
+
}
|
|
109
108
|
return new Promise((resolve, reject) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
128
|
-
|
|
122
|
+
if (!this.db) {
|
|
123
|
+
throw new Error("Not connected to SQLite database");
|
|
124
|
+
}
|
|
129
125
|
return new Promise((resolve, reject) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
148
|
-
|
|
138
|
+
if (!this.db) {
|
|
139
|
+
throw new Error("Not connected to SQLite database");
|
|
140
|
+
}
|
|
149
141
|
return new Promise((resolve, reject) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/dist/prompts/index.js
CHANGED
|
@@ -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("
|
|
8
|
+
server.prompt("generate_sql", "Generate SQL queries from natural language descriptions", sqlGeneratorSchema, sqlGeneratorPromptHandler);
|
|
9
9
|
// Register Database Explainer prompt
|
|
10
|
-
server.prompt("
|
|
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.
|
|
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": {
|