@bytebase/dbhub 0.2.3 → 0.3.0
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 +36 -23
- package/dist/index.js +448 -83
- package/package.json +3 -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/resources/images/logo-full.
|
|
4
|
+
<img src="https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/logo-full.webp" width="50%">
|
|
5
5
|
</picture>
|
|
6
6
|
</a>
|
|
7
7
|
</p>
|
|
@@ -20,7 +20,7 @@ DBHub is a universal database gateway implementing the Model Context Protocol (M
|
|
|
20
20
|
| Clients | | | | |
|
|
21
21
|
| | | +--->+ MySQL |
|
|
22
22
|
| | | | | |
|
|
23
|
-
| | | +--->+
|
|
23
|
+
| | | +--->+ MariaDB |
|
|
24
24
|
| | | | | |
|
|
25
25
|
+------------------+ +--------------+ +------------------+
|
|
26
26
|
MCP Clients MCP Server Databases
|
|
@@ -36,28 +36,28 @@ https://demo.dbhub.ai/sse connects a [sample employee database](https://github.c
|
|
|
36
36
|
|
|
37
37
|
### Database Resources
|
|
38
38
|
|
|
39
|
-
| Resource Name | URI Format | PostgreSQL | MySQL | SQL Server | SQLite |
|
|
40
|
-
| --------------------------- | ------------------------------------------------------ | :--------: | :---: | :--------: | :----: |
|
|
41
|
-
| schemas | `db://schemas` | ✅ | ✅ | ✅ | ✅ |
|
|
42
|
-
| tables_in_schema | `db://schemas/{schemaName}/tables` | ✅ | ✅ | ✅ | ✅ |
|
|
43
|
-
| table_structure_in_schema | `db://schemas/{schemaName}/tables/{tableName}` | ✅ | ✅ | ✅ | ✅ |
|
|
44
|
-
| indexes_in_table | `db://schemas/{schemaName}/tables/{tableName}/indexes` | ✅ | ✅ | ✅ | ✅ |
|
|
45
|
-
| procedures_in_schema | `db://schemas/{schemaName}/procedures` | ✅ | ✅ | ✅ | ❌ |
|
|
46
|
-
| procedure_details_in_schema | `db://schemas/{schemaName}/procedures/{procedureName}` | ✅ | ✅ | ✅ | ❌ |
|
|
39
|
+
| Resource Name | URI Format | PostgreSQL | MySQL | MariaDB | SQL Server | SQLite |
|
|
40
|
+
| --------------------------- | ------------------------------------------------------ | :--------: | :---: | :-----: | :--------: | :----: |
|
|
41
|
+
| schemas | `db://schemas` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
42
|
+
| tables_in_schema | `db://schemas/{schemaName}/tables` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
43
|
+
| table_structure_in_schema | `db://schemas/{schemaName}/tables/{tableName}` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
44
|
+
| indexes_in_table | `db://schemas/{schemaName}/tables/{tableName}/indexes` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
45
|
+
| procedures_in_schema | `db://schemas/{schemaName}/procedures` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
46
|
+
| procedure_details_in_schema | `db://schemas/{schemaName}/procedures/{procedureName}` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
47
47
|
|
|
48
48
|
### Database Tools
|
|
49
49
|
|
|
50
|
-
| Tool | Command Name | PostgreSQL | MySQL | SQL Server | SQLite |
|
|
51
|
-
| --------------- | ----------------- | :--------: | :---: | :--------: |
|
|
52
|
-
| Execute Query | `run_query` | ✅ | ✅ | ✅ |
|
|
53
|
-
| List Connectors | `list_connectors` | ✅ | ✅ | ✅ |
|
|
50
|
+
| Tool | Command Name | PostgreSQL | MySQL | MariaDB | SQL Server | SQLite |
|
|
51
|
+
| --------------- | ----------------- | :--------: | :---: | :-----: | :--------: | ------ |
|
|
52
|
+
| Execute Query | `run_query` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
53
|
+
| List Connectors | `list_connectors` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
54
54
|
|
|
55
55
|
### Prompt Capabilities
|
|
56
56
|
|
|
57
|
-
| Prompt | Command Name | PostgreSQL | MySQL | SQL Server | SQLite |
|
|
58
|
-
| ------------------- | -------------- | :--------: | :---: | :--------: |
|
|
59
|
-
| Generate SQL | `generate_sql` | ✅ | ✅ | ✅ |
|
|
60
|
-
| Explain DB Elements | `explain_db` | ✅ | ✅ | ✅ |
|
|
57
|
+
| Prompt | Command Name | PostgreSQL | MySQL | MariaDB | SQL Server | SQLite |
|
|
58
|
+
| ------------------- | -------------- | :--------: | :---: | :-----: | :--------: | ------ |
|
|
59
|
+
| Generate SQL | `generate_sql` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
60
|
+
| Explain DB Elements | `explain_db` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
61
61
|
|
|
62
62
|
## Installation
|
|
63
63
|
|
|
@@ -156,7 +156,7 @@ npx @bytebase/dbhub --transport sse --port 8080 --demo
|
|
|
156
156
|
You can use DBHub in demo mode with a sample employee database for testing:
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
|
-
|
|
159
|
+
npx @bytebase/dbhub --demo
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
For real databases, a Database Source Name (DSN) is required. You can provide this in several ways:
|
|
@@ -164,14 +164,14 @@ For real databases, a Database Source Name (DSN) is required. You can provide th
|
|
|
164
164
|
- **Command line argument** (highest priority):
|
|
165
165
|
|
|
166
166
|
```bash
|
|
167
|
-
|
|
167
|
+
npx @bytebase/dbhub --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
- **Environment variable** (second priority):
|
|
171
171
|
|
|
172
172
|
```bash
|
|
173
173
|
export DSN="postgres://user:password@localhost:5432/dbname?sslmode=disable"
|
|
174
|
-
|
|
174
|
+
npx @bytebase/dbhub
|
|
175
175
|
```
|
|
176
176
|
|
|
177
177
|
- **Environment file** (third priority):
|
|
@@ -188,10 +188,19 @@ DBHub supports the following database connection string formats:
|
|
|
188
188
|
|
|
189
189
|
| Database | DSN Format | Example |
|
|
190
190
|
| ---------- | -------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
191
|
+
| MySQL | `mysql://[user]:[password]@[host]:[port]/[database]` | `mysql://user:password@localhost:3306/dbname` |
|
|
192
|
+
| MariaDB | `mariadb://[user]:[password]@[host]:[port]/[database]` | `mariadb://user:password@localhost:3306/dbname` |
|
|
191
193
|
| PostgreSQL | `postgres://[user]:[password]@[host]:[port]/[database]` | `postgres://user:password@localhost:5432/dbname?sslmode=disable` |
|
|
192
|
-
| SQLite | `sqlite:///[path/to/file]` or `sqlite::memory:` | `sqlite:///path/to/database.db` or `sqlite::memory:` |
|
|
193
194
|
| SQL Server | `sqlserver://[user]:[password]@[host]:[port]/[database]` | `sqlserver://user:password@localhost:1433/dbname` |
|
|
194
|
-
|
|
|
195
|
+
| SQLite | `sqlite:///[path/to/file]` or `sqlite::memory:` | `sqlite:///path/to/database.db` or `sqlite::memory:` |
|
|
196
|
+
|
|
197
|
+
#### SQL Server
|
|
198
|
+
|
|
199
|
+
Extra query parameters:
|
|
200
|
+
|
|
201
|
+
#### authentication
|
|
202
|
+
|
|
203
|
+
- `authentication=azure-active-directory-access-token`. Only applicable when running from Azure. See [DefaultAzureCredential](https://learn.microsoft.com/en-us/azure/developer/javascript/sdk/authentication/credential-chains#use-defaultazurecredential-for-flexibility).
|
|
195
204
|
|
|
196
205
|
### Transport
|
|
197
206
|
|
|
@@ -257,3 +266,7 @@ npx @modelcontextprotocol/inspector
|
|
|
257
266
|
```
|
|
258
267
|
|
|
259
268
|
Connect to the DBHub server `/sse` endpoint
|
|
269
|
+
|
|
270
|
+
## Star History
|
|
271
|
+
|
|
272
|
+
[](https://www.star-history.com/#bytebase/dbhub&Date)
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,7 @@ var ConnectorRegistry = _ConnectorRegistry;
|
|
|
60
60
|
// src/connectors/postgres/index.ts
|
|
61
61
|
var { Pool } = pg;
|
|
62
62
|
var PostgresDSNParser = class {
|
|
63
|
-
parse(dsn) {
|
|
63
|
+
async parse(dsn) {
|
|
64
64
|
if (!this.isValidDSN(dsn)) {
|
|
65
65
|
throw new Error(`Invalid PostgreSQL DSN: ${dsn}`);
|
|
66
66
|
}
|
|
@@ -72,7 +72,7 @@ var PostgresDSNParser = class {
|
|
|
72
72
|
database: url.pathname.substring(1),
|
|
73
73
|
// Remove leading '/'
|
|
74
74
|
user: url.username,
|
|
75
|
-
password: url.password
|
|
75
|
+
password: url.password ? decodeURIComponent(url.password) : ""
|
|
76
76
|
};
|
|
77
77
|
url.searchParams.forEach((value, key) => {
|
|
78
78
|
if (key === "sslmode") {
|
|
@@ -105,7 +105,7 @@ var PostgresConnector = class {
|
|
|
105
105
|
}
|
|
106
106
|
async connect(dsn) {
|
|
107
107
|
try {
|
|
108
|
-
const config = this.dsnParser.parse(dsn);
|
|
108
|
+
const config = await this.dsnParser.parse(dsn);
|
|
109
109
|
this.pool = new Pool(config);
|
|
110
110
|
const client = await this.pool.connect();
|
|
111
111
|
console.error("Successfully connected to PostgreSQL database");
|
|
@@ -363,8 +363,9 @@ ConnectorRegistry.register(postgresConnector);
|
|
|
363
363
|
|
|
364
364
|
// src/connectors/sqlserver/index.ts
|
|
365
365
|
import sql from "mssql";
|
|
366
|
+
import { DefaultAzureCredential } from "@azure/identity";
|
|
366
367
|
var SQLServerDSNParser = class {
|
|
367
|
-
parse(dsn) {
|
|
368
|
+
async parse(dsn) {
|
|
368
369
|
if (!this.isValidDSN(dsn)) {
|
|
369
370
|
throw new Error("Invalid SQL Server DSN format. Expected: sqlserver://username:password@host:port/database");
|
|
370
371
|
}
|
|
@@ -373,7 +374,7 @@ var SQLServerDSNParser = class {
|
|
|
373
374
|
const port = url.port ? parseInt(url.port, 10) : 1433;
|
|
374
375
|
const database = url.pathname.substring(1);
|
|
375
376
|
const user = url.username;
|
|
376
|
-
const password = url.password;
|
|
377
|
+
const password = url.password ? decodeURIComponent(url.password) : "";
|
|
377
378
|
const options = {};
|
|
378
379
|
for (const [key, value] of url.searchParams.entries()) {
|
|
379
380
|
if (key === "encrypt") {
|
|
@@ -384,9 +385,11 @@ var SQLServerDSNParser = class {
|
|
|
384
385
|
options.connectTimeout = parseInt(value, 10);
|
|
385
386
|
} else if (key === "requestTimeout") {
|
|
386
387
|
options.requestTimeout = parseInt(value, 10);
|
|
388
|
+
} else if (key === "authentication") {
|
|
389
|
+
options.authentication = value;
|
|
387
390
|
}
|
|
388
391
|
}
|
|
389
|
-
|
|
392
|
+
const config = {
|
|
390
393
|
user,
|
|
391
394
|
password,
|
|
392
395
|
server: host,
|
|
@@ -401,6 +404,22 @@ var SQLServerDSNParser = class {
|
|
|
401
404
|
requestTimeout: options.requestTimeout ?? 15e3
|
|
402
405
|
}
|
|
403
406
|
};
|
|
407
|
+
if (options.authentication === "azure-active-directory-access-token") {
|
|
408
|
+
try {
|
|
409
|
+
const credential = new DefaultAzureCredential();
|
|
410
|
+
const token = await credential.getToken("https://database.windows.net/");
|
|
411
|
+
config.authentication = {
|
|
412
|
+
type: "azure-active-directory-access-token",
|
|
413
|
+
options: {
|
|
414
|
+
token: token.token
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
} catch (error) {
|
|
418
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
419
|
+
throw new Error(`Failed to get Azure AD token: ${errorMessage}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return config;
|
|
404
423
|
}
|
|
405
424
|
getSampleDSN() {
|
|
406
425
|
return "sqlserver://username:password@localhost:1433/database?encrypt=true";
|
|
@@ -422,7 +441,7 @@ var SQLServerConnector = class {
|
|
|
422
441
|
}
|
|
423
442
|
async connect(dsn) {
|
|
424
443
|
try {
|
|
425
|
-
this.config = this.dsnParser.parse(dsn);
|
|
444
|
+
this.config = await this.dsnParser.parse(dsn);
|
|
426
445
|
if (!this.config.options) {
|
|
427
446
|
this.config.options = {};
|
|
428
447
|
}
|
|
@@ -443,9 +462,9 @@ var SQLServerConnector = class {
|
|
|
443
462
|
}
|
|
444
463
|
try {
|
|
445
464
|
const result = await this.connection.request().query(`
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
465
|
+
SELECT SCHEMA_NAME
|
|
466
|
+
FROM INFORMATION_SCHEMA.SCHEMATA
|
|
467
|
+
ORDER BY SCHEMA_NAME
|
|
449
468
|
`);
|
|
450
469
|
return result.recordset.map((row) => row.SCHEMA_NAME);
|
|
451
470
|
} catch (error) {
|
|
@@ -460,10 +479,10 @@ var SQLServerConnector = class {
|
|
|
460
479
|
const schemaToUse = schema || "dbo";
|
|
461
480
|
const request = this.connection.request().input("schema", sql.VarChar, schemaToUse);
|
|
462
481
|
const query = `
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
482
|
+
SELECT TABLE_NAME
|
|
483
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
484
|
+
WHERE TABLE_SCHEMA = @schema
|
|
485
|
+
ORDER BY TABLE_NAME
|
|
467
486
|
`;
|
|
468
487
|
const result = await request.query(query);
|
|
469
488
|
return result.recordset.map((row) => row.TABLE_NAME);
|
|
@@ -479,10 +498,10 @@ var SQLServerConnector = class {
|
|
|
479
498
|
const schemaToUse = schema || "dbo";
|
|
480
499
|
const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
|
|
481
500
|
const query = `
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
501
|
+
SELECT COUNT(*) as count
|
|
502
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
503
|
+
WHERE TABLE_NAME = @tableName
|
|
504
|
+
AND TABLE_SCHEMA = @schema
|
|
486
505
|
`;
|
|
487
506
|
const result = await request.query(query);
|
|
488
507
|
return result.recordset[0].count > 0;
|
|
@@ -498,28 +517,24 @@ var SQLServerConnector = class {
|
|
|
498
517
|
const schemaToUse = schema || "dbo";
|
|
499
518
|
const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
|
|
500
519
|
const query = `
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
AND s.name = @schema
|
|
520
|
-
ORDER BY
|
|
521
|
-
i.name,
|
|
522
|
-
ic.key_ordinal
|
|
520
|
+
SELECT i.name AS index_name,
|
|
521
|
+
i.is_unique,
|
|
522
|
+
i.is_primary_key,
|
|
523
|
+
c.name AS column_name,
|
|
524
|
+
ic.key_ordinal
|
|
525
|
+
FROM sys.indexes i
|
|
526
|
+
INNER JOIN
|
|
527
|
+
sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
528
|
+
INNER JOIN
|
|
529
|
+
sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
530
|
+
INNER JOIN
|
|
531
|
+
sys.tables t ON i.object_id = t.object_id
|
|
532
|
+
INNER JOIN
|
|
533
|
+
sys.schemas s ON t.schema_id = s.schema_id
|
|
534
|
+
WHERE t.name = @tableName
|
|
535
|
+
AND s.name = @schema
|
|
536
|
+
ORDER BY i.name,
|
|
537
|
+
ic.key_ordinal
|
|
523
538
|
`;
|
|
524
539
|
const result = await request.query(query);
|
|
525
540
|
const indexMap = /* @__PURE__ */ new Map();
|
|
@@ -560,15 +575,14 @@ var SQLServerConnector = class {
|
|
|
560
575
|
const schemaToUse = schema || "dbo";
|
|
561
576
|
const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
|
|
562
577
|
const query = `
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
ORDER BY ORDINAL_POSITION
|
|
578
|
+
SELECT COLUMN_NAME as column_name,
|
|
579
|
+
DATA_TYPE as data_type,
|
|
580
|
+
IS_NULLABLE as is_nullable,
|
|
581
|
+
COLUMN_DEFAULT as column_default
|
|
582
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
583
|
+
WHERE TABLE_NAME = @tableName
|
|
584
|
+
AND TABLE_SCHEMA = @schema
|
|
585
|
+
ORDER BY ORDINAL_POSITION
|
|
572
586
|
`;
|
|
573
587
|
const result = await request.query(query);
|
|
574
588
|
return result.recordset;
|
|
@@ -584,11 +598,11 @@ var SQLServerConnector = class {
|
|
|
584
598
|
const schemaToUse = schema || "dbo";
|
|
585
599
|
const request = this.connection.request().input("schema", sql.VarChar, schemaToUse);
|
|
586
600
|
const query = `
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
601
|
+
SELECT ROUTINE_NAME
|
|
602
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
603
|
+
WHERE ROUTINE_SCHEMA = @schema
|
|
604
|
+
AND (ROUTINE_TYPE = 'PROCEDURE' OR ROUTINE_TYPE = 'FUNCTION')
|
|
605
|
+
ORDER BY ROUTINE_NAME
|
|
592
606
|
`;
|
|
593
607
|
const result = await request.query(query);
|
|
594
608
|
return result.recordset.map((row) => row.ROUTINE_NAME);
|
|
@@ -604,13 +618,12 @@ var SQLServerConnector = class {
|
|
|
604
618
|
const schemaToUse = schema || "dbo";
|
|
605
619
|
const request = this.connection.request().input("procedureName", sql.VarChar, procedureName).input("schema", sql.VarChar, schemaToUse);
|
|
606
620
|
const routineQuery = `
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
AND ROUTINE_SCHEMA = @schema
|
|
621
|
+
SELECT ROUTINE_NAME as procedure_name,
|
|
622
|
+
ROUTINE_TYPE,
|
|
623
|
+
DATA_TYPE as return_data_type
|
|
624
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
625
|
+
WHERE ROUTINE_NAME = @procedureName
|
|
626
|
+
AND ROUTINE_SCHEMA = @schema
|
|
614
627
|
`;
|
|
615
628
|
const routineResult = await request.query(routineQuery);
|
|
616
629
|
if (routineResult.recordset.length === 0) {
|
|
@@ -618,16 +631,15 @@ var SQLServerConnector = class {
|
|
|
618
631
|
}
|
|
619
632
|
const routine = routineResult.recordset[0];
|
|
620
633
|
const parameterQuery = `
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
ORDER BY ORDINAL_POSITION
|
|
634
|
+
SELECT PARAMETER_NAME,
|
|
635
|
+
PARAMETER_MODE,
|
|
636
|
+
DATA_TYPE,
|
|
637
|
+
CHARACTER_MAXIMUM_LENGTH,
|
|
638
|
+
ORDINAL_POSITION
|
|
639
|
+
FROM INFORMATION_SCHEMA.PARAMETERS
|
|
640
|
+
WHERE SPECIFIC_NAME = @procedureName
|
|
641
|
+
AND SPECIFIC_SCHEMA = @schema
|
|
642
|
+
ORDER BY ORDINAL_POSITION
|
|
631
643
|
`;
|
|
632
644
|
const parameterResult = await request.query(parameterQuery);
|
|
633
645
|
let parameterList = "";
|
|
@@ -638,12 +650,12 @@ var SQLServerConnector = class {
|
|
|
638
650
|
}).join(", ");
|
|
639
651
|
}
|
|
640
652
|
const definitionQuery = `
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
653
|
+
SELECT definition
|
|
654
|
+
FROM sys.sql_modules sm
|
|
655
|
+
JOIN sys.objects o ON sm.object_id = o.object_id
|
|
656
|
+
JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
657
|
+
WHERE o.name = @procedureName
|
|
658
|
+
AND s.name = @schema
|
|
647
659
|
`;
|
|
648
660
|
const definitionResult = await request.query(definitionQuery);
|
|
649
661
|
let definition = void 0;
|
|
@@ -701,7 +713,7 @@ ConnectorRegistry.register(sqlServerConnector);
|
|
|
701
713
|
// src/connectors/sqlite/index.ts
|
|
702
714
|
import Database from "better-sqlite3";
|
|
703
715
|
var SQLiteDSNParser = class {
|
|
704
|
-
parse(dsn) {
|
|
716
|
+
async parse(dsn) {
|
|
705
717
|
if (!this.isValidDSN(dsn)) {
|
|
706
718
|
throw new Error(`Invalid SQLite DSN: ${dsn}`);
|
|
707
719
|
}
|
|
@@ -744,7 +756,7 @@ var SQLiteConnector = class {
|
|
|
744
756
|
}
|
|
745
757
|
// Default to in-memory database
|
|
746
758
|
async connect(dsn, initScript) {
|
|
747
|
-
const config = this.dsnParser.parse(dsn);
|
|
759
|
+
const config = await this.dsnParser.parse(dsn);
|
|
748
760
|
this.dbPath = config.dbPath;
|
|
749
761
|
try {
|
|
750
762
|
this.db = new Database(this.dbPath);
|
|
@@ -908,7 +920,7 @@ ConnectorRegistry.register(sqliteConnector);
|
|
|
908
920
|
// src/connectors/mysql/index.ts
|
|
909
921
|
import mysql from "mysql2/promise";
|
|
910
922
|
var MySQLDSNParser = class {
|
|
911
|
-
parse(dsn) {
|
|
923
|
+
async parse(dsn) {
|
|
912
924
|
if (!this.isValidDSN(dsn)) {
|
|
913
925
|
throw new Error(`Invalid MySQL DSN: ${dsn}`);
|
|
914
926
|
}
|
|
@@ -920,7 +932,7 @@ var MySQLDSNParser = class {
|
|
|
920
932
|
database: url.pathname.substring(1),
|
|
921
933
|
// Remove leading '/'
|
|
922
934
|
user: url.username,
|
|
923
|
-
password: url.password
|
|
935
|
+
password: url.password ? decodeURIComponent(url.password) : ""
|
|
924
936
|
};
|
|
925
937
|
url.searchParams.forEach((value, key) => {
|
|
926
938
|
if (key === "ssl") {
|
|
@@ -953,7 +965,7 @@ var MySQLConnector = class {
|
|
|
953
965
|
}
|
|
954
966
|
async connect(dsn) {
|
|
955
967
|
try {
|
|
956
|
-
const config = this.dsnParser.parse(dsn);
|
|
968
|
+
const config = await this.dsnParser.parse(dsn);
|
|
957
969
|
this.pool = mysql.createPool(config);
|
|
958
970
|
const [rows] = await this.pool.query("SELECT 1");
|
|
959
971
|
console.error("Successfully connected to MySQL database");
|
|
@@ -1247,6 +1259,348 @@ var MySQLConnector = class {
|
|
|
1247
1259
|
var mysqlConnector = new MySQLConnector();
|
|
1248
1260
|
ConnectorRegistry.register(mysqlConnector);
|
|
1249
1261
|
|
|
1262
|
+
// src/connectors/mariadb/index.ts
|
|
1263
|
+
import mariadb from "mariadb";
|
|
1264
|
+
var MariadbDSNParser = class {
|
|
1265
|
+
async parse(dsn) {
|
|
1266
|
+
if (!this.isValidDSN(dsn)) {
|
|
1267
|
+
throw new Error(`Invalid MariaDB DSN: ${dsn}`);
|
|
1268
|
+
}
|
|
1269
|
+
try {
|
|
1270
|
+
const url = new URL(dsn);
|
|
1271
|
+
const config = {
|
|
1272
|
+
host: url.hostname,
|
|
1273
|
+
port: url.port ? parseInt(url.port) : 3306,
|
|
1274
|
+
database: url.pathname.substring(1),
|
|
1275
|
+
user: url.username,
|
|
1276
|
+
password: decodeURIComponent(url.password)
|
|
1277
|
+
};
|
|
1278
|
+
url.searchParams.forEach((value, key) => {
|
|
1279
|
+
if (key === "ssl") {
|
|
1280
|
+
config.ssl = value === "true" ? {} : void 0;
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
return config;
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
throw new Error(`Failed to parse MariaDB DSN: ${error instanceof Error ? error.message : String(error)}`);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
getSampleDSN() {
|
|
1289
|
+
return "mariadb://root:password@localhost:3306/db";
|
|
1290
|
+
}
|
|
1291
|
+
isValidDSN(dsn) {
|
|
1292
|
+
try {
|
|
1293
|
+
const url = new URL(dsn);
|
|
1294
|
+
return url.protocol === "mariadb:";
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
var MariaDBConnector = class {
|
|
1301
|
+
constructor() {
|
|
1302
|
+
this.id = "mariadb";
|
|
1303
|
+
this.name = "MariaDB";
|
|
1304
|
+
this.dsnParser = new MariadbDSNParser();
|
|
1305
|
+
this.pool = null;
|
|
1306
|
+
}
|
|
1307
|
+
async connect(dsn) {
|
|
1308
|
+
try {
|
|
1309
|
+
const config = await this.dsnParser.parse(dsn);
|
|
1310
|
+
this.pool = mariadb.createPool(config);
|
|
1311
|
+
console.error("Testing connection to MariaDB...");
|
|
1312
|
+
const [rows] = await this.pool.query("SELECT 1");
|
|
1313
|
+
console.error("Successfully connected to MariaDB database");
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
console.error("Failed to connect to MariaDB database:", err);
|
|
1316
|
+
throw err;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
async disconnect() {
|
|
1320
|
+
if (this.pool) {
|
|
1321
|
+
await this.pool.end();
|
|
1322
|
+
this.pool = null;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
async getSchemas() {
|
|
1326
|
+
if (!this.pool) {
|
|
1327
|
+
throw new Error("Not connected to database");
|
|
1328
|
+
}
|
|
1329
|
+
try {
|
|
1330
|
+
const [rows] = await this.pool.query(`
|
|
1331
|
+
SELECT SCHEMA_NAME
|
|
1332
|
+
FROM INFORMATION_SCHEMA.SCHEMATA
|
|
1333
|
+
ORDER BY SCHEMA_NAME
|
|
1334
|
+
`);
|
|
1335
|
+
return rows.map((row) => row.SCHEMA_NAME);
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
console.error("Error getting schemas:", error);
|
|
1338
|
+
throw error;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
async getTables(schema) {
|
|
1342
|
+
if (!this.pool) {
|
|
1343
|
+
throw new Error("Not connected to database");
|
|
1344
|
+
}
|
|
1345
|
+
try {
|
|
1346
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
1347
|
+
const queryParams = schema ? [schema] : [];
|
|
1348
|
+
const [rows] = await this.pool.query(`
|
|
1349
|
+
SELECT TABLE_NAME
|
|
1350
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
1351
|
+
${schemaClause}
|
|
1352
|
+
ORDER BY TABLE_NAME
|
|
1353
|
+
`, queryParams);
|
|
1354
|
+
return rows.map((row) => row.TABLE_NAME);
|
|
1355
|
+
} catch (error) {
|
|
1356
|
+
console.error("Error getting tables:", error);
|
|
1357
|
+
throw error;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
async tableExists(tableName, schema) {
|
|
1361
|
+
if (!this.pool) {
|
|
1362
|
+
throw new Error("Not connected to database");
|
|
1363
|
+
}
|
|
1364
|
+
try {
|
|
1365
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
1366
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
1367
|
+
const [rows] = await this.pool.query(`
|
|
1368
|
+
SELECT COUNT(*) AS COUNT
|
|
1369
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
1370
|
+
${schemaClause}
|
|
1371
|
+
AND TABLE_NAME = ?
|
|
1372
|
+
`, queryParams);
|
|
1373
|
+
return rows[0].COUNT > 0;
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
console.error("Error checking if table exists:", error);
|
|
1376
|
+
throw error;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
async getTableIndexes(tableName, schema) {
|
|
1380
|
+
if (!this.pool) {
|
|
1381
|
+
throw new Error("Not connected to database");
|
|
1382
|
+
}
|
|
1383
|
+
try {
|
|
1384
|
+
const schemaClause = schema ? "TABLE_SCHEMA = ?" : "TABLE_SCHEMA = DATABASE()";
|
|
1385
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
1386
|
+
const [indexRows] = await this.pool.query(`
|
|
1387
|
+
SELECT
|
|
1388
|
+
INDEX_NAME,
|
|
1389
|
+
COLUMN_NAME,
|
|
1390
|
+
NON_UNIQUE,
|
|
1391
|
+
SEQ_IN_INDEX
|
|
1392
|
+
FROM
|
|
1393
|
+
INFORMATION_SCHEMA.STATISTICS
|
|
1394
|
+
WHERE
|
|
1395
|
+
${schemaClause}
|
|
1396
|
+
AND TABLE_NAME = ?
|
|
1397
|
+
ORDER BY
|
|
1398
|
+
INDEX_NAME,
|
|
1399
|
+
SEQ_IN_INDEX
|
|
1400
|
+
`, queryParams);
|
|
1401
|
+
const indexMap = /* @__PURE__ */ new Map();
|
|
1402
|
+
for (const row of indexRows) {
|
|
1403
|
+
const indexName = row.INDEX_NAME;
|
|
1404
|
+
const columnName = row.COLUMN_NAME;
|
|
1405
|
+
const isUnique = row.NON_UNIQUE === 0;
|
|
1406
|
+
const isPrimary = indexName === "PRIMARY";
|
|
1407
|
+
if (!indexMap.has(indexName)) {
|
|
1408
|
+
indexMap.set(indexName, {
|
|
1409
|
+
columns: [],
|
|
1410
|
+
is_unique: isUnique,
|
|
1411
|
+
is_primary: isPrimary
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
const indexInfo = indexMap.get(indexName);
|
|
1415
|
+
indexInfo.columns.push(columnName);
|
|
1416
|
+
}
|
|
1417
|
+
const results = [];
|
|
1418
|
+
indexMap.forEach((indexInfo, indexName) => {
|
|
1419
|
+
results.push({
|
|
1420
|
+
index_name: indexName,
|
|
1421
|
+
column_names: indexInfo.columns,
|
|
1422
|
+
is_unique: indexInfo.is_unique,
|
|
1423
|
+
is_primary: indexInfo.is_primary
|
|
1424
|
+
});
|
|
1425
|
+
});
|
|
1426
|
+
return results;
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
console.error("Error getting table indexes:", error);
|
|
1429
|
+
throw error;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
async getTableSchema(tableName, schema) {
|
|
1433
|
+
if (!this.pool) {
|
|
1434
|
+
throw new Error("Not connected to database");
|
|
1435
|
+
}
|
|
1436
|
+
try {
|
|
1437
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
1438
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
1439
|
+
const [rows] = await this.pool.query(`
|
|
1440
|
+
SELECT
|
|
1441
|
+
COLUMN_NAME as column_name,
|
|
1442
|
+
DATA_TYPE as data_type,
|
|
1443
|
+
IS_NULLABLE as is_nullable,
|
|
1444
|
+
COLUMN_DEFAULT as column_default
|
|
1445
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
1446
|
+
${schemaClause}
|
|
1447
|
+
AND TABLE_NAME = ?
|
|
1448
|
+
ORDER BY ORDINAL_POSITION
|
|
1449
|
+
`, queryParams);
|
|
1450
|
+
return rows;
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
console.error("Error getting table schema:", error);
|
|
1453
|
+
throw error;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
async getStoredProcedures(schema) {
|
|
1457
|
+
if (!this.pool) {
|
|
1458
|
+
throw new Error("Not connected to database");
|
|
1459
|
+
}
|
|
1460
|
+
try {
|
|
1461
|
+
const schemaClause = schema ? "WHERE ROUTINE_SCHEMA = ?" : "WHERE ROUTINE_SCHEMA = DATABASE()";
|
|
1462
|
+
const queryParams = schema ? [schema] : [];
|
|
1463
|
+
const [rows] = await this.pool.query(`
|
|
1464
|
+
SELECT ROUTINE_NAME
|
|
1465
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
1466
|
+
${schemaClause}
|
|
1467
|
+
ORDER BY ROUTINE_NAME
|
|
1468
|
+
`, queryParams);
|
|
1469
|
+
return rows.map((row) => row.ROUTINE_NAME);
|
|
1470
|
+
} catch (error) {
|
|
1471
|
+
console.error("Error getting stored procedures:", error);
|
|
1472
|
+
throw error;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
async getStoredProcedureDetail(procedureName, schema) {
|
|
1476
|
+
if (!this.pool) {
|
|
1477
|
+
throw new Error("Not connected to database");
|
|
1478
|
+
}
|
|
1479
|
+
try {
|
|
1480
|
+
const schemaClause = schema ? "WHERE r.ROUTINE_SCHEMA = ?" : "WHERE r.ROUTINE_SCHEMA = DATABASE()";
|
|
1481
|
+
const queryParams = schema ? [schema, procedureName] : [procedureName];
|
|
1482
|
+
const [rows] = await this.pool.query(`
|
|
1483
|
+
SELECT
|
|
1484
|
+
r.ROUTINE_NAME AS procedure_name,
|
|
1485
|
+
CASE
|
|
1486
|
+
WHEN r.ROUTINE_TYPE = 'PROCEDURE' THEN 'procedure'
|
|
1487
|
+
ELSE 'function'
|
|
1488
|
+
END AS procedure_type,
|
|
1489
|
+
LOWER(r.ROUTINE_TYPE) AS routine_type,
|
|
1490
|
+
r.ROUTINE_DEFINITION,
|
|
1491
|
+
r.DTD_IDENTIFIER AS return_type,
|
|
1492
|
+
(
|
|
1493
|
+
SELECT GROUP_CONCAT(
|
|
1494
|
+
CONCAT(p.PARAMETER_NAME, ' ', p.PARAMETER_MODE, ' ', p.DATA_TYPE)
|
|
1495
|
+
ORDER BY p.ORDINAL_POSITION
|
|
1496
|
+
SEPARATOR ', '
|
|
1497
|
+
)
|
|
1498
|
+
FROM INFORMATION_SCHEMA.PARAMETERS p
|
|
1499
|
+
WHERE p.SPECIFIC_SCHEMA = r.ROUTINE_SCHEMA
|
|
1500
|
+
AND p.SPECIFIC_NAME = r.ROUTINE_NAME
|
|
1501
|
+
AND p.PARAMETER_NAME IS NOT NULL
|
|
1502
|
+
) AS parameter_list
|
|
1503
|
+
FROM INFORMATION_SCHEMA.ROUTINES r
|
|
1504
|
+
${schemaClause}
|
|
1505
|
+
AND r.ROUTINE_NAME = ?
|
|
1506
|
+
`, queryParams);
|
|
1507
|
+
if (rows.length === 0) {
|
|
1508
|
+
const schemaName = schema || "current schema";
|
|
1509
|
+
throw new Error(`Stored procedure '${procedureName}' not found in ${schemaName}`);
|
|
1510
|
+
}
|
|
1511
|
+
const procedure = rows[0];
|
|
1512
|
+
let definition = procedure.ROUTINE_DEFINITION;
|
|
1513
|
+
try {
|
|
1514
|
+
const schemaValue = schema || await this.getCurrentSchema();
|
|
1515
|
+
if (procedure.procedure_type === "procedure") {
|
|
1516
|
+
try {
|
|
1517
|
+
const [defRows] = await this.pool.query(`
|
|
1518
|
+
SHOW CREATE PROCEDURE ${schemaValue}.${procedureName}
|
|
1519
|
+
`);
|
|
1520
|
+
if (defRows && defRows.length > 0) {
|
|
1521
|
+
definition = defRows[0]["Create Procedure"];
|
|
1522
|
+
}
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
console.error(`Error getting procedure definition with SHOW CREATE: ${err}`);
|
|
1525
|
+
}
|
|
1526
|
+
} else {
|
|
1527
|
+
try {
|
|
1528
|
+
const [defRows] = await this.pool.query(`
|
|
1529
|
+
SHOW CREATE FUNCTION ${schemaValue}.${procedureName}
|
|
1530
|
+
`);
|
|
1531
|
+
if (defRows && defRows.length > 0) {
|
|
1532
|
+
definition = defRows[0]["Create Function"];
|
|
1533
|
+
}
|
|
1534
|
+
} catch (innerErr) {
|
|
1535
|
+
console.error(`Error getting function definition with SHOW CREATE: ${innerErr}`);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
if (!definition) {
|
|
1539
|
+
const [bodyRows] = await this.pool.query(`
|
|
1540
|
+
SELECT ROUTINE_DEFINITION, ROUTINE_BODY
|
|
1541
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
1542
|
+
WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ?
|
|
1543
|
+
`, [schemaValue, procedureName]);
|
|
1544
|
+
if (bodyRows && bodyRows.length > 0) {
|
|
1545
|
+
if (bodyRows[0].ROUTINE_DEFINITION) {
|
|
1546
|
+
definition = bodyRows[0].ROUTINE_DEFINITION;
|
|
1547
|
+
} else if (bodyRows[0].ROUTINE_BODY) {
|
|
1548
|
+
definition = bodyRows[0].ROUTINE_BODY;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
console.error(`Error getting procedure/function details: ${error}`);
|
|
1554
|
+
}
|
|
1555
|
+
return {
|
|
1556
|
+
procedure_name: procedure.procedure_name,
|
|
1557
|
+
procedure_type: procedure.procedure_type,
|
|
1558
|
+
language: "sql",
|
|
1559
|
+
// MariaDB procedures are generally in SQL
|
|
1560
|
+
parameter_list: procedure.parameter_list || "",
|
|
1561
|
+
return_type: procedure.routine_type === "function" ? procedure.return_type : void 0,
|
|
1562
|
+
definition: definition || void 0
|
|
1563
|
+
};
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
console.error("Error getting stored procedure detail:", error);
|
|
1566
|
+
throw error;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
// Helper method to get current schema (database) name
|
|
1570
|
+
async getCurrentSchema() {
|
|
1571
|
+
const [rows] = await this.pool.query("SELECT DATABASE() AS DB");
|
|
1572
|
+
return rows[0].DB;
|
|
1573
|
+
}
|
|
1574
|
+
async executeQuery(query) {
|
|
1575
|
+
if (!this.pool) {
|
|
1576
|
+
throw new Error("Not connected to database");
|
|
1577
|
+
}
|
|
1578
|
+
const safetyCheck = this.validateQuery(query);
|
|
1579
|
+
if (!safetyCheck.isValid) {
|
|
1580
|
+
throw new Error(safetyCheck.message || "Query validation failed");
|
|
1581
|
+
}
|
|
1582
|
+
try {
|
|
1583
|
+
const [rows, fields] = await this.pool.query(query);
|
|
1584
|
+
return { rows, fields };
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
console.error("Error executing query:", error);
|
|
1587
|
+
throw error;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
validateQuery(query) {
|
|
1591
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
1592
|
+
if (!normalizedQuery.startsWith("select")) {
|
|
1593
|
+
return {
|
|
1594
|
+
isValid: false,
|
|
1595
|
+
message: "Only SELECT queries are allowed for security reasons."
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
return { isValid: true };
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
var mariadbConnector = new MariaDBConnector();
|
|
1602
|
+
ConnectorRegistry.register(mariadbConnector);
|
|
1603
|
+
|
|
1250
1604
|
// src/server.ts
|
|
1251
1605
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1252
1606
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
@@ -1437,6 +1791,17 @@ function resolvePort() {
|
|
|
1437
1791
|
}
|
|
1438
1792
|
return { port: 8080, source: "default" };
|
|
1439
1793
|
}
|
|
1794
|
+
function redactDSN(dsn) {
|
|
1795
|
+
try {
|
|
1796
|
+
const url = new URL(dsn);
|
|
1797
|
+
if (url.password) {
|
|
1798
|
+
url.password = "*******";
|
|
1799
|
+
}
|
|
1800
|
+
return url.toString();
|
|
1801
|
+
} catch (error) {
|
|
1802
|
+
return dsn.replace(/\/\/([^:]+):([^@]+)@/, "//$1:***@");
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1440
1805
|
|
|
1441
1806
|
// src/config/demo-loader.ts
|
|
1442
1807
|
import fs2 from "fs";
|
|
@@ -2336,7 +2701,7 @@ See documentation for more details on configuring database connections.
|
|
|
2336
2701
|
registerTools(server);
|
|
2337
2702
|
registerPrompts(server);
|
|
2338
2703
|
const connectorManager = new ConnectorManager();
|
|
2339
|
-
console.error(`Connecting with DSN: ${dsnData.dsn}`);
|
|
2704
|
+
console.error(`Connecting with DSN: ${redactDSN(dsnData.dsn)}`);
|
|
2340
2705
|
console.error(`DSN source: ${dsnData.source}`);
|
|
2341
2706
|
if (dsnData.isDemo) {
|
|
2342
2707
|
console.error("Running in demo mode with sample employee database");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytebase/dbhub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Universal Database MCP Server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
"author": "",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@azure/identity": "^4.8.0",
|
|
19
20
|
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
20
21
|
"better-sqlite3": "^11.9.0",
|
|
21
22
|
"dotenv": "^16.4.7",
|
|
22
23
|
"express": "^4.18.2",
|
|
23
24
|
"mssql": "^11.0.1",
|
|
25
|
+
"mariadb": "^3.4.0",
|
|
24
26
|
"mysql2": "^3.13.0",
|
|
25
27
|
"pg": "^8.13.3",
|
|
26
28
|
"zod": "^3.24.2"
|