@bytebase/dbhub 0.0.6 → 0.0.8

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,11 +14,11 @@ 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
23
  | | | +--->+ Other Databases |
24
24
  | | | | | |
@@ -26,18 +26,35 @@ DBHub is a universal database gateway implementing the Model Context Protocol (M
26
26
  MCP Clients MCP Server Databases
27
27
  ```
28
28
 
29
- ## Features
29
+ ## Supported Matrix
30
30
 
31
- - Browse available tables in the database
32
- - View schema information for tables
33
- - Run read-only SQL queries against the database
34
- - Safety checks to prevent dangerous queries
31
+ ### Database Resources
32
+
33
+ | Resource | URI Format | PostgreSQL | MySQL | SQL Server | SQLite |
34
+ |------------------------|:----------:|:----------:|:-----:|:----------:|:------:|
35
+ | Tables | `db://tables` | ✅ | ✅ | ✅ | ✅ |
36
+ | Schema | `db://schema/{tableName}` | ✅ | ✅ | ✅ | ✅ |
37
+
38
+ ### Database Tools
39
+
40
+ | Tool | Command Name | PostgreSQL | MySQL | SQL Server | SQLite |
41
+ |------------------------|:------------------:|:----------:|:-----:|:----------:|:------:|
42
+ | Execute Query | `run_query` | ✅ | ✅ | ✅ | ✅ |
43
+ | List Connectors | `list_connectors` | ✅ | ✅ | ✅ | ✅ |
44
+
45
+ ### Prompt Capabilities
46
+
47
+ | Prompt | Command Name | PostgreSQL | MySQL | SQL Server | SQLite |
48
+ |------------------------|:----------------:|:----------:|:-----:|:----------:|:------:|
49
+ | Generate SQL | `generate_sql` | ✅ | ✅ | ✅ | ✅ |
50
+ | Explain DB Elements | `explain_db` | ✅ | ✅ | ✅ | ✅ |
35
51
 
36
52
  ## Installation
37
53
 
38
54
  ### Docker
39
55
 
40
56
  ```bash
57
+ # PostgreSQL example
41
58
  docker run --rm --init \
42
59
  --name dbhub \
43
60
  --publish 8080:8080 \
@@ -47,17 +64,96 @@ docker run --rm --init \
47
64
  --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
48
65
  ```
49
66
 
67
+ ```bash
68
+ # Demo mode with sample employee database
69
+ docker run --rm --init \
70
+ --name dbhub \
71
+ --publish 8080:8080 \
72
+ bytebase/dbhub \
73
+ --transport sse \
74
+ --port 8080 \
75
+ --demo
76
+ ```
77
+
50
78
  ### NPM
51
79
 
52
80
  ```bash
81
+ # PostgreSQL example
53
82
  npx @bytebase/dbhub --transport sse --port 8080 --dsn "postgres://user:password@localhost:5432/dbname"
54
83
  ```
55
84
 
85
+ ```bash
86
+ # Demo mode with sample employee database
87
+ npx @bytebase/dbhub --transport sse --port 8080 --demo
88
+ ```
89
+
90
+ ### Claude Desktop
91
+
92
+ ![claude-desktop](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/claude-desktop.webp)
93
+
94
+ - Claude Desktop only supports `stdio` transport https://github.com/orgs/modelcontextprotocol/discussions/16
95
+
96
+ ```json
97
+ // claude_desktop_config.json
98
+ {
99
+ "mcpServers": {
100
+ "dbhub-postgres-docker": {
101
+ "command": "docker",
102
+ "args": [
103
+ "run",
104
+ "-i",
105
+ "--rm",
106
+ "bytebase/dbhub",
107
+ "--transport",
108
+ "stdio",
109
+ "--dsn",
110
+ // Use host.docker.internal as the host if connecting to the local db
111
+ "postgres://user:password@host.docker.internal:5432/dbname?sslmode=disable"
112
+ ]
113
+ },
114
+ "dbhub-postgres-npx": {
115
+ "command": "npx",
116
+ "args": [
117
+ "-y",
118
+ "@bytebase/dbhub",
119
+ "--transport",
120
+ "stdio",
121
+ "--dsn",
122
+ "postgres://user:password@localhost:5432/dbname?sslmode=disable"
123
+ ]
124
+ },
125
+ "dbhub-demo": {
126
+ "command": "npx",
127
+ "args": [
128
+ "-y",
129
+ "@bytebase/dbhub",
130
+ "--transport",
131
+ "stdio",
132
+ "--demo"
133
+ ]
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### Cursor
140
+
141
+ ![cursor](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/cursor.webp)
142
+
143
+ - Cursor supports both `stdio` and `sse`.
144
+ - Follow [Cursor MCP guide](https://docs.cursor.com/context/model-context-protocol) and make sure to use [Agent](https://docs.cursor.com/chat/agent) mode.
145
+
56
146
  ## Usage
57
147
 
58
148
  ### Configure your database connection
59
149
 
60
- Database Source Name (DSN) is required to connect to your database. You can provide this in several ways:
150
+ You can use DBHub in demo mode with a sample employee database for testing:
151
+
152
+ ```bash
153
+ pnpm dev --demo
154
+ ```
155
+
156
+ For real databases, a Database Source Name (DSN) is required. You can provide this in several ways:
61
157
 
62
158
  - **Command line argument** (highest priority):
63
159
 
@@ -79,6 +175,15 @@ Database Source Name (DSN) is required to connect to your database. You can prov
79
175
  DSN=postgres://user:password@localhost:5432/dbname?sslmode=disable
80
176
  ```
81
177
 
178
+ DBHub supports the following database connection string formats:
179
+
180
+ | Database | DSN Format | Example |
181
+ |------------|-----------------------------------------------------------|--------------------------------------------------------|
182
+ | PostgreSQL | `postgres://[user]:[password]@[host]:[port]/[database]` | `postgres://user:password@localhost:5432/dbname?sslmode=disable` |
183
+ | SQLite | `sqlite:///[path/to/file]` or `sqlite::memory:` | `sqlite:///path/to/database.db` or `sqlite::memory:` |
184
+ | SQL Server | `sqlserver://[user]:[password]@[host]:[port]/[database]` | `sqlserver://user:password@localhost:1433/dbname` |
185
+ | MySQL | `mysql://[user]:[password]@[host]:[port]/[database]` | `mysql://user:password@localhost:3306/dbname` |
186
+
82
187
  ### Transport
83
188
 
84
189
  - **stdio** (default) - for direct integration with tools like Claude Desktop:
@@ -96,60 +201,11 @@ Database Source Name (DSN) is required to connect to your database. You can prov
96
201
 
97
202
  | Option | Description | Default |
98
203
  | :-------- | :-------------------------------------------------------------- | :---------------------------------- |
99
- | dsn | Database connection string | Required if not set via environment |
204
+ | demo | Run in demo mode with sample employee database | `false` |
205
+ | dsn | Database connection string | Required if not in demo mode |
100
206
  | transport | Transport mode: `stdio` or `sse` | `stdio` |
101
207
  | port | HTTP server port (only applicable when using `--transport=sse`) | `8080` |
102
208
 
103
- ### Claude Desktop
104
-
105
- ![claude-desktop](https://raw.githubusercontent.com/bytebase/dbhub/main/assets/claude-desktop.webp)
106
-
107
- - Claude Desktop only supports `stdio` transport https://github.com/orgs/modelcontextprotocol/discussions/16
108
-
109
- #### Docker
110
-
111
- ```json
112
- // claude_desktop_config.json
113
- {
114
- "mcpServers": {
115
- "dbhub": {
116
- "command": "docker",
117
- "args": [
118
- "run",
119
- "-i",
120
- "--rm",
121
- "bytebase/dbhub",
122
- "--transport",
123
- "stdio",
124
- "--dsn",
125
- // Use host.docker.internal as the host if connecting to the local db
126
- "postgres://user:password@host.docker.internal:5432/dbname?sslmode=disable"
127
- ]
128
- }
129
- }
130
- }
131
- ```
132
-
133
- #### NPX
134
-
135
- ```json
136
- // claude_desktop_config.json
137
- {
138
- "mcpServers": {
139
- "dbhub": {
140
- "command": "npx",
141
- "args": [
142
- "-y",
143
- "@bytebase/dbhub",
144
- "--transport",
145
- "stdio",
146
- "--dsn",
147
- "postgres://user:password@localhost:5432/dbname?sslmode=disable"
148
- ]
149
- }
150
- }
151
- }
152
- ```
153
209
 
154
210
  ## Development
155
211
 
@@ -176,6 +232,7 @@ Database Source Name (DSN) is required to connect to your database. You can prov
176
232
  #### stdio
177
233
 
178
234
  ```bash
235
+ # PostgreSQL example
179
236
  TRANSPORT=stdio DSN="postgres://user:password@localhost:5432/dbname?sslmode=disable" npx @modelcontextprotocol/inspector node /path/to/dbhub/dist/index.js
180
237
  ```
181
238
 
@@ -191,4 +248,4 @@ npx @modelcontextprotocol/inspector
191
248
 
192
249
  Connect to the DBHub server `/sse` endpoint
193
250
 
194
- ![mcp-inspector](https://raw.githubusercontent.com/bytebase/dbhub/main/assets/mcp-inspector.webp)
251
+ ![mcp-inspector](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/mcp-inspector.webp)
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Demo data loader for SQLite in-memory database
3
+ *
4
+ * This module loads the sample employee database into the SQLite in-memory database
5
+ * when the --demo flag is specified.
6
+ */
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ // Create __dirname equivalent for ES modules
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ // Path to sample data files
14
+ const DEMO_DATA_DIR = path.join(__dirname, '..', '..', 'resources', 'employee-sqlite');
15
+ /**
16
+ * Load SQL file contents
17
+ */
18
+ export function loadSqlFile(fileName) {
19
+ const filePath = path.join(DEMO_DATA_DIR, fileName);
20
+ return fs.readFileSync(filePath, 'utf8');
21
+ }
22
+ /**
23
+ * Get SQLite DSN for in-memory database
24
+ */
25
+ export function getInMemorySqliteDSN() {
26
+ return 'sqlite::memory:';
27
+ }
28
+ /**
29
+ * Load SQL files sequentially
30
+ */
31
+ export function getSqliteInMemorySetupSql() {
32
+ // First, load the schema
33
+ let sql = loadSqlFile('employee.sql');
34
+ // Replace .read directives with the actual file contents
35
+ // This is necessary because in-memory SQLite can't use .read
36
+ const readRegex = /\.read\s+([a-zA-Z0-9_]+\.sql)/g;
37
+ let match;
38
+ while ((match = readRegex.exec(sql)) !== null) {
39
+ const includePath = match[1];
40
+ const includeContent = loadSqlFile(includePath);
41
+ // Replace the .read line with the file contents
42
+ sql = sql.replace(match[0], includeContent);
43
+ }
44
+ return sql;
45
+ }
@@ -62,6 +62,14 @@ export function loadEnvFiles() {
62
62
  }
63
63
  return null;
64
64
  }
65
+ /**
66
+ * Check if demo mode is enabled from command line args
67
+ * Returns true if --demo flag is provided
68
+ */
69
+ export function isDemoMode() {
70
+ const args = parseCommandLineArgs();
71
+ return args.demo === 'true';
72
+ }
65
73
  /**
66
74
  * Resolve DSN from command line args, environment variables, or .env files
67
75
  * Returns the DSN and its source, or null if not found
@@ -69,7 +77,16 @@ export function loadEnvFiles() {
69
77
  export function resolveDSN() {
70
78
  // Get command line arguments
71
79
  const args = parseCommandLineArgs();
72
- // 1. Check command line arguments first (highest priority)
80
+ // Check for demo mode first (highest priority)
81
+ if (isDemoMode()) {
82
+ // Will use in-memory SQLite with demo data
83
+ return {
84
+ dsn: 'sqlite::memory:',
85
+ source: 'demo mode',
86
+ isDemo: true
87
+ };
88
+ }
89
+ // 1. Check command line arguments
73
90
  if (args.dsn) {
74
91
  return { dsn: args.dsn, source: 'command line argument' };
75
92
  }
@@ -15,7 +15,7 @@ export class ConnectorManager {
15
15
  /**
16
16
  * Initialize and connect to the database using a DSN
17
17
  */
18
- async connectWithDSN(dsn) {
18
+ async connectWithDSN(dsn, initScript) {
19
19
  // First try to find a connector that can handle this DSN
20
20
  let connector = ConnectorRegistry.getConnectorForDSN(dsn);
21
21
  if (!connector) {
@@ -23,7 +23,7 @@ export class ConnectorManager {
23
23
  }
24
24
  this.activeConnector = connector;
25
25
  // Connect to the database
26
- await this.activeConnector.connect(dsn);
26
+ await this.activeConnector.connect(dsn, initScript);
27
27
  this.connected = true;
28
28
  }
29
29
  /**
@@ -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,132 @@ 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
- async connect(dsn) {
70
+ async connect(dsn, initScript) {
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
+ // If an initialization script is provided, run it
83
+ if (initScript) {
84
+ this.db.exec(initScript, (err) => {
85
+ if (err) {
86
+ console.error("Failed to initialize database with script:", err);
87
+ reject(err);
88
+ }
89
+ else {
90
+ console.error("Successfully initialized database with script");
91
+ resolve();
92
+ }
93
+ });
94
+ }
95
+ else {
96
+ resolve();
97
+ }
98
+ }
99
+ });
84
100
  });
85
- */
86
- throw new Error('SQLite connector not implemented yet');
87
101
  }
88
102
  async disconnect() {
89
103
  // Close the SQLite connection
90
- /*
91
104
  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
- }
105
+ return new Promise((resolve, reject) => {
106
+ this.db.close((err) => {
107
+ if (err) {
108
+ reject(err);
109
+ }
110
+ else {
111
+ this.db = null;
112
+ resolve();
113
+ }
114
+ });
100
115
  });
101
- });
102
116
  }
103
- */
104
- throw new Error('SQLite connector not implemented yet');
117
+ return Promise.resolve();
105
118
  }
106
119
  async getTables() {
107
- // Get all tables from SQLite
108
- /*
120
+ if (!this.db) {
121
+ throw new Error("Not connected to SQLite database");
122
+ }
109
123
  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
- );
124
+ this.db.all(`SELECT name FROM sqlite_master
125
+ WHERE type='table' AND name NOT LIKE 'sqlite_%'
126
+ ORDER BY name`, (err, rows) => {
127
+ if (err) {
128
+ reject(err);
129
+ }
130
+ else {
131
+ resolve(rows.map(row => row.name));
132
+ }
133
+ });
122
134
  });
123
- */
124
- throw new Error('SQLite connector not implemented yet');
125
135
  }
126
136
  async tableExists(tableName) {
127
- // Check if a table exists in SQLite
128
- /*
137
+ if (!this.db) {
138
+ throw new Error("Not connected to SQLite database");
139
+ }
129
140
  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
- );
141
+ this.db.get(`SELECT name FROM sqlite_master
142
+ WHERE type='table' AND name = ?`, [tableName], (err, row) => {
143
+ if (err) {
144
+ reject(err);
145
+ }
146
+ else {
147
+ resolve(!!row);
148
+ }
149
+ });
142
150
  });
143
- */
144
- throw new Error('SQLite connector not implemented yet');
145
151
  }
146
152
  async getTableSchema(tableName) {
147
- // Get schema for a specific table
148
- /*
153
+ if (!this.db) {
154
+ throw new Error("Not connected to SQLite database");
155
+ }
149
156
  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
- );
157
+ this.db.all(`PRAGMA table_info(${tableName})`, (err, rows) => {
158
+ if (err) {
159
+ reject(err);
160
+ }
161
+ else {
162
+ // Convert SQLite schema format to our standard TableColumn format
163
+ const columns = rows.map(row => ({
164
+ column_name: row.name,
165
+ data_type: row.type,
166
+ is_nullable: row.notnull === 0 ? 'YES' : 'NO', // In SQLite, 0 means nullable
167
+ column_default: row.dflt_value
168
+ }));
169
+ resolve(columns);
170
+ }
171
+ });
167
172
  });
168
- */
169
- throw new Error('SQLite connector not implemented yet');
170
173
  }
171
174
  async executeQuery(query) {
172
- // Execute a query against SQLite
175
+ if (!this.db) {
176
+ throw new Error("Not connected to SQLite database");
177
+ }
178
+ // Validate query for safety
173
179
  const safetyCheck = this.validateQuery(query);
174
180
  if (!safetyCheck.isValid) {
175
181
  throw new Error(safetyCheck.message || "Query validation failed");
176
182
  }
177
- /*
178
183
  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
- });
184
+ this.db.all(query, (err, rows) => {
185
+ if (err) {
186
+ reject(err);
187
+ }
188
+ else {
189
+ resolve({ rows });
190
+ }
191
+ });
186
192
  });
187
- */
188
- throw new Error('SQLite connector not implemented yet');
189
193
  }
190
194
  validateQuery(query) {
191
195
  // Basic check to prevent non-SELECT queries
@@ -199,6 +203,6 @@ export class SQLiteConnector {
199
203
  return { isValid: true };
200
204
  }
201
205
  }
202
- // To enable this connector, uncomment the following lines and install sqlite3:
203
- // const sqliteConnector = new SQLiteConnector();
204
- // ConnectorRegistry.register(sqliteConnector);
206
+ // Register the SQLite connector
207
+ const sqliteConnector = new SQLiteConnector();
208
+ 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/dist/server.js CHANGED
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'url';
8
8
  import { ConnectorManager } from './connectors/manager.js';
9
9
  import { ConnectorRegistry } from './connectors/interface.js';
10
10
  import { resolveDSN, resolveTransport, resolvePort } from './config/env.js';
11
+ import { getSqliteInMemorySetupSql } from './config/demo-loader.js';
11
12
  import { registerResources } from './resources/index.js';
12
13
  import { registerTools } from './tools/index.js';
13
14
  import { registerPrompts } from './prompts/index.js';
@@ -23,7 +24,8 @@ export const SERVER_VERSION = packageJson.version;
23
24
  /**
24
25
  * Generate ASCII art banner with version information
25
26
  */
26
- export function generateBanner(version) {
27
+ export function generateBanner(version, isDemo = false) {
28
+ const demoText = isDemo ? " [DEMO MODE]" : "";
27
29
  return `
28
30
  _____ ____ _ _ _
29
31
  | __ \\| _ \\| | | | | |
@@ -32,7 +34,7 @@ export function generateBanner(version) {
32
34
  | |__| | |_) | | | | |_| | |_) |
33
35
  |_____/|____/|_| |_|\\__,_|_.__/
34
36
 
35
- v${version} - Universal Database MCP Server
37
+ v${version}${demoText} - Universal Database MCP Server
36
38
  `;
37
39
  }
38
40
  /**
@@ -51,9 +53,10 @@ export async function main() {
51
53
  ERROR: Database connection string (DSN) is required.
52
54
  Please provide the DSN in one of these ways (in order of priority):
53
55
 
54
- 1. Command line argument: --dsn="your-connection-string"
55
- 2. Environment variable: export DSN="your-connection-string"
56
- 3. .env file: DSN=your-connection-string
56
+ 1. Use demo mode: --demo (uses in-memory SQLite with sample employee database)
57
+ 2. Command line argument: --dsn="your-connection-string"
58
+ 3. Environment variable: export DSN="your-connection-string"
59
+ 4. .env file: DSN=your-connection-string
57
60
 
58
61
  Example formats:
59
62
  ${sampleFormats}
@@ -75,13 +78,21 @@ See documentation for more details on configuring database connections.
75
78
  const connectorManager = new ConnectorManager();
76
79
  console.error(`Connecting with DSN: ${dsnData.dsn}`);
77
80
  console.error(`DSN source: ${dsnData.source}`);
78
- await connectorManager.connectWithDSN(dsnData.dsn);
81
+ // If in demo mode, load the employee database
82
+ if (dsnData.isDemo) {
83
+ console.error('Running in demo mode with sample employee database');
84
+ const initScript = getSqliteInMemorySetupSql();
85
+ await connectorManager.connectWithDSN(dsnData.dsn, initScript);
86
+ }
87
+ else {
88
+ await connectorManager.connectWithDSN(dsnData.dsn);
89
+ }
79
90
  // Resolve transport type
80
91
  const transportData = resolveTransport();
81
92
  console.error(`Using transport: ${transportData.type}`);
82
93
  console.error(`Transport source: ${transportData.source}`);
83
94
  // Print ASCII art banner with version and slogan
84
- console.error(generateBanner(SERVER_VERSION));
95
+ console.error(generateBanner(SERVER_VERSION, dsnData.isDemo));
85
96
  // Set up transport based on type
86
97
  if (transportData.type === 'sse') {
87
98
  // Set up Express server for SSE transport
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebase/dbhub",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
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": {