@zz1996/dbhub-dameng 0.1.0 → 0.1.2

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
@@ -5,6 +5,10 @@
5
5
  > This package is a local Dameng/DM8 connector fork of Bytebase DBHub. Use
6
6
  > `npx -y @zz1996/dbhub-dameng --transport stdio --config ./dbhub.dameng.toml`
7
7
  > for Dameng MCP usage.
8
+ >
9
+ > Dameng DSN format: `dameng://user:password@host:5236/schema`. Prefer a
10
+ > least-privilege read-only Dameng account for production or shared databases,
11
+ > even when the `execute_sql` tool is configured with `readonly = true`.
8
12
 
9
13
  <p align="center">
10
14
  <a href="https://dbhub.ai/" target="_blank">
@@ -30,6 +34,7 @@
30
34
  | | | | | |
31
35
  | Copilot CLI +--->+ +--->+ MariaDB |
32
36
  | | | | | |
37
+ | +--->+ +--->+ Dameng / DM8 |
33
38
  | | | | | |
34
39
  +------------------+ +--------------+ +------------------+
35
40
  MCP Clients MCP Server Databases
@@ -38,14 +43,14 @@
38
43
  DBHub is a zero-dependency, token efficient MCP server implementing the Model Context Protocol (MCP) server interface. This lightweight gateway allows MCP-compatible clients to connect to and explore different databases:
39
44
 
40
45
  - **Local Development First**: Zero dependency, token efficient with just two MCP tools to maximize context window
41
- - **Multi-Database**: PostgreSQL, MySQL, MariaDB, SQL Server, and SQLite through a single interface
46
+ - **Multi-Database**: PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, and Dameng/DM8 through a single interface
42
47
  - **Multi-Connection**: Connect to multiple databases simultaneously with TOML configuration
43
48
  - **Guardrails**: Read-only mode, row limiting, and query timeout to prevent runaway operations
44
49
  - **Secure Access**: SSH tunneling and SSL/TLS encryption
45
50
 
46
51
  ## Supported Databases
47
52
 
48
- PostgreSQL, MySQL, SQL Server, MariaDB, and SQLite.
53
+ PostgreSQL, MySQL, SQL Server, MariaDB, SQLite, and Dameng/DM8.
49
54
 
50
55
  ## MCP Tools
51
56
 
@@ -85,6 +90,41 @@ docker run --rm --init \
85
90
  npx @bytebase/dbhub@latest --transport http --port 8080 --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
86
91
  ```
87
92
 
93
+ **Dameng / DM8 fork:**
94
+
95
+ ```bash
96
+ npx -y @zz1996/dbhub-dameng --transport stdio --config ./dbhub.dameng.toml
97
+ ```
98
+
99
+ Minimal Dameng TOML configuration:
100
+
101
+ ```toml
102
+ [[sources]]
103
+ id = "dm8_readonly"
104
+ description = "Dameng DM8 database for schema discovery and read-only SQL queries"
105
+ dsn = "dameng://${DM8_USER}:${DM8_PASSWORD}@${DM8_HOST}:5236/${DM8_SCHEMA}"
106
+ query_timeout = 30
107
+ lazy = true
108
+
109
+ [[tools]]
110
+ name = "search_objects"
111
+ source = "dm8_readonly"
112
+
113
+ [[tools]]
114
+ name = "execute_sql"
115
+ source = "dm8_readonly"
116
+ readonly = true
117
+ max_rows = 100
118
+ ```
119
+
120
+ To verify the local Dameng connector against a real DM8 instance without
121
+ committing credentials, set `DAMENG_DSN` or point to a local TOML file:
122
+
123
+ ```bash
124
+ DAMENG_DSN="dameng://user:password@host:5236/schema" pnpm verify:dameng
125
+ pnpm verify:dameng -- --config ./dbhub.dameng.toml --source dm8_readonly
126
+ ```
127
+
88
128
  **Demo Mode:**
89
129
 
90
130
  ```bash
@@ -5,6 +5,7 @@
5
5
  id = "dm8_dev"
6
6
  description = "Dameng DM8 development database. Read-only access for agent schema discovery and SQL queries."
7
7
  dsn = "dameng://${DM8_USER}:${DM8_PASSWORD}@${DM8_HOST}:5236/${DM8_SCHEMA}"
8
+ connection_timeout = 5
8
9
  query_timeout = 30
9
10
  lazy = true
10
11
 
@@ -65,6 +65,7 @@ var DamengConnector = class _DamengConnector {
65
65
  this.pool = null;
66
66
  this.sourceId = "default";
67
67
  this.defaultSchema = null;
68
+ this.poolAlias = null;
68
69
  }
