SQL MCP Server
SQL MCP Server
Ein KI-gestützter Model Context Protocol (MCP)-Server, mit dem Sie eine E-Commerce-SQLite-Datenbank in natürlicher Sprache abfragen und analysieren können.
Stellen Sie Fragen wie:
„Wer sind unsere Top-5-Kunden nach Gesamtausgaben?“
„Zeige alle Produkte in der Kategorie Elektronik mit einem Bestand unter 50“
„Wie hoch war unser Gesamtumsatz für abgeschlossene Bestellungen im Jahr 2026?“
Vier Tools, von denen drei überhaupt keinen API-Schlüssel benötigen. Schreibgeschützt auf zwei unabhängigen Ebenen, paginierte Ergebnisse, SQLites eigene Fehlermeldungen werden an den Aufrufer zurückgegeben, und 74 automatisierte Tests.
Inhalt — Schnellstart · KI-Anbieter konfigurieren · Tools · Paginierung · Fehler · Tests · Docker · MCP-Clients · Konfiguration · Sicherheit · Datenabfluss · Projektstruktur
🚀 Schnellstart
1. Voraussetzungen
Node.js:
v22.5.0oder höher (für das eingebautenode:sqlite-Modul);v24empfohlennpm:
v11.0.0oder höher
2. Installation
Klonen Sie dieses Repository und installieren Sie die Abhängigkeiten:
npm install
cp .env.example .env
npm run buildDas reicht aus, um den Server mit einem Client zu verbinden und list_tables,
describe_table und execute_sql zu verwenden. Ein Anbieter wird nur für das
Tool für natürliche Sprache benötigt – siehe unten.
Related MCP server: shop-db MCP Server
🔑 KI-Anbieter konfigurieren
Öffnen Sie die .env-Datei und richten Sie Ihr bevorzugtes KI-Modell ein. Der Server erkennt Ihren Anbieter automatisch anhand der von Ihnen gesetzten Variablen:
Option A: Anthropic Claude (Empfohlen)
ANTHROPIC_API_KEY=sk-ant-api03-...
ANTHROPIC_MODEL=claude-opus-5Option B: Lokales Ollama (Kostenlos & Offline)
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3.2Hinweis: Stellen Sie sicher, dass Ollama läuft (
ollama serve) und Sie das Modell heruntergeladen haben (ollama pull llama3.2).
Option C: OpenAI
OPENAI_API_KEY=sk-proj-...
OPENAI_MODEL=gpt-4o-miniOption D: Benutzerdefiniert / Drittanbieter (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🛠 Verfügbare Tools
Drei der vier sprechen direkt mit SQLite – kein API-Schlüssel, keine Kosten, sofort:
Tool | Was es tut | Benötigt einen Anbieter |
| Jede Tabelle mit einer verständlichen Erklärung, was sie enthält, ihrer Zeilenanzahl und ihren Spalten, sowie die Beziehungen zwischen den Tabellen und die Umsatzkonvention, die diese Datenbank verwendet. | Nein |
| Eine Tabelle vollständig – Spalten mit Typen, Schlüsseln und Beschreibungen, Fremdschlüssel, die | Nein |
| Jede schreibgeschützte | Nein |
| Nimmt eine Frage in natürlicher Sprache, generiert und führt das passende SQL aus und gibt eine schriftliche Antwort mit Erkenntnissen zurück. | Ja |
Die Beschreibung jedes Tools sagt dem aufrufenden Agenten nicht nur, was es tut, sondern auch, wann
es nicht verwendet werden soll – query_database gibt an, dass es Prosa statt
Werte zurückgibt, Geld kostet und zwei LLM-Aufrufe macht, und verweist für alles,
was der Agent selbst berechnen möchte, auf execute_sql. Beide Tools geben das Zeilenlimit und die
Umsatzkonvention direkt an, sodass der Agent sie nicht durch Ausprobieren
herausfinden muss.
Beispiel: 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 ist vorhanden, damit ein Agent ein leeres Ergebnis von einer
Frage außerhalb des Bereichs unterscheiden kann: Eine Frage zu 2025 liefert „die Daten reichen von … bis …“
anstatt einer nackten Null, die wie ein Fehler wirkt.
📄 Paginierung großer Ergebnisse
Jedes Ergebnis ist begrenzt – auf DATABASE_MAX_ROWS (Standard 100) oder auf ein
kleineres limit, das Sie übergeben. Ein größeres limit wird abgeschnitten statt abgelehnt, sodass ein
Aufrufer immer Zeilen zurückbekommt.
execute_sql akzeptiert limit und offset und teilt Ihnen mit, ob es mehr gibt:
// 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
}Rufen Sie weiter mit offset: nextOffset auf, bis hasMore false ist. Wenn ein Ergebnis
in eine Seite passt, ist hasMore false und totalAvailableRows meldet die tatsächliche
Gesamtzahl.
Das Limit wird während des Durchlaufens der Anweisung durchgesetzt, nicht durch Abschneiden eines fertigen
Ergebnisses: Der Server stoppt eine Zeile über dem Limit und materialisiert den Rest nie.
Das SQL ist modellgeneriert, sodass ein versehentlicher Cross Join sonst Millionen
von Zeilen in den Speicher ziehen würde, bevor irgendwelche verworfen würden. Die Paginierung erfolgt ebenfalls
während der Iteration und nicht durch Anhängen von LIMIT/OFFSET an das SQL,
was überleben müsste, womit auch immer die generierte Anweisung bereits endet.
query_database teilt das Zeilenlimit, paginiert aber nicht – es fasst in Prosa
zusammen, wo eine Seitennummer nichts zu befestigen hat. Verwenden Sie execute_sql für alles,
was größer als eine Seite ist.
🚦 Wie ein Fehler aussieht
Fehler kommen als normale MCP-Toolergebnisse mit isError: true und einer Meldung
zurück, auf die der aufrufende Agent reagieren kann, und nicht als Transportfehler.
Sie senden | Sie erhalten |
|
|
|
|
|
|
|
|
Eine Anfrage in natürlicher Sprache zum Löschen von Daten |
|
Zwei Regeln bestimmen diesen Text:
SQLites eigene Meldung bleibt erhalten. „no such column: nope“ ist die nützlichste Information, die einem Agenten gegeben werden kann, weil sie ausreicht, um die Abfrage neu zu schreiben und erneut zu versuchen. Sie wird nie zu „Abfrage fehlgeschlagen“ vereinfacht.
Host-Details entkommen nie. Unerkannte Fehler – die einen Stacktrace enthalten können – werden auf eine generische Zeile reduziert, und alles, was nach außen geht, wird von Datenbankpfad, Projektstamm und Home-Verzeichnis bereinigt. Vollständige Details bleiben in den Server-Logs. Dies wird durch eine eigene Testdatei abgedeckt.
🧪 Automatisierte Tests
npm test # 74 tests across 4 files, runs in well under a second
npm run test:watch
npm run typecheckEinfaches node --test mit tsx – keine Test-Framework-Abhängigkeit. Die Suiten laufen
gegen die echte db/shop.db, nicht gegen einen Mock, sodass sie fehlschlagen, wenn Schema und
Dokumentation auseinanderdriften.
Datei | Abdeckung |
| Jede Möglichkeit, wie ein Schreibvorgang am Schreibschutz vorbeigeschmuggelt werden könnte: führende Kommentare, |
| Zeilenbegrenzung, |
| Was ein Aufrufer sehen darf: umsetzbare Meldungen kommen durch, unbekannte Fehler werden reduziert, und Datenbankpfad / Projektstamm / Home-Verzeichnis werden aus beiden entfernt. |
| Dass jede Tabelle und jede Spalte in der Live-Datenbank eine schriftliche Beschreibung hat, dass keine Beschreibung auf eine Tabelle verweist, die nicht mehr existiert, und dass die Umsatzkonvention angegeben ist. |
Die Guard-Suite ist die wichtigste: Sie ist die Grenze, die „schreibgeschützt“ wahr macht und nicht nur beabsichtigt, und einer ihrer Fälle ist ein echter Fehlalarm, den sie während der Entwicklung aufgedeckt hat.
🐳 Docker
docker build -t sql-mcp .Das Image enthält die Datenbank, benötigt also kein Volume-Mount. Da dies ein
Stdio-Server ist, muss er mit -i und ohne TTY ausgeführt werden – die Standardeingabe und
Standardausgabe des Containers transportieren den JSON-RPC-Stream:
docker run -i --rm -e ANTHROPIC_API_KEY sql-mcpBinden Sie ihn mit examples/claude_desktop_config.docker.json in einen Client ein.
Entfernen Sie -e ANTHROPIC_API_KEY, um ohne Anmeldedaten zu laufen – list_tables,
describe_table und execute_sql funktionieren ohne Anbieter.
Der Build ist mehrstufig: TypeScript wird in einem node:24-alpine-Builder kompiliert,
und nur dist/, db/ und Produktionsabhängigkeiten werden in das Laufzeitimage
kopiert. Es läuft als unprivilegierter node-Benutzer, es wird nie eine .env-Datei
hineinkopiert (Anmeldedaten kommen über -e), und es gibt keine nativen Addons zu kompilieren, weil
SQLite in Node selbst enthalten ist.
🔌 Verbindung zu MCP-Clients
Gebrauchsfertige Konfigurationsdateien finden Sie in examples/ – kopieren Sie die
zu Ihrem Client passende und ersetzen Sie den Pfad. examples/claude_desktop_config.no-api-key.json
führt den Server ohne Anmeldedaten aus, was für list_tables,
describe_table und execute_sql ausreicht.
Claude-Desktop-Konfiguration
Fügen Sie diesen Server zu Ihrer Claude-Desktop-Konfigurationsdatei (claude_desktop_config.json) hinzu:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
(Stellen Sie sicher, dass Sie vor dem Verbinden einmal npm run build ausführen)
Beispiel 1: Anthropic Claude (Standard)
{
"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"
}
}
}
}Beispiel 2: Lokales Ollama (Kostenlos & 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"
}
}
}
}Beispiel 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"
}
}
}
}Beispiel 4: Benutzerdefiniert / 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"
}
}
}
}Der Server löst db/shop.db relativ zu seinem eigenen Speicherort auf, sodass DATABASE_PATH
in keiner dieser Konfigurationen benötigt wird – MCP-Clients starten Server aus einem Arbeitsverzeichnis
ihrer eigenen Wahl, und der Server hängt nicht davon ab.
🔎 Lokal ausprobieren
Sofortiger Terminal-Test
Sie können Fragen in natürlicher Sprache direkt in Ihrem Terminal testen:
npm run query -- "Show top 3 products by price"Visueller Web-Inspektor
Testen Sie Tools interaktiv in Ihrem Browser mit dem offiziellen MCP-Inspektor:
npm run inspect:devÖffnen Sie die Inspektor-URL in Ihrem Browser (z. B.
http://localhost:5173).Klicken Sie auf Connect.
Wählen Sie unter Tools die Option
query_database, geben Sie Ihre Frage ein und klicken Sie auf Run Tool.
Alle npm-Skripte
Script | Funktion |
| Nach |
| Den gebauten Server über stdio ausführen |
| Aus dem Quellcode mit Reload ausführen ( |
| Automatisierte Tests |
|
|
| Eine Frage vom Terminal aus stellen |
| MCP Inspector gegen |
🔧 Konfigurationsreferenz
Jede Variable ist optional; die Standardwerte greifen, wenn nichts gesetzt ist.
Variable | Standard | Zweck |
|
| Datenbankpfad. Absolut oder relativ zum Projektstamm – niemals zum Arbeitsverzeichnis. |
|
| Harte Obergrenze für Zeilen pro Aufruf und für an das LLM gesendete Zeilen. |
|
| Zeitlimit pro Anfrage für LLM-Aufrufe. Eine Frage macht zwei sequenzielle Aufrufe, daher hängt ein blockierter Provider ohne dies den Tool-Aufruf auf. |
| automatisch erkannt |
|
| — / | Anthropic-Provider. |
| — / | OpenAI und jeder OpenAI-kompatible Endpunkt. |
|
| Lokales Ollama. |
| nicht gesetzt |
|
Ein fehlerhafter Wert wird auf stderr gemeldet und fällt auf den Standardwert zurück,
anstatt stillschweigend akzeptiert zu werden – ein Tippfehler im env-Block eines
Clients zeigt sich beim Start, statt sich so zu verhalten, als wäre die Variable nie
gesetzt worden. DEBUG-Logs enthalten jede gestellte Frage und jede generierte
Anweisung, und unter einem MCP-Client landen sie in den persistenten Logdateien des
Clients – sie bleiben also aus, sofern man sie nicht explizit aktiviert.
🔒 Sicherheit
Die Datenbank wird auf Treiberebene schreibgeschützt geöffnet, und jede Anweisung wird
vor der Ausführung validiert: Sie muss eine einzelne SELECT/WITH/VALUES-Anweisung
sein, ohne Schlüsselwort, das Daten schreibt, das Schema ändert oder den
Verbindungszustand verändert. Keine der beiden Prüfungen kann per Konfiguration
deaktiviert werden. Eine Anfrage wie "alle stornierten Bestellungen löschen" wird
abgelehnt statt ausgeführt.
Der Validator arbeitet über eine tokenisierte Sicht der Anweisung statt über den
rohen Text, sodass Kommentare, String-Literale und in Anführungszeichen gesetzte
Bezeichner nicht zum Verstecken eines Schlüsselworts genutzt werden können –
/* c */ DELETE FROM orders und WITH x AS (SELECT 1) DELETE FROM orders
werden beide abgelehnt, während SELECT replace(name, 'a', 'b') nicht abgelehnt wird.
Text, den dieser Server nicht geschrieben hat – Ihre Frage und aus der Datenbank
gelesene Werte – wird in den Prompts mit einem nicht fälschbaren, pro Anfrage
einzigartigen Marker abgegrenzt, sodass ein Produkt namens Widget (SYSTEM: ignore prior instructions…)
nicht in den Anweisungskontext entkommen kann. Das ist über diesen Prozess hinaus
relevant: Die Antwort reist als Tool-Ausgabe zurück zum aufrufenden Agenten, einen
Hop weiter.
🔐 Was wohin gesendet wird
Dieser Server beantwortet Fragen durch einen LLM-Aufruf, daher verlassen
Datenbankinhalte bei jedem query_database-Aufruf Ihren Rechner. Konkret sendet
jeder Aufruf:
Ihr Datenbankschema – Tabellennamen, Spaltennamen und -typen sowie Zeilenzahlen – um das SQL zu generieren.
Die vom Query zurückgegebenen Zeilen (bis zu
DATABASE_MAX_ROWS, Standard 100) – um sie in eine schriftliche Antwort umzuwandeln.
Bei der mitgelieferten Shop-Datenbank enthalten diese Zeilen Kundennamen,
E-Mail-Adressen und Telefonnummern. Sie gehen an den jeweils konfigurierten Provider,
an den Endpunkt, den OPENAI_BASE_URL benennt – was bei Groq, OpenRouter oder DeepSeek
ein Drittanbieter unter eigenen Bedingungen ist.
Falls das für Ihre Daten nicht akzeptabel ist:
Nutzen Sie die anderen drei Tools.
list_tables,describe_tableundexecute_sqltätigen überhaupt keinen Netzwerkaufruf – nichts verlässt den Rechner.Nutzen Sie Ollama. Es läuft lokal, daher verlässt nichts den Rechner.
Schränken Sie die Queries ein. Aggregatfragen („Umsatz nach Kategorie") liefern Zusammenfassungszeilen statt Kundendatensätze.
Senken Sie
DATABASE_MAX_ROWS, um zu begrenzen, wie viele Zeilendaten pro Query gesendet werden.
Der Server sendet niemals die Datenbankdatei, und er kann nur lesen – siehe Sicherheit.
📁 Projektstruktur
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📚 Technische Dokumentation
Technische Spezifikationen & Architekturdiagramme: Systemdesign, Sequenzdiagramme, LLM-Strategiemuster und Sicherheitsmechanismen.
Datenbankschema-Dokumentation: Vollständige Tabellenschemadefinitionen, ER-Diagramm und SQLite-Datenwörterbuch.
Client-Konfigurationsbeispiele: Welche Konfigurationsdatei kopiert werden soll und wie ohne API-Schlüssel ausgeführt wird.
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.
4 tool updates
v0.1.0- First observed
describe_table - First observed
execute_sql - First observed
list_tables - First observed
query_database
TDQS
Scored across 4 tools
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
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.
Text-to-SQL MCP server: read-only queries on PostgreSQL, MySQL and SQL Server with your real schema
Ask questions in plain language, get answers from your business database. No SQL required.
Related MCP Servers
- 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-
- FlicenseAqualityCmaintenanceEnables AI agents to analyze an SQLite e-commerce database via secure read-only SQL queries, providing tools for table inspection and analytical requests.2-
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to query an SQLite e-commerce database read-only, supporting analytical questions about customers, products, orders, and revenue.-