Skip to main content
Glama
stdio-server.ts5.35 kB
// STDIO MCP server entrypoint so model clients can launch with just: node dist/stdio-server.js import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { registerAllTools } from './shared-tools.js'; import process from 'node:process'; import fs from 'node:fs'; import path from 'node:path'; // Local DB shim identical to node-core but minimal (reuse logic by lazy dynamic import of node-core for DB init) let db: any = null; async function ensureDb() { if (db) return; const envPath = process.env.GOSPEL_DB_PATH; const candidate = envPath ? envPath : 'gospel-library.db'; const absPath = path.isAbsolute(candidate) ? candidate : path.join(process.cwd(), candidate); if (!fs.existsSync(absPath)) { throw new Error(`SQLite file not found at ${absPath} (set GOSPEL_DB_PATH or place gospel-library.db in project root).`); } // Helper to build a uniform statement wrapper const wrap = (runner: (sql: string, args: any[]) => { all: () => any[]; first: () => any }) => ({ prepare(sql: string) { let bindArgs: any[] = []; const api: any = { bind(...args: any[]) { bindArgs = args; return api; }, async all() { const { all } = runner(sql, bindArgs); return { results: all() }; }, async first() { const { first } = runner(sql, bindArgs); return first(); }, async run() { return { success: false }; }, async raw() { return []; } }; return api; } }); const forced = (process.env.GOSPEL_DB_BACKEND || '').toLowerCase(); // Try better-sqlite3 unless forced to something else if (!forced || forced === 'better' || forced === 'native') { try { const BetterSqlite = (await import('better-sqlite3')).default; const native = new BetterSqlite(absPath, { readonly: true }); db = wrap((sql, args) => { const stmt = native.prepare(sql); if (args.length) stmt.bind(...args); return { all: () => stmt.all(), first: () => stmt.get() }; }); console.error(`[gospel-library] using better-sqlite3 backend at ${absPath}`); return; } catch (e) { console.error('[gospel-library] better-sqlite3 unavailable', (e as any)?.message); } } // Bun if (!forced || forced === 'bun') { try { // @ts-ignore Bun global if (typeof Bun !== 'undefined') { // @ts-ignore const { Database } = await import('bun:sqlite'); const sqlite = new Database(absPath, { readOnly: true }); db = wrap((sql, args) => { const stmt = sqlite.query(sql); const bound = args.length ? stmt.bind(...args) : stmt; return { all: () => bound.all(), first: () => bound.get() }; }); console.error(`[gospel-library] using bun:sqlite backend at ${absPath}`); return; } } catch (e) { console.error('[gospel-library] bun:sqlite unavailable', (e as any)?.message); } } // sql.js fallback (pure WASM, slower but portable) if (!forced || forced === 'sqljs' || forced === 'sql.js' || forced === 'wasm') { try { const initSqlJs = (await import('sql.js')).default; const SQL = await initSqlJs(); const fileBuffer = fs.readFileSync(absPath); const sqlDb = new SQL.Database(fileBuffer); db = wrap((sql, args) => { return { all: () => { const stmt = sqlDb.prepare(sql); if (args.length) stmt.bind(args); const rows: any[] = []; while (stmt.step()) rows.push(stmt.getAsObject()); stmt.free(); return rows; }, first: () => { const stmt = sqlDb.prepare(sql); if (args.length) stmt.bind(args); let row: any = null; if (stmt.step()) row = stmt.getAsObject(); stmt.free(); return row; } }; }); console.error(`[gospel-library] using sql.js backend at ${absPath}`); return; } catch (e) { if (forced && (forced === 'sqljs' || forced === 'sql.js' || forced === 'wasm')) { throw new Error(`Forced sql.js backend failed: ${e instanceof Error? e.message : e}`); } console.error('[gospel-library] sql.js unavailable', (e as any)?.message); } } throw new Error('No viable SQLite backend initialized (tried better-sqlite3, bun:sqlite, sql.js)'); } function getDB() { if (!db) throw new Error('DB not initialized'); return db; } async function main() { const transport = new StdioServerTransport(process.stdin, process.stdout); const server = new McpServer({ name: 'gospel-library', version: '0.1.0-stdio' }); if (process.env.GOSPEL_DEBUG) console.error('[gospel-library] starting stdio server'); await ensureDb(); registerAllTools(server, { ensureDb, getDB }); await server.connect(transport); // Keep process alive; on stdin end we exit. process.stdin.resume(); } main().catch(err => { console.error('Fatal stdio server error', err); process.exit(1); }); process.on('unhandledRejection', (reason:any) => { console.error('[gospel-library] unhandledRejection', reason); }); process.on('uncaughtException', err => { console.error('[gospel-library] uncaughtException', err); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/adammharris/gospel-library-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server