DBHub
by bytebase
- src
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import express from 'express';
import path from 'path';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { ConnectorManager } from './connectors/manager.js';
import { ConnectorRegistry } from './connectors/interface.js';
import { resolveDSN, resolveTransport, resolvePort } from './config/env.js';
import { registerResources } from './resources/index.js';
import { registerTools } from './tools/index.js';
import { registerPrompts } from './prompts/index.js';
// Create __dirname equivalent for ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Load package.json to get version
const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
// Server info
export const SERVER_NAME = "DBHub MCP Server";
export const SERVER_VERSION = packageJson.version;
/**
* Generate ASCII art banner with version information
*/
export function generateBanner(version: string): string {
return `
_____ ____ _ _ _
| __ \\| _ \\| | | | | |
| | | | |_) | |_| |_ _| |__
| | | | _ <| _ | | | | '_ \\
| |__| | |_) | | | | |_| | |_) |
|_____/|____/|_| |_|\\__,_|_.__/
v${version} - Universal Database MCP Server
`;
}
/**
* Initialize and start the DBHub server
*/
export async function main(): Promise<void> {
try {
// Resolve DSN from command line args, environment variables, or .env files
const dsnData = resolveDSN();
if (!dsnData) {
const samples = ConnectorRegistry.getAllSampleDSNs();
const sampleFormats = Object.entries(samples)
.map(([id, dsn]) => ` - ${id}: ${dsn}`)
.join('\n');
console.error(`
ERROR: Database connection string (DSN) is required.
Please provide the DSN in one of these ways (in order of priority):
1. Command line argument: --dsn="your-connection-string"
2. Environment variable: export DSN="your-connection-string"
3. .env file: DSN=your-connection-string
Example formats:
${sampleFormats}
See documentation for more details on configuring database connections.
`);
process.exit(1);
}
// Create MCP server
const server = new McpServer({
name: SERVER_NAME,
version: SERVER_VERSION
});
// Register resources, tools, and prompts
registerResources(server);
registerTools(server);
registerPrompts(server);
// Create connector manager and connect to database
const connectorManager = new ConnectorManager();
console.error(`Connecting with DSN: ${dsnData.dsn}`);
console.error(`DSN source: ${dsnData.source}`);
await connectorManager.connectWithDSN(dsnData.dsn);
// Resolve transport type
const transportData = resolveTransport();
console.error(`Using transport: ${transportData.type}`);
console.error(`Transport source: ${transportData.source}`);
// Print ASCII art banner with version and slogan
console.error(generateBanner(SERVER_VERSION));
// Set up transport based on type
if (transportData.type === 'sse') {
// Set up Express server for SSE transport
const app = express();
let transport: SSEServerTransport;
app.get("/sse", async (req, res) => {
transport = new SSEServerTransport("/message", res);
console.error("Client connected", transport?.['_sessionId']);
await server.connect(transport);
// Listen for connection close
res.on('close', () => {
console.error("Client Disconnected", transport?.['_sessionId']);
});
});
app.post("/message", async (req, res) => {
console.error("Client Message", transport?.['_sessionId']);
await transport.handlePostMessage(req, res, req.body);
});
// Start the HTTP server (port is only relevant for SSE transport)
const portData = resolvePort();
const port = portData.port;
console.error(`Port source: ${portData.source}`);
app.listen(port, () => {
console.error(`DBHub server listening at http://localhost:${port}`);
console.error(`Connect to MCP server at http://localhost:${port}/sse`);
});
} else {
// Set up STDIO transport
const transport = new StdioServerTransport();
console.error("Starting with STDIO transport");
await server.connect(transport);
// Listen for SIGINT to gracefully shut down
process.on('SIGINT', async () => {
console.error('Shutting down...');
await transport.close();
process.exit(0);
});
}
} catch (err) {
console.error("Fatal error:", err);
process.exit(1);
}
}