Skip to main content
Glama
lampmaster

shop-sql-mcp

by lampmaster

shop-sql-mcp

一个小型 MCP 服务器,通过 stdio 为 AI 代理提供对 shop.db SQLite 数据库的只读分析访问。

该服务器只做三件事,不多做:它列出表、描述表结构,并在每次调用时运行一条只读 SQL 语句,且由服务器强制分页。所有推理——做哪些 JOIN、如何聚合、何时查看 schema——都由代理负责。

AI Agent
    |
    |  MCP over stdio
    v
shop-sql-mcp
    |
    +-- list_tables
    +-- describe_table
    +-- query_database
    |
    v
read-only SQLite connection
    |
    v
shop.db

要求

  • Node.js 22.5 或更新版本(推荐 24+)。该服务器使用内置的 node:sqlite 模块,所以没有需要编译的原生 SQLite 依赖。

  • 没有其他运行时依赖。

Related MCP server: MCP SQLite Server (Read-Only)

安装

npm install

配置

配置是可选的。默认情况下,服务器在项目根目录打开 shop.db。

变量

默认值

含义

DATABASE_PATH

<project>/shop.db

SQLite 文件的路径。相对路径基于项目根目录解析,因此服务器不依赖于启动时的工作目录。

如果想保留本地覆盖配置,请将 .env.example 复制为 .env。服务器本身读取普通环境变量;.env.example 中的 ANTHROPIC_API_KEY、EVAL_MODEL 和 EVAL_MAX_STEPS 仅由 npm run eval 使用。

构建

npm run build

将 src/ 编译到 dist/。

运行

npm start              # runs the built server (dist/index.js)
npm run dev            # runs src/index.ts directly, no build step

服务器在 stdin/stdout 上进行 MCP 通信,并且只在 stderr 上打印诊断信息,因此在终端中运行它看起来像卡住了——这是正常现象。它应由 MCP 宿主启动。

连接 MCP 代理

将下面内容添加到你的 MCP 宿主配置中(Claude Desktop 的 claude_desktop_config.json、Claude Code 的 .mcp.json,或你的宿主对应的配置文件),并使用项目的绝对路径:

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

若要直接从源码运行而不先构建,请指向 TypeScript 入口点——Node 会直接执行它:

{
  "mcpServers": {
    "shop-sql": {
      "command": "node",
      "args": ["/absolute/path/to/shop-sql-mcp/src/index.ts"]
    }
  }
}

要读取其他位置的数据库:

{
  "mcpServers": {
    "shop-sql": {
      "command": "node",
      "args": ["/absolute/path/to/shop-sql-mcp/dist/index.js"],
      "env": { "DATABASE_PATH": "/absolute/path/to/other.db" }
    }
  }
}

对于 Claude Code,你还可以从命令行注册它:

claude mcp add shop-sql -- node /absolute/path/to/shop-sql-mcp/dist/index.js

工具

list_tables

无参数。返回用户表;内部 sqlite_* 表会被隐藏。

{
  "tables": [
    { "name": "customers" },
    { "name": "order_items" },
    { "name": "orders" },
    { "name": "products" }
  ]
}

describe_table

{ table: string }

实时从 SQLite 读取表结构——没有硬编码内容——报告列、类型、可空性、主键和外键:

{
  "table": "order_items",
  "columns": [
    { "name": "id", "type": "INTEGER", "nullable": false, "primaryKey": true },
    { "name": "order_id", "type": "INTEGER", "nullable": false, "primaryKey": false }
  ],
  "foreignKeys": [
    { "column": "order_id", "referencesTable": "orders", "referencesColumn": "id" },
    { "column": "product_id", "referencesTable": "products", "referencesColumn": "id" }
  ]
}

未知名称是可恢复的错误,不会导致崩溃:

{ "error": { "code": "TABLE_NOT_FOUND", "message": "TABLE_NOT_FOUND: Table \"foo\" does not exist." } }

注意:INTEGER PRIMARY KEY 列会被报告为 nullable: false。SQLite 的 table_info 会返回不同的结果,但这种列是 rowid 的别名,永远不可能为 NULL。

query_database

{ sql: string; limit?: number; offset?: number }

运行一条只读语句——SELECT ... 或 WITH ... SELECT ...——支持 JOIN、WHERE、GROUP BY、HAVING、ORDER BY、子查询、聚合以及日期过滤。

{
  "columns": ["category", "revenue"],
  "rows": [["Electronics", 1234567.89]],
  "returnedRows": 1,
  "limit": 100,
  "offset": 0,
  "hasMore": false
}

行是按照 columns 顺序排列的值数组。这让结果数据保持紧凑,并且在查询产生两个同名列时也不会产生歧义。

失败会作为普通工具结果返回,设置 isError,并附带简短、可操作的信息,让代理能够修复其 SQL 并重试:

{ "error": { "code": "SQL_ERROR", "message": "no such column: total" } }

错误代码:SQL_ERROR、READ_ONLY_VIOLATION、MULTIPLE_STATEMENTS、TABLE_NOT_FOUND、INVALID_ARGUMENT、DATABASE_UNAVAILABLE。堆栈跟踪永远不会返回。

分页

分页由服务器强制执行,而不是由模型的 SQL 控制。

  • limit 默认为 100,最大 500;offset 默认为 0。

  • 代理的查询会被包装为 SELECT * FROM (<your sql>) LIMIT ? OFFSET ?,因此即使查询自带 LIMIT 100000,返回的行数也不会超过 limit。

  • 服务器内部会获取 limit + 1 行来判断 hasMore,不需要第二次计数查询,并且最多返回 limit 行。

  • 因此单次调用永远不会返回超过 500 行,这样宽范围的 SELECT * 就不会淹没模型的上下文。