69
70
  getId() {
70
71
  return this.sourceId;
@@ -73,13 +74,20 @@ var DamengConnector = class _DamengConnector {
73
74
  return new _DamengConnector();
74
75
  }
75
76
  async connect(dsn, initScript, config) {
77
+ let createdPool = null;
76
78
  try {
77
79
  const connectionConfig = await this.dsnParser.parse(dsn, config);
80
+ connectionConfig.poolAlias = this.buildPoolAlias();
78
81
  this.defaultSchema = connectionConfig.schema ?? null;
82
+ this.poolAlias = connectionConfig.poolAlias;
83
+ this.queryTimeoutMs = void 0;
79
84
  if (config?.queryTimeoutSeconds !== void 0) {
80
85
  this.queryTimeoutMs = config.queryTimeoutSeconds * 1e3;
81
86
  }
82
- this.pool = await dmdb.createPool(connectionConfig);
87
+ await this.closeRegisteredPool(connectionConfig.poolAlias);
88
+ await this.validateDirectConnection(connectionConfig);
89
+ createdPool = await dmdb.createPool(connectionConfig);
90
+ this.pool = createdPool;
83
91
  await this.withConnection(async (conn) => {
84
92
  await conn.execute("SELECT 1 AS OK", [], this.executeOptions());
85
93
  if (!this.defaultSchema) {
@@ -97,15 +105,26 @@ var DamengConnector = class _DamengConnector {
97
105
  }
98
106
  });
99
107
  } catch (error) {
108
+ if (createdPool) {
109
+ await this.closePoolQuietly(createdPool);
110
+ } else if (this.poolAlias) {
111
+ await this.closeRegisteredPool(this.poolAlias);
112
+ }
113
+ this.pool = null;
114
+ this.poolAlias = null;
100
115
  console.error("Failed to connect to Dameng database:", error);
101
116
  throw error;
102
117
  }
103
118
  }
104
119
  async disconnect() {
105
120
  if (this.pool) {
106
- await this.pool.close(0);
121
+ await this.closePoolQuietly(this.pool);
107
122
  this.pool = null;
108
123
  }
124
+ if (this.poolAlias) {
125
+ await this.closeRegisteredPool(this.poolAlias);
126
+ this.poolAlias = null;
127
+ }
109
128
  }
110
129
  async getSchemas() {
111
130
  const rows = await this.queryRows(`
@@ -366,6 +385,38 @@ var DamengConnector = class _DamengConnector {
366
385
  ...extra
367
386
  };
368
387
  }
388
+ async validateDirectConnection(config) {
389
+ let conn = null;
390
+ try {
391
+ conn = await dmdb.getConnection(config.connectString);
392
+ await conn.execute("SELECT 1 AS OK", [], this.executeOptions());
393
+ } finally {
394
+ if (conn) {
395
+ await conn.close();
396
+ }
397
+ }
398
+ }
399
+ buildPoolAlias() {
400
+ const safeSourceId = this.sourceId.replace(/[^a-zA-Z0-9_-]/g, "_") || "default";
401
+ return `dbhub_dameng_${safeSourceId}`;
402
+ }
403
+ async closeRegisteredPool(poolAlias) {
404
+ if (!dmdb.pools?.has?.(poolAlias)) {
405
+ return;
406
+ }
407
+ const pool = dmdb.pools.get(poolAlias);
408
+ await this.closePoolQuietly(pool);
409
+ dmdb.pools?.delete?.(poolAlias);
410
+ }
411
+ async closePoolQuietly(pool) {
412
+ try {
413
+ await pool.close(0);
414
+ } catch {
415
+ if (pool.poolAlias) {
416
+ dmdb.pools?.delete?.(pool.poolAlias);
417
+ }
418
+ }
419
+ }
369
420
  async resolveSchema(schema) {
370
421
  const resolved = schema || this.defaultSchema || await this.getDefaultSchema();
371
422
  if (!resolved) {
package/dist/index.js CHANGED
@@ -1833,7 +1833,7 @@ var connectorModules = [
1833
1833
  { load: () => import("./sqlite-IOUAYHGE.js"), name: "SQLite", driver: "node:sqlite" },
1834
1834
  { load: () => import("./mysql-A43SL7UM.js"), name: "MySQL", driver: "mysql2" },
1835
1835
  { load: () => import("./mariadb-7F72IRB4.js"), name: "MariaDB", driver: "mariadb" },
1836
- { load: () => import("./dameng-DC7OP4VV.js"), name: "Dameng", driver: "dmdb" }
1836
+ { load: () => import("./dameng-UJELBZ5R.js"), name: "Dameng", driver: "dmdb" }
1837
1837
  ];
1838
1838
  loadConnectors(connectorModules).then(() => main()).catch((error) => {
1839
1839
  console.error("Fatal error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zz1996/dbhub-dameng",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "mcpName": "io.github.zuozh11/dbhub-dameng",
5
5
  "description": "Local fork of DBHub with Dameng/DM8 database connector support",
6
6
  "repository": {
@@ -41,6 +41,7 @@
41
41
  "test:watch": "vitest",
42
42
  "test:integration": "vitest run --project integration",
43
43
  "test:build": "node scripts/smoke-test-build.mjs",
44
+ "verify:dameng": "tsx scripts/verify-dameng.mjs",
44
45
  "prepublishOnly": "npm run build:backend"
45
46
  },
46
47
  "engines": {