@bytebase/dbhub 0.5.0 → 0.6.1

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.
Files changed (3) hide show
  1. package/README.md +17 -17
  2. package/dist/index.js +61 -41
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -28,9 +28,9 @@ DBHub is a universal database gateway implementing the Model Context Protocol (M
28
28
  MCP Clients MCP Server Databases
29
29
  ```
30
30
 
31
- ## Demo SSE Endpoint
31
+ ## Demo HTTP Endpoint
32
32
 
33
- https://demo.dbhub.ai/sse connects a [sample employee database](https://github.com/bytebase/employee-sample-database). You can point Cursor or MCP Inspector to it to see it in action.
33
+ https://demo.dbhub.ai/message connects a [sample employee database](https://github.com/bytebase/employee-sample-database). You can point Cursor or MCP Inspector to it to see it in action.
34
34
 
35
35
  ![mcp-inspector](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/mcp-inspector.webp)
36
36
 
@@ -71,7 +71,7 @@ docker run --rm --init \
71
71
  --name dbhub \
72
72
  --publish 8080:8080 \
73
73
  bytebase/dbhub \
74
- --transport sse \
74
+ --transport http \
75
75
  --port 8080 \
76
76
  --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
77
77
  ```
@@ -82,7 +82,7 @@ docker run --rm --init \
82
82
  --name dbhub \
83
83
  --publish 8080:8080 \
84
84
  bytebase/dbhub \
85
- --transport sse \
85
+ --transport http \
86
86
  --port 8080 \
87
87
  --demo
88
88
  ```
@@ -93,7 +93,7 @@ docker run --rm --init \
93
93
  --name dbhub \
94
94
  --publish 8080:8080 \
95
95
  bytebase/dbhub \
96
- --transport sse \
96
+ --transport http \
97
97
  --port 8080 \
98
98
  --dsn "oracle://username:password@localhost:1521/service_name"
99
99
  ```
@@ -104,7 +104,7 @@ docker run --rm --init \
104
104
  --name dbhub \
105
105
  --publish 8080:8080 \
106
106
  bytebase/dbhub-oracle-thick \
107
- --transport sse \
107
+ --transport http \
108
108
  --port 8080 \
109
109
  --dsn "oracle://username:password@localhost:1521/service_name"
110
110
  ```
@@ -113,12 +113,12 @@ docker run --rm --init \
113
113
 
114
114
  ```bash
115
115
  # PostgreSQL example
116
- npx @bytebase/dbhub --transport sse --port 8080 --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
116
+ npx @bytebase/dbhub --transport http --port 8080 --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
117
117
  ```
118
118
 
119
119
  ```bash
120
120
  # Demo mode with sample employee database
121
- npx @bytebase/dbhub --transport sse --port 8080 --demo
121
+ npx @bytebase/dbhub --transport http --port 8080 --demo
122
122
  ```
123
123
 
124
124
  > Note: The demo mode includes a bundled SQLite sample "employee" database with tables for employees, departments, salaries, and more.
@@ -170,7 +170,7 @@ npx @bytebase/dbhub --transport sse --port 8080 --demo
170
170
 
171
171
  ![cursor](https://raw.githubusercontent.com/bytebase/dbhub/main/resources/images/cursor.webp)
172
172
 
173
- - Cursor supports both `stdio` and `sse`.
173
+ - Cursor supports both `stdio` and `http`.
174
174
  - 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.
175
175
 
176
176
  ## Usage
@@ -302,9 +302,9 @@ Extra query parameters:
302
302
  npx @bytebase/dbhub --transport stdio --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
303
303
  ```
304
304
 
305
- - **sse** - for browser and network clients:
305
+ - **http** - for browser and network clients:
306
306
  ```bash
307
- npx @bytebase/dbhub --transport sse --port 5678 --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
307
+ npx @bytebase/dbhub --transport http --port 5678 --dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
308
308
  ```
309
309
 
310
310
  ### Command line options
@@ -313,8 +313,8 @@ Extra query parameters:
313
313
  | --------- | --------------------------------------------------------------- | ---------------------------- |
314
314
  | demo | Run in demo mode with sample employee database | `false` |
315
315
  | dsn | Database connection string | Required if not in demo mode |
316
- | transport | Transport mode: `stdio` or `sse` | `stdio` |
317
- | port | HTTP server port (only applicable when using `--transport=sse`) | `8080` |
316
+ | transport | Transport mode: `stdio` or `http` | `stdio` |
317
+ | port | HTTP server port (only applicable when using `--transport=http`) | `8080` |
318
318
  | readonly | Restrict SQL execution to read-only operations | `false` |
319
319
 
320
320
  The demo mode uses an in-memory SQLite database loaded with the [sample employee database](https://github.com/bytebase/dbhub/tree/main/resources/employee-sqlite) that includes tables for employees, departments, titles, salaries, department employees, and department managers. The sample database includes SQL scripts for table creation, data loading, and testing.
@@ -367,17 +367,17 @@ The project includes pre-commit hooks to run tests automatically before each com
367
367
  TRANSPORT=stdio DSN="postgres://user:password@localhost:5432/dbname?sslmode=disable" npx @modelcontextprotocol/inspector node /path/to/dbhub/dist/index.js
368
368
  ```
369
369
 
370
- #### SSE
370
+ #### HTTP
371
371
 
372
372
  ```bash
373
- # Start DBHub with SSE transport
374
- pnpm dev --transport=sse --port=8080
373
+ # Start DBHub with HTTP transport
374
+ pnpm dev --transport=http --port=8080
375
375
 
376
376
  # Start the MCP Inspector in another terminal
377
377
  npx @modelcontextprotocol/inspector
378
378
  ```
379
379
 
380
- Connect to the DBHub server `/sse` endpoint
380
+ Connect to the DBHub server `/message` endpoint
381
381
 
382
382
  ## Contributors
383
383
 
package/dist/index.js CHANGED
@@ -1404,17 +1404,17 @@ var MySQLConnector = class {
1404
1404
  }
1405
1405
  try {
1406
1406
  const results = await this.pool.query(sql2);
1407
- if (Array.isArray(results[0])) {
1407
+ const [firstResult] = results;
1408
+ if (Array.isArray(firstResult) && firstResult.length > 0 && Array.isArray(firstResult[0]) && firstResult[0].length === 2) {
1408
1409
  let allRows = [];
1409
- for (const result of results[0]) {
1410
- if (Array.isArray(result) && result.length > 0) {
1411
- allRows.push(...result);
1410
+ for (const [rows, _fields] of firstResult) {
1411
+ if (Array.isArray(rows)) {
1412
+ allRows.push(...rows);
1412
1413
  }
1413
1414
  }
1414
1415
  return { rows: allRows };
1415
1416
  } else {
1416
- const [rows, fields] = results;
1417
- return { rows, fields };
1417
+ return { rows: Array.isArray(firstResult) ? firstResult : [] };
1418
1418
  }
1419
1419
  } catch (error) {
1420
1420
  console.error("Error executing query:", error);
@@ -1774,21 +1774,17 @@ var MariaDBConnector = class {
1774
1774
  }
1775
1775
  try {
1776
1776
  const results = await this.pool.query(sql2);
1777
- if (Array.isArray(results)) {
1778
- if (results.length > 1 || results[0] && Array.isArray(results[0])) {
1779
- let allRows = [];
1780
- for (const result of results) {
1781
- if (Array.isArray(result) && result.length > 0) {
1782
- allRows.push(...result);
1783
- }
1777
+ const [firstResult] = results;
1778
+ if (Array.isArray(firstResult) && firstResult.length > 0 && Array.isArray(firstResult[0]) && firstResult[0].length === 2) {
1779
+ let allRows = [];
1780
+ for (const [rows, _fields] of firstResult) {
1781
+ if (Array.isArray(rows)) {
1782
+ allRows.push(...rows);
1784
1783
  }
1785
- return { rows: allRows };
1786
- } else {
1787
- const [rows, fields] = results;
1788
- return { rows, fields };
1789
1784
  }
1785
+ return { rows: allRows };
1790
1786
  } else {
1791
- return { rows: results };
1787
+ return { rows: Array.isArray(firstResult) ? firstResult : [] };
1792
1788
  }
1793
1789
  } catch (error) {
1794
1790
  console.error("Error executing query:", error);
@@ -2300,7 +2296,7 @@ ConnectorRegistry.register(new OracleConnector());
2300
2296
 
2301
2297
  // src/server.ts
2302
2298
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
2303
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
2299
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2304
2300
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2305
2301
  import express from "express";
2306
2302
  import path3 from "path";
@@ -2477,11 +2473,11 @@ function resolveDSN() {
2477
2473
  function resolveTransport() {
2478
2474
  const args = parseCommandLineArgs();
2479
2475
  if (args.transport) {
2480
- const type = args.transport === "sse" ? "sse" : "stdio";
2476
+ const type = args.transport === "http" ? "http" : "stdio";
2481
2477
  return { type, source: "command line argument" };
2482
2478
  }
2483
2479
  if (process.env.TRANSPORT) {
2484
- const type = process.env.TRANSPORT === "sse" ? "sse" : "stdio";
2480
+ const type = process.env.TRANSPORT === "http" ? "http" : "stdio";
2485
2481
  return { type, source: "environment variable" };
2486
2482
  }
2487
2483
  return { type: "stdio", source: "default" };
@@ -3444,13 +3440,16 @@ See documentation for more details on configuring database connections.
3444
3440
  `);
3445
3441
  process.exit(1);
3446
3442
  }
