Skip to main content
Glama
mcp-sql-analyst

mcp-sql-analyst

mcp-sql-analyst

CI License: MIT

A read-only Model Context Protocol server that lets an AI assistant explore and query a SQLite database safely.

Connect it to an MCP client such as Claude Code, then ask questions in plain language. The assistant inspects the schema and writes the SQL. The server decides what is allowed to run.

Why it exists

Giving an LLM database access is useful and risky. A generated query can modify data, read other files, or run forever. This server treats every query as untrusted input and enforces its limits below the model, so safety never depends on the model behaving well.

Related MCP server: PostgreSQL Advanced MCP Server

What it looks like

These results come from a real Claude Code session using the sample database.

Which three product categories earned the most from delivered orders in 2025?

The assistant reads the schema first, then writes its own query:

list_tables
describe_table  orders
describe_table  order_items
describe_table  products
run_query       SELECT p.category, ROUND(SUM(oi.quantity * p.price), 2) AS revenue ...

Category

Revenue

Sports

108,052.38

Toys

89,742.04

Kitchen

66,980.56

When the same assistant was told to run unsafe SQL word for word, the server refused:

Attempt

Server response

DELETE FROM orders

Rejected: only read-only SELECT statements are allowed.

ATTACH DATABASE 'data/sample.db' AS other

Rejected: only read-only SELECT statements are allowed.

A recursive query that never ends

Query stopped: exceeded 5000 ms.

Quick start

Requires Node.js 24.10 or later. No API key is needed.

git clone https://github.com/tuliovargasrj/mcp-sql-analyst.git
cd mcp-sql-analyst
npm ci
npm run seed

npm run seed creates data/sample.db, a fictional store with customers, products, and orders.

Try it without an AI client

npx @modelcontextprotocol/inspector --cli node dist/index.js data/sample.db --method tools/list
npx @modelcontextprotocol/inspector --cli node dist/index.js data/sample.db --method tools/call --tool-name run_query --tool-arg "sql=DELETE FROM orders"

The second command prints Rejected: only read-only SELECT statements are allowed. and exits with a non-zero code, because the Inspector treats a tool error as a failure.

Use it with Claude Code

After the quick start, run claude from this folder. It finds the included .mcp.json and offers to enable the server. Then ask:

Which three product categories earned the most from delivered orders in 2025?

Use your own database

Point any MCP client at the built server:

{
  "mcpServers": {
    "sql-analyst": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js", "/absolute/path/to/your.db"]
    }
  }
}

Tools

Tool

Purpose

list_tables

List tables and views, with row counts for tables

describe_table

Show columns, foreign keys, and three sample rows

run_query

Run one read-only SELECT with row, size, and time limits

run_query returns rows as arrays aligned with columns, so a join that repeats a column name keeps both values:

{ "columns": ["id", "id"], "rows": [[31, 1]], "truncated": false }

How the safety model works

All three tools share the same controls, because they all run through the same connection and process.

Risk

Control

Writing or deleting data

The connection is opened read-only

ATTACH to read other files, PRAGMA, schema changes

A SQLite authorizer allows only reads, functions, recursive queries, and the two schema pragmas table_info and foreign_key_list

Runaway queries

Each operation runs in a disposable process that is killed after 5 seconds

A query outliving the server

The server kills any running database process when it stops

Oversized queries

SQL longer than 10,000 characters is rejected before it reaches SQLite

Oversized results

Limits of 200 rows, 2,000 characters per cell, and 100,000 characters per result

A second statement being silently dropped

Anything after the first statement is rejected, including a trailing comment

Design decisions

Authorization happens inside SQLite, not with a text filter. Filtering SQL strings for words like DELETE is easy to bypass. SQLite's authorizer sees the compiled statement, so it does not depend on how the query was written.

Queries run in a process, not a worker thread. The first design used a worker thread. A test showed that node:sqlite blocks inside native code, so worker.terminate() never finished and the program could not exit. Killing a process is reliable, at the cost of starting one for each call.

Two layers cover writes. The read-only connection and the authorizer each block writes alone, and the tests check each one with the other removed.

Testing

npm test
npm run lint

The 43 tests run a real server over stdio through the MCP client SDK. They cover write attempts, ATTACH, multiple statements, oversized queries and results, runaway queries, and a database process that fails to start. CI runs them on Ubuntu and Windows.

A safety control only counts if removing it breaks a test, so each one was disabled in turn to confirm that a test fails. That covered 30 changes across the server. Three of them fail only on Linux: they kill a running query when the server stops, and on Windows the child process did not outlive the server even without them.

Architecture

flowchart LR
    client[MCP client] -- stdio --> server[server.ts<br/>tools]
    server --> dbClient[db-client.ts<br/>time limit]
    dbClient -- one process per call --> dbProcess[db-process.ts]
    dbProcess --> db[db.ts<br/>safety and SQLite]

File

Responsibility

src/index.ts

Command-line entry point

src/server.ts

MCP tool definitions

src/db-client.ts

Starts the database process and enforces the time limit

src/db-process.ts

Entry point of the database process

src/db.ts

Read-only connection, authorizer, and queries

src/db-protocol.ts

Messages between the two processes

src/limits.ts

All limits in one place

src/seed.ts

Sample database generator

Limitations

  • SQLite only, by design.

  • It limits what a query can do, not what it can see: any table in the database can be read, so do not connect a database holding data the AI client should not receive.

  • The database process is not an operating system sandbox. Its protection comes from the read-only connection and the authorizer.

  • Peak memory is bounded only by the time limit, because node:sqlite has no memory cap. A query that builds a very large value can use significant memory for up to 5 seconds.

  • On Linux, a server killed with SIGKILL cannot stop its running query, which keeps running until it is killed by hand. A normal stop is handled.

  • SQL error messages from SQLite are returned to the client. They can mention table and column names, which the client can already read.

Roadmap

  • Allow only selected tables or columns

  • Expose the schema as an MCP resource

  • Add an optional audit log of executed queries

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A Model Context Protocol server implementation that enables AI assistants to execute SQL queries and interact with SQLite databases through a structured interface.
    7
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    A Model Context Protocol server that enables AI assistants to interact with SQLite databases by connecting to database files, listing tables, describing schemas, and executing queries.
    6
    204 npm
    2
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Implements a Model Context Protocol server that enables natural language interactions with SQLite databases, providing tools to list tables, retrieve schemas, count rows, and execute read-only SQL queries.
    MIT