Skip to main content
Glama

Read-only Database MCP

by Crei03
app.ts4.33 kB
import http from "http"; import fs from "fs"; import path from "path"; import { URL } from "url"; import { listConfigs, saveConfig, listHistory, getConfig, DbConfig } from "./storage.js"; import { listTables, previewTable, runSelect } from "./db.js"; const port = Number(process.env.PORT || 4000); const publicDir = path.join(process.cwd(), "public"); function sendJson(res: http.ServerResponse, status: number, payload: unknown) { res.writeHead(status, { "Content-Type": "application/json" }); res.end(JSON.stringify(payload)); } async function readBody(req: http.IncomingMessage): Promise<any> { const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(chunk as Buffer); } const raw = Buffer.concat(chunks).toString("utf8"); if (!raw) return {}; return JSON.parse(raw); } function serveStatic(req: http.IncomingMessage, res: http.ServerResponse, pathname: string) { const filePath = path.join(publicDir, pathname === "/" ? "index.html" : pathname.slice(1)); if (!filePath.startsWith(publicDir)) { res.writeHead(403); return res.end("Forbidden"); } if (!fs.existsSync(filePath)) { res.writeHead(404); return res.end("Not found"); } const content = fs.readFileSync(filePath); const ext = path.extname(filePath); const contentType = ext === ".html" ? "text/html" : ext === ".js" ? "text/javascript" : ext === ".css" ? "text/css" : "application/octet-stream"; res.writeHead(200, { "Content-Type": contentType }); res.end(content); } const server = http.createServer(async (req, res) => { if (!req.url || !req.method) return; const url = new URL(req.url, `http://localhost:${port}`); if (url.pathname.startsWith("/api/")) { try { if (req.method === "GET" && url.pathname === "/api/configs") { return sendJson(res, 200, listConfigs()); } if (req.method === "POST" && url.pathname === "/api/configs") { const body = await readBody(req); const config: DbConfig = { name: body.name, kind: body.kind || "postgres", host: body.host, port: Number(body.port), user: body.user, password: body.password, database: body.database, ssl: body.ssl !== undefined ? Boolean(body.ssl) : undefined, sslMode: body.sslMode || undefined, }; if (!config.name) throw new Error("Config name is required"); saveConfig(config); return sendJson(res, 201, { ok: true }); } if (req.method === "GET" && url.pathname === "/api/history") { return sendJson(res, 200, listHistory()); } if (req.method === "GET" && url.pathname === "/api/tables") { const dbName = url.searchParams.get("db"); if (!dbName) throw new Error("Missing db"); const config = getConfig(dbName); if (!config) throw new Error(`Unknown db config: ${dbName}`); const tables = await listTables(config); return sendJson(res, 200, tables); } if (req.method === "GET" && url.pathname === "/api/preview") { const dbName = url.searchParams.get("db"); const table = url.searchParams.get("table"); if (!dbName || !table) throw new Error("Missing db or table"); const config = getConfig(dbName); if (!config) throw new Error(`Unknown db config: ${dbName}`); const data = await previewTable(config, table); return sendJson(res, 200, data); } if (req.method === "POST" && url.pathname === "/api/select") { const body = await readBody(req); const dbName = body.db as string; const sql = body.sql as string; if (!dbName || !sql) throw new Error("Missing db or sql"); const config = getConfig(dbName); if (!config) throw new Error(`Unknown db config: ${dbName}`); const rows = await runSelect(config, sql); return sendJson(res, 200, rows); } res.writeHead(404); return res.end("Not found"); } catch (err) { console.error(err); return sendJson(res, 400, { error: (err as Error).message }); } } serveStatic(req, res, url.pathname); }); server.listen(port, () => { console.log(`UI/API server running at http://localhost:${port}`); });

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/Crei03/mcp-db'

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