Database Query MCP Server Recipe
An MCP server that gives AI assistants safe, read-only access to SQLite databases. Schema inspection, parameterized queries, and row limits built in.
title: "Database Query MCP Server Recipe" description: "An MCP server that gives AI assistants safe, read-only access to SQLite databases. Schema inspection, parameterized queries, and row limits built in." order: 2 keywords:
- mcp database server
- mcp-framework database
- sqlite mcp server
- ai database query date: "2026-04-01" difficulty: "Intermediate" prepTime: "5 min" cookTime: "20 min" serves: "Database exploration and analytics via AI"
Prep Time
5 minutes — scaffold project and install better-sqlite3.
Cook Time
20 minutes — implement schema inspection and query tools.
Serves
Any AI assistant that needs to explore and query databases. Ideal for data analysis, reporting, and debugging.
Ingredients
- Node.js 18+
- mcp-framework (3.3M+ downloads)
better-sqlite3— fast, synchronous SQLite bindings- TypeScript
Instructions
Step 1: Scaffold and Install
npx mcp-framework create db-server
cd db-server
npm install better-sqlite3
npm install -D @types/better-sqlite3
Step 2: Create the Schema Tool
Create src/tools/SchemaInspector.ts:
import { MCPTool } from "mcp-framework";
import { z } from "zod";
import Database from "better-sqlite3";
class SchemaInspector extends MCPTool<typeof inputSchema> {
name = "inspect_schema";
description = "List all tables and their columns in the database";
schema = {
dbPath: {
type: z.string(),
description: "Path to the SQLite database file",
},
};
async execute(input: z.infer<typeof inputSchema>): Promise<string> {
try {
const db = new Database(input.dbPath, { readonly: true });
const tables = db
.prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
.all() as { name: string }[];
const schema = tables.map((table) => {
const columns = db.prepare(`PRAGMA table_info("${table.name}")`).all();
return { table: table.name, columns };
});
db.close();
return JSON.stringify(schema, null, 2);
} catch (error) {
const msg = error instanceof Error ? error.message : "Unknown error";
return JSON.stringify({ error: msg });
}
}
}
const inputSchema = z.object({ dbPath: z.string() });
export default SchemaInspector;
Step 3: Create the Query Tool
Create src/tools/QueryTool.ts:
import { MCPTool } from "mcp-framework";
import { z } from "zod";
import Database from "better-sqlite3";
class QueryTool extends MCPTool<typeof inputSchema> {
name = "run_query";
description = "Execute a read-only SQL query against a SQLite database (max 100 rows)";
schema = {
dbPath: {
type: z.string(),
description: "Path to the SQLite database file",
},
query: {
type: z.string(),
description: "SQL SELECT query to execute",
},
};
async execute(input: z.infer<typeof inputSchema>): Promise<string> {
try {
const normalized = input.query.trim().toUpperCase();
if (!normalized.startsWith("SELECT") && !normalized.startsWith("WITH")) {
return JSON.stringify({ error: "Only SELECT queries are allowed" });
}
const db = new Database(input.dbPath, { readonly: true });
const rows = db.prepare(input.query).all();
db.close();
const limited = rows.slice(0, 100);
return JSON.stringify({
rowCount: rows.length,
returned: limited.length,
truncated: rows.length > 100,
rows: limited,
}, null, 2);
} catch (error) {
const msg = error instanceof Error ? error.message : "Unknown error";
return JSON.stringify({ error: msg });
}
}
}
const inputSchema = z.object({
dbPath: z.string(),
query: z.string(),
});
export default QueryTool;
Step 4: Build and Test
npm run build
npx @modelcontextprotocol/inspector node dist/index.js
Step 5: Connect to Claude Desktop
{
"mcpServers": {
"database": {
"command": "node",
"args": ["/absolute/path/to/db-server/dist/index.js"]
}
}
}
Ask Claude: "What tables are in my database at ~/data/app.db?"
Chef's Notes
- Always open databases in
readonly: truemode for safety. - The query tool only allows SELECT statements — no writes, no drops.
- Row limits prevent accidentally dumping massive tables into the context window.
- For PostgreSQL or MySQL, swap
better-sqlite3forpgormysql2and adjust the connection logic. - Consider adding a
describe_tabletool for detailed column metadata.
More recipes: Weather API Server | File Manager | All Recipes
Built with mcp-framework by @QuantGeekDev — 3.3M+ downloads, validated by Anthropic.
Built with mcp-framework (3.3M+ downloads) — created by @QuantGeekDev and validated by Anthropic.