要分页遍历结果,请保持 SQL 不变(使用确定性的 ORDER BY),并在 hasMore 为 true 时让 offset 按 limit 递增。

只读安全性

两层独立防护,因此没有任何一层需要单独负责。

1. SQL 验证(src/sqlSafety.ts)。一个小型词法分析器跳过注释、字符串字面量和带引号的标识符,然后要求:

  • 语句以 SELECT 或 WITH 开头——简单的 startsWith("SELECT") 会拒绝合法的只读 CTE;

  • 只能有一条语句(第一个 ; 之后的所有内容都会被拒绝,字面量或注释中的 ; 不算分隔符);

  • 任何地方都不能出现禁止的关键字——包括嵌套在 CTE 内部:INSERT、UPDATE、DELETE、CREATE、DROP、ALTER、REPLACE、ATTACH、DETACH、VACUUM、REINDEX、PRAGMA、ANALYZE、BEGIN、COMMIT、ROLLBACK、SAVEPOINT、load_extension、writable_schema。

禁止的 SQL 总是会被显式错误拒绝——绝不会被静默忽略,也绝不会被部分执行。REPLACE(a, b, c) 作为标量函数仍然是允许的,因为只有 REPLACE INTO 语句才是写操作。

2. SQLite 连接本身。shop.db 使用 new DatabaseSync(path, { readOnly: true }) 打开。即使写操作绕过 SQL 验证,SQLite 也会以 "attempt to write a readonly database" 拒绝它。测试套件通过绕过验证器直接在连接上执行写操作来验证这种行为。

错误或禁止的查询会作为工具错误返回,并且绝不会导致进程终止,因此会话可以承受任意多次失败尝试。

运行测试

npm test

只运行 确定性 测试套件——无网络、无 API 密钥、无 LLM。Node 内置测试运行器直接执行 TypeScript 源码。覆盖范围包括:list_tables、describe_table(列、类型、可空性、主键、外键、未知表)、简单 SELECT、过滤、聚合、连接、GROUP BY、只读 CTE、日期过滤、分页(默认 limit、最大 limit、offset、hasMore 边界)、无效 SQL、未知列和表、拒绝 INSERT/UPDATE/DELETE/CREATE/DROP/ALTER/REPLACE/ATTACH/DETACH/VACUUM/REINDEX/PRAGMA 和多个语句、证明每次写入被拒绝后数据库内容完全一致,以及通过 stdio 进行端到端 MCP 调用确认服务器在错误后仍然可用。

手动运行 eval

export ANTHROPIC_API_KEY=sk-...
npm run eval

请手动启动它。 它特意被排除在 npm test 之外,因为它会通过 stdio 驱动真实 LLM 对接真实 MCP 服务器,并产生付费 API 调用。

它会启动服务器,把三个 MCP 工具以及一个 submit_answer 工具交给模型,后者的 JSON schema 针对每个任务固定,并将结构化答案与直接从 SQLite 计算的参考值进行比较——而不是与自然语言文本进行比较。任务覆盖:表发现、多步 schema 发现、过滤、聚合、连接、客户总消费、客户订单数、产品销量、类别收入、2025 年收入,以及一个必须被拒绝的破坏性请求(检查还会验证数据库在之后没有被修改)。

可选配置:EVAL_MODEL(默认为 claude-sonnet-5)和 EVAL_MAX_STEPS(默认为 12)。如果任何任务失败,退出码非零。

目录结构

src/
  index.ts       MCP server: tool registration, stdio wiring, error shaping
  db.ts          read-only connection, path resolution, row/value normalisation
  tools.ts       the three tools: list_tables, describe_table, query_database
  sqlSafety.ts   single-statement read-only SQL validation
tests/
  sqlSafety.test.ts   validator, allowed and forbidden SQL
  tools.test.ts       tools against the real shop.db
  mcp.test.ts         end-to-end over stdio with a real MCP client
eval/
  tasks.ts       eval tasks and their SQLite reference values
  run.ts         LLM + MCP eval runner (manual)
shop.db

依赖

包

说明

@modelcontextprotocol/server

官方 MCP TypeScript SDK(v2)。提供 McpServer 和 stdio 传输,因此协议不是手动实现的。

zod

SDK 用于工具输入/输出 schema 的依赖;它向代理发布机器可读的参数类型。

typescript, @types/node

仅开发依赖:构建和类型检查。

@modelcontextprotocol/client

仅开发依赖:官方 MCP 客户端,用于 stdio 端到端测试和 eval 运行器。

SQLite 来自 Node 内置的 node:sqlite,测试来自 Node 内置的测试运行器,eval 的 HTTP 调用来自内置的 fetch——没有安装任何数据库驱动、ORM、查询构建器、Web 框架、日志库、测试框架、SQL 解析器或 LLM SDK。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Exposes any SQLite database as read-only MCP tools for AI assistants, enabling listing tables, describing schemas, and running SELECT queries with filtering, ordering, and pagination.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to read-only analyze a SQLite e-commerce database, exploring schema and running analytical SQL queries over stdio.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI models to securely analyze SQLite datasets and read local text documents through a local stdio interface. It provides read-only querying, schema discovery, pagination, and timeout protections to prevent data leaks or destructive changes.
    MIT