3447
- const server = new McpServer2({
3448
- name: SERVER_NAME,
3449
- version: SERVER_VERSION
3450
- });
3451
- registerResources(server);
3452
- registerTools(server);
3453
- registerPrompts(server);
3443
+ const createServer = () => {
3444
+ const server = new McpServer2({
3445
+ name: SERVER_NAME,
3446
+ version: SERVER_VERSION
3447
+ });
3448
+ registerResources(server);
3449
+ registerTools(server);
3450
+ registerPrompts(server);
3451
+ return server;
3452
+ };
3454
3453
  const connectorManager = new ConnectorManager();
3455
3454
  console.error(`Connecting with DSN: ${redactDSN(dsnData.dsn)}`);
3456
3455
  console.error(`DSN source: ${dsnData.source}`);
@@ -3478,29 +3477,50 @@ See documentation for more details on configuring database connections.
3478
3477
  console.error(`Running in ${activeModes.join(" and ")} mode - ${modeDescriptions.join(", ")}`);
3479
3478
  }
3480
3479
  console.error(generateBanner(SERVER_VERSION, activeModes));
3481
- if (transportData.type === "sse") {
3480
+ if (transportData.type === "http") {
3482
3481
  const app = express();
3483
- let transport;
3484
- app.get("/sse", async (req, res) => {
3485
- transport = new SSEServerTransport("/message", res);
3486
- console.error("Client connected", transport?.["_sessionId"]);
3487
- await server.connect(transport);
3488
- res.on("close", () => {
3489
- console.error("Client Disconnected", transport?.["_sessionId"]);
3490
- });
3482
+ app.use(express.json());
3483
+ app.use((req, res, next) => {
3484
+ const origin = req.headers.origin;
3485
+ if (origin && !origin.startsWith("http://localhost") && !origin.startsWith("https://localhost")) {
3486
+ return res.status(403).json({ error: "Forbidden origin" });
3487
+ }
3488
+ res.header("Access-Control-Allow-Origin", origin || "http://localhost");
3489
+ res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
3490
+ res.header("Access-Control-Allow-Headers", "Content-Type, Mcp-Session-Id");
3491
+ res.header("Access-Control-Allow-Credentials", "true");
3492
+ if (req.method === "OPTIONS") {
3493
+ return res.sendStatus(200);
3494
+ }
3495
+ next();
3491
3496
  });
3492
3497
  app.post("/message", async (req, res) => {
3493
- console.error("Client Message", transport?.["_sessionId"]);
3494
- await transport.handlePostMessage(req, res, req.body);
3498
+ try {
3499
+ const transport = new StreamableHTTPServerTransport({
3500
+ sessionIdGenerator: void 0,
3501
+ // Disable session management for stateless mode
3502
+ enableJsonResponse: false
3503
+ // Use SSE streaming
3504
+ });
3505
+ const server = createServer();
3506
+ await server.connect(transport);
3507
+ await transport.handleRequest(req, res, req.body);
3508
+ } catch (error) {
3509
+ console.error("Error handling request:", error);
3510
+ if (!res.headersSent) {
3511
+ res.status(500).json({ error: "Internal server error" });
3512
+ }
3513
+ }
3495
3514
  });
3496
3515
  const portData = resolvePort();
3497
3516
  const port = portData.port;
3498
3517
  console.error(`Port source: ${portData.source}`);
3499
- app.listen(port, () => {
3518
+ app.listen(port, "localhost", () => {
3500
3519
  console.error(`DBHub server listening at http://localhost:${port}`);
3501
- console.error(`Connect to MCP server at http://localhost:${port}/sse`);
3520
+ console.error(`Connect to MCP server at http://localhost:${port}/message`);
3502
3521
  });
3503
3522
  } else {
3523
+ const server = createServer();
3504
3524
  const transport = new StdioServerTransport();
3505
3525
  console.error("Starting with STDIO transport");
3506
3526
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebase/dbhub",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Universal Database MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
19
  "@azure/identity": "^4.8.0",
20
- "@modelcontextprotocol/sdk": "^1.6.1",
20
+ "@modelcontextprotocol/sdk": "^1.12.1",
21
21
  "better-sqlite3": "^11.9.0",
22
22
  "dotenv": "^16.4.7",
23
23
  "express": "^4.18.2",