feat: add server assembly and stdio entry

This commit is contained in:
smartass
2026-06-12 00:13:10 +05:00
parent 7514aedb1e
commit 6bf2aa74ca
6 changed files with 120 additions and 8 deletions
+10
View File
@@ -0,0 +1,10 @@
export const parseArgs = (
argv: string[],
env: NodeJS.ProcessEnv = process.env
): { configPath?: string } => {
const flagIndex = argv.indexOf('--config')
if (flagIndex !== -1 && argv[flagIndex + 1]) {
return { configPath: argv[flagIndex + 1] }
}
return { configPath: env.DBMOLE_CONFIG }
}
+35 -1
View File
@@ -1 +1,35 @@
export const placeholder = true
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { parseArgs } from './cli.js'
import { createRegistry } from './config/registry.js'
import { defaultStorePath } from './config/store.js'
import { createManager } from './db/manager.js'
import { createServer } from './server.js'
const main = async () => {
const { configPath } = parseArgs(process.argv.slice(2))
const registry = createRegistry({ storePath: defaultStorePath(), configPath })
const manager = createManager(registry)
const server = createServer(registry, manager)
let shuttingDown = false
const shutdown = async () => {
if (shuttingDown) {
return
}
shuttingDown = true
await manager.disposeAll().catch(() => {})
process.exit(0)
}
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)
process.stdin.on('close', shutdown)
await server.connect(new StdioServerTransport())
console.error('dbmole: MCP server listening on stdio')
}
main().catch((error) => {
const message = error instanceof Error ? error.message : String(error)
console.error('dbmole: fatal: ' + message)
process.exit(1)
})
+16
View File
@@ -0,0 +1,16 @@
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { Registry } from './config/registry.js'
import type { Manager } from './db/manager.js'
import { registerConnectionTools } from './tools/connections.js'
import { registerQueryTools } from './tools/query.js'
import { registerSchemaTools } from './tools/schema.js'
export const SERVER_VERSION = '0.1.0'
export const createServer = (registry: Registry, manager: Manager): McpServer => {
const server = new McpServer({ name: 'dbmole-mcp', version: SERVER_VERSION })
registerConnectionTools(server, registry, manager)
registerQueryTools(server, manager)
registerSchemaTools(server, manager)
return server
}
+22
View File
@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest'
import { parseArgs } from '../../src/cli.js'
describe('parseArgs', () => {
it('prefers the --config flag', () => {
expect(parseArgs(['--config', '/tmp/c.json'], { DBMOLE_CONFIG: '/env.json' })).toEqual({
configPath: '/tmp/c.json'
})
})
it('falls back to DBMOLE_CONFIG env', () => {
expect(parseArgs([], { DBMOLE_CONFIG: '/env.json' })).toEqual({ configPath: '/env.json' })
})
it('returns undefined without flag or env', () => {
expect(parseArgs([], {})).toEqual({ configPath: undefined })
})
it('ignores --config without a value', () => {
expect(parseArgs(['--config'], {})).toEqual({ configPath: undefined })
})
})
+37
View File
@@ -0,0 +1,37 @@
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { createRegistry } from '../../src/config/registry.js'
import { createServer } from '../../src/server.js'
import { connectClient, fakeManager } from './helpers.js'
describe('createServer', () => {
let dir: string
beforeEach(() => {
dir = mkdtempSync(join(tmpdir(), 'dbmole-server-'))
})
afterEach(() => {
rmSync(dir, { recursive: true, force: true })
})
it('registers all nine tools', async () => {
const registry = createRegistry({ storePath: join(dir, 'connections.json'), env: {} })
const server = createServer(registry, fakeManager())
const client = await connectClient(server)
const { tools } = await client.listTools()
expect(tools.map((tool) => tool.name).sort()).toEqual([
'add_connection',
'describe_table',
'execute_sql',
'list_connections',
'list_databases',
'list_tables',
'remove_connection',
'test_connection',
'update_connection'
])
})
})
-7
View File
@@ -1,7 +0,0 @@
import { describe, expect, it } from 'vitest'
describe('smoke', () => {
it('runs', () => {
expect(1 + 1).toBe(2)
})
})