SQL MCP Server
SQL MCP 서버
AI 기반 Model Context Protocol (MCP) 서버로, 자연어를 사용하여 전자상거래 SQLite 데이터베이스를 쿼리하고 분석할 수 있습니다.
다음과 같은 질문을 해 보세요:
"총 지출 기준 상위 5명의 고객은 누구인가요?"
"재고가 50개 미만인 Electronics 카테고리의 모든 제품을 보여주세요"
"2026년 완료된 주문의 총 매출은 얼마였나요?"
네 가지 도구 중 세 가지는 API 키가 전혀 필요 없습니다. 두 가지 독립적인 수준에서 읽기 전용이며, 페이지 매김 결과, SQLite 자체 오류 텍스트를 호출자에게 전달하고, 74개의 자동화된 테스트가 있습니다.
목차 — 빠른 시작 · AI 공급자 구성 · 도구 · 페이지 매김 · 오류 · 테스트 · Docker · MCP 클라이언트 · 구성 · 안전 · 데이터 송신 · 프로젝트 구조
🚀 빠른 시작
1. 사전 요구 사항
Node.js:
v22.5.0이상(내장node:sqlite모듈용);v24권장npm:
v11.0.0이상
2. 설치
이 저장소를 클론하고 의존성을 설치합니다:
npm install
cp .env.example .env
npm run build이 정도면 서버를 클라이언트에 연결하고 list_tables, describe_table, execute_sql을 사용할 수 있습니다. 자연어 도구에만 공급자가 필요합니다 — 아래를 참조하세요.
Related MCP server: shop-db MCP Server
🔑 AI 공급자 구성
.env 파일을 열고 선호하는 AI 모델을 설정하세요. 서버는 설정한 변수를 기반으로 공급자를 자동으로 감지합니다:
옵션 A: Anthropic Claude (권장)
ANTHROPIC_API_KEY=sk-ant-api03-...
ANTHROPIC_MODEL=claude-opus-5옵션 B: 로컬 Ollama (무료 및 오프라인)
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3.2참고: Ollama가 실행 중인지(
ollama serve) 모델을 가져왔는지(ollama pull llama3.2) 확인하세요.
옵션 C: OpenAI
OPENAI_API_KEY=sk-proj-...
OPENAI_MODEL=gpt-4o-mini옵션 D: 사용자 지정 / 타사 (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🛠 사용 가능한 도구
네 가지 중 세 가지는 SQLite에 직접 연결됩니다 — API 키 없음, 비용 없음, 즉시:
도구 | 기능 | 공급자 필요 |
| 모든 테이블과 그 내용에 대한 평이한 설명, 행 수와 열, 테이블 간 관계, 이 데이터베이스가 사용하는 매출 규칙. | 아니요 |
| 한 테이블 전체 — 열(유형, 키, 설명 포함), 외래 키, | 아니요 |
| 모든 읽기 전용 | 아니요 |
| 평이한 질문을 받아 적절한 SQL을 생성 및 실행하고, 통찰력이 포함된 서면 답변을 반환합니다. | 예 |
각 도구의 설명은 호출 에이전트에게 도구가 무엇을 하는지뿐만 아니라 언제 사용하지 말아야 하는지도 알려줍니다 — query_database는 값이 아닌 산문을 반환하고, 비용이 들며, 두 번의 LLM 호출을 수행한다고 명시하고, 에이전트가 계산하려는 모든 것에 대해 execute_sql을 가리킵니다. 두 도구 모두 행 상한과 매출 규칙을 인라인으로 명시하므로 에이전트가 시행착오로 발견할 필요가 없습니다.
예: 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는 에이전트가 빈 결과와 범위를 벗어난 질문을 구분할 수 있도록 하기 위한 것입니다. 2025년에 대해 묻는 경우 버그처럼 보이는 단순한 0 대신 "데이터는 …부터 …까지"를 반환합니다.
📄 대용량 결과 페이지 매김
모든 결과는 DATABASE_MAX_ROWS(기본값 100) 또는 전달한 더 작은 limit으로 제한됩니다. 더 큰 limit은 거부되지 않고 잘리므로 호출자는 항상 행을 받습니다.
execute_sql은 limit과 offset을 받고 더 많은 데이터가 있는지 알려줍니다:
// 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
}hasMore가 false가 될 때까지 offset: nextOffset으로 계속 호출하세요. 결과가 한 페이지에 맞으면 hasMore는 false이고 totalAvailableRows는 실제 총계를 보고합니다.
상한은 문을 실행하는 동안 적용되며 완료된 결과를 잘라내는 것이 아닙니다. 서버는 상한보다 한 행 더 멈추고 나머지를 구체화하지 않습니다. SQL은 모델 생성이므로 우발적인 교차 조인은 버려지기 전에 수백만 행을 메모리로 가져올 수 있습니다. 페이지 매김도 SQL에 LIMIT/OFFSET을 추가하는 대신 반복 중에 수행되며, 생성된 문이 이미 끝나는 방식에 관계없이 작동해야 합니다.
query_database는 행 상한을 공유하지만 페이지 매김은 하지 않습니다 — 산문으로 요약하며, 페이지 번호가 붙을 대상이 없습니다. 한 페이지보다 큰 것은 execute_sql을 사용하세요.
🚦 오류 형태
실패는 전송 수준 오류가 아닌 일반 MCP 도구 결과로 isError: true와 호출 에이전트가 조치할 수 있는 메시지로 반환됩니다.
보내는 내용 | 받는 내용 |
|
|
|
|
|
|
|
|
데이터 삭제를 요청하는 자연어 요청 |
|
이 텍스트를 지배하는 두 가지 규칙:
SQLite 자체 메시지가 보존됩니다. "no such column: nope"는 에이전트에게 가장 유용한 정보로, 쿼리를 다시 작성하고 재시도하기에 충분합니다. "query failed"로 평탄화되지 않습니다.
호스트 세부 정보는 절대 유출되지 않습니다. 인식할 수 없는 오류(스택 추적을 포함할 수 있음)는 일반적인 한 줄로 축소되고, 나가는 모든 것은 데이터베이스 경로, 프로젝트 루트, 홈 디렉터리에서 삭제됩니다. 전체 세부 정보는 서버 로그에 남습니다. 이는 자체 테스트 파일로 다룹니다.
🧪 자동화 테스트
npm test # 74 tests across 4 files, runs in well under a second
npm run test:watch
npm run typecheck일반 node --test와 tsx — 테스트 프레임워크 의존성 없음. 스위트는 실제 db/shop.db에 대해 실행되며 목이 아닙니다. 따라서 스키마와 문서가 어긋나면 실패합니다.
파일 | 내용 |
| 쓰기가 읽기 전용 가드를 우회할 수 있는 모든 방법: 선행 주석, |
| 행 상한, |
| 호출자가 볼 수 있는 것: 실행 가능한 메시지는 통과하고, 알 수 없는 오류는 축소되며, 데이터베이스 경로 / 프로젝트 루트 / 홈 디렉터리가 둘 다에서 삭제됩니다. |
| 라이브 데이터베이스의 모든 테이블과 열에 서면 설명이 있고, 더 이상 존재하지 않는 테이블을 참조하는 설명이 없으며, 매출 규칙이 명시되어 있는지. |
가드 스위트가 가장 중요합니다. "읽기 전용"을 의도가 아닌 사실로 만드는 경계이며, 그 사례 중 하나는 개발 중에 발견한 실제 오탐입니다.
🐳 Docker
docker build -t sql-mcp .이미지에는 데이터베이스가 포함되어 있으므로 볼륨 마운트가 필요 없습니다. stdio 서버이므로 -i와 TTY 없이 실행해야 합니다 — 컨테이너의 stdin과 stdout이 JSON-RPC 스트림을 전달합니다:
docker run -i --rm -e ANTHROPIC_API_KEY sql-mcpexamples/claude_desktop_config.docker.json으로 클라이언트에 연결하세요. 자격 증명 없이 실행하려면 -e ANTHROPIC_API_KEY를 제거하세요 — list_tables, describe_table, execute_sql은 공급자 없이 작동합니다.
빌드는 다단계입니다. TypeScript는 node:24-alpine 빌더에서 컴파일되고, dist/, db/ 및 프로덕션 의존성만 런타임 이미지에 복사됩니다. 권한 없는 node 사용자로 실행되며, .env는 절대 복사되지 않고(자격 증명은 -e에서 옴), SQLite가 Node 자체에 포함되어 있으므로 컴파일할 네이티브 애드온이 없습니다.
🔌 MCP 클라이언트 연결
바로 사용할 수 있는 구성 파일은 examples/에 있습니다 — 클라이언트에 맞는 파일을 복사하고 경로를 바꾸세요. examples/claude_desktop_config.no-api-key.json은 자격 증명 없이 서버를 실행하며, list_tables, describe_table, execute_sql에 충분합니다.
Claude Desktop 구성
이 서버를 Claude Desktop 구성 파일(claude_desktop_config.json)에 추가하세요:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
(연결 전에 npm run build를 한 번 실행하세요)
예 1: Anthropic Claude (기본)
{
"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"
}
}
}
}예 2: 로컬 Ollama (무료 및 오프라인)
{
"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"
}
}
}
}예 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"
}
}
}
}예 4: 사용자 지정 / 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"
}
}
}
}서버는 db/shop.db를 자체 위치 기준으로 해석하므로 DATABASE_PATH는 이 중 어느 것에도 필요하지 않습니다 — MCP 클라이언트는 서버를 자체 선택한 작업 디렉터리에서 시작하며, 서버는 이에 의존하지 않습니다.
🔎 로컬에서 시도하기
즉시 터미널 테스트
터미널에서 자연어 질문을 직접 테스트할 수 있습니다:
npm run query -- "Show top 3 products by price"시각적 웹 검사기
공식 MCP Inspector를 사용하여 브라우저에서 도구를 대화형으로 테스트하세요:
npm run inspect:dev브라우저에서 검사기 URL을 엽니다(예:
http://localhost:5173).Connect를 클릭합니다.
Tools 아래에서
query_database를 선택하고 질문을 입력한 후 Run Tool을 클릭합니다.
모든 npm 스크립트
스크립트 | 기능 |
|
|
| 빌드된 서버를 stdio로 실행 |
| 소스에서 실행 (리로드 포함, |
| 자동화된 테스트 |
|
|
| 터미널에서 질문하기 |
|
|
🔧 구성 참조
모든 변수는 선택 사항입니다. 기본값은 아무것도 설정하지 않았을 때 실행되는 값입니다.
변수 | 기본값 | 용도 |
|
| 데이터베이스 위치. 절대 경로 또는 프로젝트 루트 기준 상대 경로 — 작업 디렉터리 기준은 안 됩니다. |
|
| 호출당 반환되는 행 수와 LLM으로 전송되는 행 수의 상한. |
|
| LLM 호출당 요청 상한. 질문 하나는 순차 호출 두 번을 하므로, 이 값이 없으면 응답이 없는 공급자가 도구 호출을 멈추게 합니다. |
| 자동 감지 |
|
| — / | Anthropic 공급자. |
| — / | OpenAI 및 OpenAI 호환 엔드포인트. |
|
| 로컬 Ollama. |
| 설정 안 됨 |
|
잘못된 값은 stderr에 보고되고 기본값으로 대체되며, 조용히 수용되지 않습니다 — 클라이언트의 env 블록에 있는 오타는 변수가 설정되지 않은 것처럼 동작하는 대신 시작 시 표시됩니다. DEBUG 로그에는 묻는 모든 질문과 생성된 모든 문장이 기록되며, MCP 클라이언트에서는 클라이언트의 영구 로그 파일에 저장되므로, 명시적으로 선택하지 않는 한 꺼져 있습니다.
🔒 안전
데이터베이스는 드라이버 수준에서 읽기 전용으로 열리며, 모든 문장은 실행 전에 검증됩니다: 단일 SELECT/WITH/VALUES 문이어야 하며, 데이터를 쓰거나, 스키마를 변경하거나, 연결 상태를 바꾸는 키워드가 없어야 합니다. 두 검사 모두 구성으로 끌 수 없습니다. "삭제된 모든 주문 삭제" 같은 요청은 실행되지 않고 거부됩니다.
검증기는 원시 텍스트가 아닌 문장의 토큰화된 뷰에서 작동하므로, 주석, 문자열 리터럴, 따옴표로 묶인 식별자로 키워드를 숨길 수 없습니다 — /* c */ DELETE FROM orders와 WITH x AS (SELECT 1) DELETE FROM orders는 모두 거부되지만, SELECT replace(name, 'a', 'b')는 거부되지 않습니다.
이 서버가 작성하지 않은 텍스트 — 사용자의 질문과 데이터베이스에서 읽은 값 — 는 프롬프트에서 위조할 수 없는 요청별 마커로 구분되므로, Widget (SYSTEM: ignore prior instructions…) 같은 제품 이름이 명령 컨텍스트로 빠져나갈 수 없습니다. 이는 이 프로세스 너머에서도 중요합니다: 답변은 도구 출력으로 호출 에이전트에게 돌아가며, 한 홉 더 이동합니다.
🔐 무엇이 어디로 전송되는가
이 서버는 LLM을 호출하여 질문에 답하므로, query_database 호출마다 데이터베이스 콘텐츠가 사용자의 머신을 떠납니다. 구체적으로 각 호출은 다음을 전송합니다:
데이터베이스 스키마 — 테이블 이름, 열 이름과 유형, 행 수 — SQL을 생성하기 위해.
쿼리가 반환한 행 (
DATABASE_MAX_ROWS까지, 기본 100) — 이를 서면 답변으로 바꾸기 위해.
번들로 제공되는 shop 데이터베이스의 경우 해당 행에는 고객 이름, 이메일 주소, 전화번호가 포함됩니다. 이들은 구성한 공급자, OPENAI_BASE_URL이 가리키는 엔드포인트로 전송됩니다 — Groq, OpenRouter 또는 DeepSeek의 경우 자체 약관이 적용되는 제3자입니다.
데이터에 대해 이것이 허용되지 않는 경우:
다른 세 가지 도구를 사용하세요.
list_tables,describe_table,execute_sql은 네트워크 호출을 전혀 하지 않습니다 — 아무것도 머신을 떠나지 않습니다.Ollama를 사용하세요. 로컬에서 실행되므로 아무것도 머신을 떠나지 않습니다.
쿼리를 제한하세요. 집계 질문("카테고리별 매출")은 고객 레코드 대신 요약 행을 반환합니다.
DATABASE_MAX_ROWS를 낮추세요. 쿼리당 전송되는 행 데이터의 양을 제한합니다.
서버는 데이터베이스 파일을 절대 전송하지 않으며, 읽기만 가능합니다 — 안전 참조.
📁 프로젝트 구조
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📚 기술 문서
기술 사양 및 아키텍처 다이어그램: 시스템 설계, 시퀀스 다이어그램, LLM 전략 패턴, 안전 메커니즘.
데이터베이스 스키마 문서: 전체 테이블 스키마 정의, ER 다이어그램, SQLite 데이터 사전.
클라이언트 구성 예시: 복사할 구성 파일과 API 키 없이 실행하는 방법.
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.-