SQL MCP Server
Can be configured as a local AI provider for the same natural language query functionality, enabling offline operation.
Can be configured as the AI provider for natural language to SQL conversion, supporting OpenAI models and compatible endpoints.
Provides tools for querying and analyzing an e-commerce SQLite database, including listing tables, describing schemas, executing read-only SQL, and asking natural-language questions.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@SQL MCP ServerWho are our top 5 customers by total spending?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
SQL MCP Server
An AI-powered Model Context Protocol (MCP) server that allows you to query and analyze an e-commerce SQLite database using natural language.
Ask questions like:
"Who are our top 5 customers by total spending?"
"Show all products in the Electronics category with stock below 50"
"What was our total revenue for completed orders in 2026?"
Four tools, three of which need no API key at all. Read-only at two independent levels, paged results, SQLite's own error text passed back to the caller, and 74 automated tests.
Contents — Quick Start · Configure a Provider · Tools · Paging · Errors · Tests · Docker · MCP Clients · Configuration · Safety · Data Egress · Project Layout
🚀 Quick Start
1. Prerequisites
Node.js:
v22.5.0or higher (for the built-innode:sqlitemodule);v24recommendednpm:
v11.0.0or higher
2. Installation
Clone this repository and install dependencies:
npm install
cp .env.example .env
npm run buildThat is enough to connect the server to a client and use list_tables,
describe_table and execute_sql. A provider is needed only for the natural
language tool — see below.
Related MCP server: shop
🔑 Configure Your AI Provider
Open the .env file and set up your preferred AI model. The server automatically detects your provider based on the variables you set:
Option A: Anthropic Claude (Recommended)
ANTHROPIC_API_KEY=sk-ant-api03-...
ANTHROPIC_MODEL=claude-opus-5Option B: Local Ollama (Free & Offline)
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3.2Note: Make sure Ollama is running (
ollama serve) and you have pulled the model (ollama pull llama3.2).
Option C: OpenAI
OPENAI_API_KEY=sk-proj-...
OPENAI_MODEL=gpt-4o-miniOption D: Custom / Third-Party (Groq, DeepSeek, OpenRouter)
OPENAI_API_KEY=your_api_key
OPENAI_BASE_URL=https://api.groq.com/openai/v1
OPENAI_MODEL=llama-3.3-70b-versatile🛠 Available Tools
Three of the four talk to SQLite directly — no API key, no cost, instant:
Tool | What it does | Needs a provider |
| Every table with a plain-language explanation of what it holds, its row count and columns, plus the relationships between tables and the revenue convention this database uses. | No |
| One table in full — columns with types, keys and descriptions, foreign keys, the | No |
| Any read-only | No |
| Takes a plain-language question, generates and executes the appropriate SQL, and returns a written answer with insights. | Yes |
Each tool's description tells the calling agent not just what it does but when
not to use it — query_database states that it returns prose rather than
values, costs money and makes two LLM calls, and points at execute_sql for
anything the agent intends to compute with. Both tools state the row cap and the
revenue convention inline, so the agent does not have to discover them by
trial.
Example: describe_table
// describe_table { "table_name": "orders" } — abridged
{
"table": "orders",
"purpose": "Order headers — one row per order placed by a customer, carrying its date, lifecycle status and total.",
"rowCount": 750,
"columns": [
{ "name": "status", "type": "TEXT", "primaryKey": false, "notNull": true, "default": null,
"description": "Lifecycle stage, one of: new, processing, shipped, completed, cancelled. Determines whether the order counts as revenue." }
],
"foreignKeys": [
{ "column": "customer_id", "referencesTable": "customers", "referencesColumn": "id", "onDelete": "CASCADE" }
],
"notes": ["Revenue convention: count every order whose status is not 'cancelled' …"],
"dataCoverage": { "order_date": { "min": "2026-02-17 18:53:30", "max": "2026-08-22 17:06:30" } },
"createStatement": "CREATE TABLE orders ( … )"
}dataCoverage is there so an agent can tell an empty result from an
out-of-range question: asking about 2025 returns "the data runs from … to …"
rather than a bare zero that reads like a bug.
📄 Paging Through Large Results
Every result is capped — at DATABASE_MAX_ROWS (default 100), or at a
smaller limit you pass. A larger limit is clamped rather than rejected, so a
caller always gets rows back.
execute_sql takes limit and offset and tells you whether there is more:
// execute_sql { "sql": "SELECT id, name FROM products ORDER BY id", "limit": 2, "offset": 2 }
{
"columns": ["id", "name"],
"rows": [
{ "id": 3, "name": "Ноутбук UltraBook 15" },
{ "id": 4, "name": "Умные часы FitWatch" }
],
"rowCount": 2,
"offset": 2,
"hasMore": true,
"nextOffset": 4,
"note": "More rows matched than were returned. Call again with offset=4 for the next page.",
"executionTimeMs": 0.09
}Keep calling with offset: nextOffset until hasMore is false. When a result
fits in one page, hasMore is false and totalAvailableRows reports the true
total.
The cap is enforced while stepping the statement, not by trimming a finished
result: the server stops one row past the cap and never materialises the rest.
The SQL is model-generated, so an accidental cross join would otherwise pull
millions of rows into memory before any were discarded. Paging is likewise done
during iteration rather than by appending LIMIT/OFFSET to the SQL, which
would have to survive whatever the generated statement already ends with.
query_database shares the row cap but does not page — it summarises in prose,
where a page number has nothing to attach to. Use execute_sql for anything
larger than one page.
🚦 What an Error Looks Like
Failures come back as normal MCP tool results with isError: true and a message
the calling agent can act on, rather than as transport-level faults.
You send | You get back |
|
|
|
|
|
|
|
|
A natural-language request to delete data |
|
Two rules govern that text:
SQLite's own message is preserved. "no such column: nope" is the single most useful thing an agent can be told, because it is enough to rewrite the query and retry. It is never flattened into "query failed".
Host detail never escapes. Unrecognised errors — which may carry a stack trace — collapse to a generic line, and everything on the way out is scrubbed of the database path, the project root and the home directory. Full detail stays in the server logs. This is covered by its own test file.
🧪 Automated Tests
npm test # 74 tests across 4 files, runs in well under a second
npm run test:watch
npm run typecheckPlain node --test with tsx — no test framework dependency. The suites run
against the real db/shop.db, not a mock, so they fail if the schema and the
documentation drift apart.
File | Covers |
| Every way a write could be smuggled past the read-only guard: leading comments, |
| Row capping, |
| What a caller is allowed to see: actionable messages pass through, unknown errors collapse, and the database path / project root / home directory are redacted from both. |
| That every table and column in the live database has a written description, that no description refers to a table that no longer exists, and that the revenue convention is stated. |
The guard suite is the one that matters most: it is the boundary that makes "read-only" true rather than merely intended, and one of its cases is a real false positive it caught during development.
🐳 Docker
docker build -t sql-mcp .The image bundles the database, so it needs no volume mount. Because this is a
stdio server, it must be run with -i and no TTY — the container's stdin and
stdout carry the JSON-RPC stream:
docker run -i --rm -e ANTHROPIC_API_KEY sql-mcpWire it into a client with examples/claude_desktop_config.docker.json.
Drop the -e ANTHROPIC_API_KEY to run without credentials — list_tables,
describe_table and execute_sql work without a provider.
The build is multi-stage: TypeScript is compiled in a node:24-alpine builder,
and only dist/, db/ and production dependencies are copied into the runtime
image. It runs as the unprivileged node user, no .env is ever copied in
(credentials come from -e), and there are no native addons to compile because
SQLite ships inside Node itself.
🔌 Connecting to MCP Clients
Ready-to-use configuration files are in examples/ — copy the one
matching your client and replace the path. examples/claude_desktop_config.no-api-key.json
runs the server with no credentials at all, which is enough for list_tables,
describe_table and execute_sql.
Claude Desktop Configuration
Add this server to your Claude Desktop configuration file (claude_desktop_config.json):
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
(Make sure to run npm run build once before connecting)
Example 1: Anthropic Claude (Default)
{
"mcpServers": {
"sql-mcp": {
"command": "node",
"args": ["/absolute/path/to/sql-mcp/dist/index.js"],
"env": {
"ANTHROPIC_API_KEY": "sk-ant-api03-your-key-here",
"ANTHROPIC_MODEL": "claude-opus-5"
}
}
}
}Example 2: Local Ollama (Free & Offline)
{
"mcpServers": {
"sql-mcp": {
"command": "node",
"args": ["/absolute/path/to/sql-mcp/dist/index.js"],
"env": {
"OLLAMA_BASE_URL": "http://localhost:11434",
"OLLAMA_MODEL": "llama3.2"
}
}
}
}Example 3: OpenAI
{
"mcpServers": {
"sql-mcp": {
"command": "node",
"args": ["/absolute/path/to/sql-mcp/dist/index.js"],
"env": {
"OPENAI_API_KEY": "sk-proj-your-key-here",
"OPENAI_MODEL": "gpt-4o-mini"
}
}
}
}Example 4: Custom / Groq / OpenRouter / DeepSeek
{
"mcpServers": {
"sql-mcp": {
"command": "node",
"args": ["/absolute/path/to/sql-mcp/dist/index.js"],
"env": {
"OPENAI_API_KEY": "gsk_your_groq_api_key",
"OPENAI_BASE_URL": "https://api.groq.com/openai/v1",
"OPENAI_MODEL": "llama-3.3-70b-versatile"
}
}
}
}The server resolves db/shop.db relative to its own location, so DATABASE_PATH
is not needed in any of these — MCP clients launch servers from a working
directory of their own choosing, and the server does not depend on it.
🔎 Trying It Locally
Instant Terminal Test
You can test natural language questions directly in your terminal:
npm run query -- "Show top 3 products by price"Visual Web Inspector
Test tools interactively in your browser using the official MCP Inspector:
npm run inspect:devOpen the inspector URL in your browser (e.g.
http://localhost:5173).Click Connect.
Under Tools, select
query_database, enter your question, and click Run Tool.
All npm Scripts
Script | Does |
| Compile to |
| Run the built server over stdio |
| Run from source with reload ( |
| Automated tests |
|
|
| Ask a question from the terminal |
| MCP Inspector against |
🔧 Configuration Reference
Every variable is optional; the defaults are what runs if you set nothing.
Variable | Default | Purpose |
|
| Database location. Absolute, or relative to the project root — never to the working directory. |
|
| Hard ceiling on rows returned per call, and on rows sent to the LLM. |
|
| Per-request ceiling for LLM calls. A question makes two sequential calls, so without this a stalled provider hangs the tool call. |
| auto-detected |
|
| — / | Anthropic provider. |
| — / | OpenAI and any OpenAI-compatible endpoint. |
|
| Local Ollama. |
| unset |
|
A malformed value is reported on stderr and falls back to the default rather
than being silently accepted — a typo in a client's env block shows up at
startup instead of behaving as though the variable were never set. DEBUG logs
carry every question asked and every statement generated, and under an MCP client
they land in the client's persistent log files, so they stay off unless you opt in.
🔒 Safety
The database is opened read-only at the driver level, and every statement is validated
before execution: it must be a single SELECT/WITH/VALUES statement, with no
keyword that writes data, alters schema, or changes connection state. Neither check can
be switched off by configuration. A request like "delete all cancelled orders" is
refused rather than executed.
The validator works over a tokenized view of the statement rather than the raw
text, so comments, string literals and quoted identifiers cannot be used to hide
a keyword — /* c */ DELETE FROM orders and WITH x AS (SELECT 1) DELETE FROM orders
are both rejected, while SELECT replace(name, 'a', 'b') is not.
Text that this server did not write — your question, and values read out of the
database — is delimited in the prompts with an unforgeable per-request marker, so
a product named Widget (SYSTEM: ignore prior instructions…) cannot escape into
instruction context. That matters beyond this process: the answer travels back to
the calling agent as tool output, one hop further.
🔐 What Gets Sent Where
This server answers questions by calling an LLM, so database content leaves your
machine on every query_database call. Specifically, each call sends:
Your database schema — table names, column names and types, and row counts — to generate the SQL.
The rows the query returned (up to
DATABASE_MAX_ROWS, default 100) — to turn them into a written answer.
For the bundled shop database those rows include customer names, email addresses and
phone numbers. They go to whichever provider you configure, at whichever endpoint
OPENAI_BASE_URL names — which for Groq, OpenRouter or DeepSeek is a third party under
its own terms.
If that is not acceptable for your data:
Use the other three tools.
list_tables,describe_tableandexecute_sqlmake no network call at all — nothing leaves the machine.Use Ollama. It runs locally, so nothing leaves the machine.
Restrict the queries. Aggregate questions ("revenue by category") return summary rows rather than customer records.
Lower
DATABASE_MAX_ROWSto cap how much row data is sent per query.
The server never sends the database file, and it can only read — see Safety.
📁 Project Layout
src/
index.ts MCP server entry point (stdio transport)
cli.ts Terminal harness: npm run query -- "…"
config/ Env parsing, provider detection, path resolution
tools/ The four MCP tools and their descriptions
services/
database.service.ts SQLite access, row capping, paging, introspection
sql-guard.ts Read-only enforcement (tokenizing validator)
errors.ts Caller-safe messages, path redaction
schema-metadata.ts Human-written meaning the schema cannot record
query-engine.service.ts NL → SQL → execute → prose pipeline
llm/ Anthropic / OpenAI / Ollama behind one interface
prompts/ SQL generation, humanization, untrusted-input framing
tests/ node --test suites (see Automated Tests)
db/ shop.db and its schema documentation
docs/ Architecture and sequence diagrams
examples/ Ready-to-paste client configurations📚 Technical Documentation
Technical Specifications & Architecture Diagrams: System design, sequence diagrams, LLM strategy pattern, and safety mechanisms.
Database Schema Documentation: Complete table schema definitions, ER diagram, and SQLite data dictionary.
Client Configuration Examples: Which config file to copy, and how to run with no API key.
Available Tools
4 toolsdescribe_tableDescribe a Database TableA
Returns everything about one table: its purpose, every column with type, nullability, primary key and a plain-language description, its foreign keys, its CREATE TABLE statement, caveats worth knowing before querying it, and — for date columns — the range the data actually covers, so you can tell an empty result from an out-of-range question. Use it before writing SQL against a table you have not queried yet. Reads SQLite directly: no LLM call, no API key. Returns JSON.
| Name | Required | Description | Default |
|---|---|---|---|
| table_name | Yes | Exact table name, as returned by list_tables (e.g. 'orders', 'order_items'). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full disclosure burden. It states execution mechanics ('Reads SQLite directly'), that no external dependency exists ('no LLM call, no API key'), and the return format ('Returns JSON'). It also explains a non-obvious behavior — returning real data coverage ranges for date columns so empty results can be distinguished from out-of-range questions. The 'describe' verb implies read-only; the description could state non-mutating explicitly but the disclosed details exceed the baseline for an annotation-less tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: deliverable list, when-to-use, and execution/return format. The content list is front-loaded ahead of the usage guidance. It is information-dense rather than wasteful, though the long enumerative first sentence could be tightened slightly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description bears full responsibility for explaining return content — it enumerates all major elements including the non-obvious date-range feature. One fully-documented parameter, usage routing against siblings, execution behavior, and return format are all specified. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single table_name parameter, which the schema already documents as 'Exact table name, as returned by list_tables (e.g. 'orders', 'order_items').' With full schema coverage the baseline is 3; the description adds no parameter-specific detail beyond hinting that the table must exist (it describes 'one table'). The schema does the heavy lifting, so 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb-resource pair ('Returns everything about one table') and enumerates the exact contents delivered: columns with type/nullability/PK/plain-language, foreign keys, CREATE TABLE, caveats, and date range coverage. It clearly differentiates from siblings — it describes schema rather than querying data (query_database, execute_sql) or listing tables (list_tables).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use instruction: 'Use it before writing SQL against a table you have not queried yet.' This frames it as a prerequisite step and implies the alternative is the SQL/query tools, though it does not name them explicitly or state when-not-to-use. Clear context, slightly implicit on exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
execute_sqlRun a Read-Only SQL QueryA
Runs a single read-only SQL query against the e-commerce SQLite database and returns structured rows plus column names as JSON. Use this to answer analytical questions yourself — joins, aggregates, multi-step work — and when you need the actual values rather than a written summary. Call list_tables and describe_table first if you do not know the schema.
LIMITATIONS: SELECT only. The statement must be a single SELECT (or WITH ... SELECT, or VALUES); anything that writes data, changes schema, or alters connection state is rejected, as is more than one statement per call. SQLite dialect. Results are capped at 100 rows per call — use offset to page through more, and check hasMore in the response. No LLM is involved: no API key needed, no cost, and the result is exact.
Revenue convention: count every order whose status is not 'cancelled' (i.e. new, processing, shipped and completed all count as revenue). Cancelled orders are excluded because the sale did not complete. To count only fully delivered sales instead, filter status = 'completed' and say so in the answer.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | A single read-only SQL SELECT statement in SQLite dialect. Example: "SELECT p.name, SUM(oi.quantity) AS units FROM order_items oi JOIN products p ON p.id = oi.product_id JOIN orders o ON o.id = oi.order_id WHERE o.status != 'cancelled' GROUP BY p.id ORDER BY units DESC LIMIT 5" | |
| limit | No | Maximum rows to return (default and hard ceiling: 100). Larger values are clamped. | |
| offset | No | Rows to skip before returning results. Use with `limit` to page through a large result set. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It thoroughly discloses: read-only and SELECT-only enforcement, single-statement limitation, SQLite dialect, row cap of 100 with offset/hasMore paging, no LLM involvement (no key, no cost, exact results), and the revenue convention (counting non-cancelled orders). This is exemplary transparency for a tool that executes arbitrary SQL.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than a single sentence but every section earns its place: purpose, use case, limitations, and revenue convention are separated and clearly front-loaded. It respects the reader by grouping constraints and providing a concrete example. Slightly verbose but never wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description correctly explains return format ('structured rows plus column names as JSON') and mentions hasMore. It covers all necessary operational details: single-statement rule, paging, dialect, and domain convention. For a complex SQL tool with no annotations, this is remarkably complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters (sql, limit, offset). The description reinforces paging behavior ('use offset to page through more') and the revenue convention, but adds no new parameter semantics beyond what the schema provides. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Runs') and resource ('read-only SQL query against the e-commerce SQLite database') and specifies the output shape ('structured rows plus column names as JSON'). It clearly separates itself from sibling tools by instructing to use list_tables and describe_table first when schema is unknown, implying this tool is for querying after schema discovery.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear when-to-use guidance: 'Use this to answer analytical questions yourself' mentioning joins, aggregates, and multi-step work, and when actual values are needed rather than a written summary. It also gives a prerequisite ('Call list_tables and describe_table first if you do not know the schema') and explicit limitations (SELECT only, single statement, paging). However, it does not explicitly name query_database as an alternative, so the situational contrast is slightly weaker than ideal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tablesList Database TablesA
Lists every table in the e-commerce database with a plain-language explanation of what it holds, its row count, and its column names. Also returns the relationships between tables and the convention this database uses for revenue. Use this first when you need to know what data exists, or to answer questions about the database's structure. Reads SQLite directly: no LLM call, no API key, no cost, returns immediately. Returns JSON.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that it reads SQLite directly, makes no LLM call, requires no API key, costs nothing, returns immediately, and returns JSON. This is comprehensive behavioral disclosure beyond what the empty schema provides.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is moderately lengthy but every sentence adds valuable information: primary output, use-case guidance, and technical behavior. It is front-loaded with the main purpose and remains readable without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no output schema, this description is entirely sufficient. It covers what the tool returns (table lists, explanations, row counts, columns, relationships, revenue convention), when to use it, and how it executes. Nothing an agent needs to decide to call it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema requires no explanation. Baseline for 0 params is 4, and the description appropriately does not invent parameter information. No additional semantics needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and resource: lists every table in the e-commerce database, with a plain-language explanation of contents, row count, and column names. It also mentions relationships and revenue convention, distinguishing it from sibling tools like describe_table which likely focuses on a single table.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent to use this first when needing to know what data exists or answer database structure questions. It does not name sibling tools directly or state when not to use it, but the stated context is sufficient for an agent to decide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_databaseAsk About the Online ShopA
Answers questions about the online shop in plain language: customers, products, stock, orders and sales. Ask it the way a shop owner would ask a colleague — no SQL, no table names, no technical wording needed.
USE THIS for any question about the shop or the business behind it, however casually it is phrased. For example: 'what sells best?', 'how many customers do we have?', 'which products are nearly out of stock?', 'how much did we earn last month?', 'who are our biggest spenders?', 'are any orders stuck in processing?', 'what was in order 42?', 'which category makes the most money?', 'how many orders were cancelled?'.
IT KNOWS ABOUT: customers (names, email, phone, when they signed up), products (name, category, price, stock on hand), orders (date, status — new, processing, shipped, completed, cancelled — and total), and the individual products inside each order (quantity and price paid).
RETURNS: a written answer in everyday language, with Markdown tables when a list or comparison helps. Numbers are read out of the text rather than returned as structured data.
GOOD TO KNOW: it can only look things up — it can never add, change or delete anything, and any request to do so is refused. One answer covers at most 100 rows of data. It asks a language model to write the SQL behind the scenes, so it needs an API key (or a local Ollama) configured, takes a few seconds, and may phrase the same question slightly differently between runs.
| Name | Required | Description | Default |
|---|---|---|---|
| message | Yes | The question about the shop, in plain language — pass the user's own wording where possible (e.g. 'What are our top 5 bestselling products?', 'How many orders were completed in May 2026?', 'Which customers have ordered more than 3 times?', 'What is running low on stock?') | |
| include_sql_details | No | Whether to show the SQL query and timing details underneath the answer. Set to false for a clean, non-technical answer (default: true) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It extensively discloses read-only behavior, refusal of write attempts, a 100-row limit, dependence on an LLM/API key or Ollama, variability between runs, and return format (Markdown tables, numbers read out). This goes well beyond typical disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with clear sections (USE THIS, IT KNOWS ABOUT, RETURNS, GOOD TO KNOW) front-loaded with the core purpose. While lengthy, every sentence adds value and the section headers make it easy to scan. No redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Comprehensive for a natural-language query tool with no annotations and no output schema. It covers scope, entity data, return format, limitations, prerequisites, and refusal behavior. An agent has all necessary information to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers 100% of parameters with detailed descriptions. The description adds value by reinforcing plain-language usage and clarifying that numbers are read from text rather than returned as structured data. It also implies the message should be a natural question, which complements the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States that it answers questions about the online shop in plain language, covering customers, products, stock, orders, and sales. Clearly distinguishes itself from siblings by noting no SQL, table names, or technical wording are needed, implying execute_sql and schema tools are for different tasks.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'USE THIS for any question about the shop or the business behind it' and provides numerous examples. However, it does not explicitly name alternative tools or give a when-not-to-use clause; the no-SQL instruction implies but does not explicitly state that execute_sql is for raw queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
4 tool updates
v0.1.0- First observed
describe_table - First observed
execute_sql - First observed
list_tables - First observed
query_database
TDQS
Each tool has a distinct, non-overlapping purpose: raw SQL execution, schema listing, table detail inspection, and natural-language querying. No ambiguity between them.
All tools follow a consistent verb_object pattern (execute_sql, list_tables, describe_table, query_database). Naming is uniform and predictable.
Four tools is well-scoped for a read-only SQL MCP server. Each tool adds clear value without redundancy, and the count is ideal for the domain.
The server fully covers its read-only analytics purpose: schema exploration (list, describe) and data retrieval (raw SQL and natural language). No missing operations or dead ends within its stated scope.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Ask questions in plain language, get answers from your business database. No SQL required.
1Ask questions across Shopify, Klaviyo, GA4 and 20+ e-commerce sources in plain English.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables safe, read-only analysis of an online store's SQLite database, providing schema introspection, restricted SELECT queries, and specialized analytics tools through MCP.-
- FlicenseAqualityCmaintenanceEnables read-only interaction with an online store's SQLite database over MCP stdio, including table listing, schema inspection, safe read-only SQL execution, and sales analytics. It rejects mutating SQL operations to keep data intact.4-
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to read-only analyze a SQLite e-commerce database, exploring schema and running analytical SQL queries over stdio.-
- FlicenseAqualityBmaintenanceGives AI agents read-only analytical access to an e-commerce SQLite database (customers, orders, order_items, products) via SQL queries, table listing, and schema inspection.3-
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/harutlc/sql-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server