mcpField
D
DB Explorer

DB Explorer

本地服务
github.com/AYUSH-148数据库71 周前更新

关于 DB Explorer

DB Explorer 是面向 PostgreSQL、MySQL 和 SQLite 的只读 MCP 服务,让 AI 编码助手检查数据库结构、查询数据并审视迁移。它在解析层拒绝非单条只读 SELECT,并限制结果行数,同时提供执行计划和索引建议。

主要功能

  1. 01检查表、视图、列、键和索引
  2. 02解析并执行受限的 SELECT 查询
  3. 03查看原生执行计划
  4. 04检测模式问题并建议索引
  5. 05验证迁移脚本但不执行

适用场景

  1. 01用自然语言探索关系数据库结构
  2. 02查询数据并排查慢查询
  3. 03识别缺失主键或外键索引
  4. 04起草并检查数据库迁移

MCP 相关

MCP 服务为客户端提供 7 个数据库工具,通过 SQLAlchemy 连接 PostgreSQL、MySQL 和 SQLite。支持本地 stdio,以及带令牌认证的远程 HTTP;查询安全检查在访问数据库前执行。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"ayush-148-db-explorer": {
"command": "uvx",
"args": [
"db-explorer-mcp-server"
],
"env": {
"DATABASE_URL": "<DATABASE_URL>"
}
}
}
}
启动命令uvx db-explorer-mcp-server
需要配置的环境变量
DATABASE_URL密钥SQLAlchemy URL of the database to explore. Unset opens the bundled demo database.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypidb-explorer-mcp-server0.1.0

工具

7

收录的工具列表 · 更新于 1 周前

参数类型必填说明
limitinteger否每页的表数,范围为 1 到 1000。
detailboolean否列出每个表的列、键和索引,而不是列数。若问题涉及多个表,请使用此参数,而不是对每个表分别调用一次 table_name。页面会变大,因此请使用 name_pattern 缩小较大架构的范围。
offsetinteger否要跳过的表数。传入上一页的 next_offset。
table_nameany否某个表或视图的准确名称。返回其列、键和索引;对于表,还会返回行数。此时会忽略列表参数。
name_patternany否按名称筛选表,不区分大小写。普通文本匹配子字符串('order');*、? 和 [] 用于 glob 匹配('order_*')。
include_sample_databoolean否与 table_name 一起使用时,还会返回 3 条示例行。

详细介绍

DB Explorer MCP

CI M8ven Score

一个只读的 Model Context Protocol(MCP)服务,支持 PostgreSQL、MySQL 和 SQLite,可让 AI 编码助手探索、查询和审计关系数据库,同时绝无写入数据库的可能。

将 MCP 客户端指向数据库,然后用自然语言提问。客户端的大模型负责编写 SQL;此服务会解析 SQL,拒绝任何非单条只读 SELECT 语句,在行数上限内执行查询,并返回结构化结果。此外还提供架构检查、执行计划、索引建议和迁移审查功能。

MCP client LLM  ->  FastMCP tools  ->  safety layer  ->  SQLAlchemy  ->  database
   (writes SQL)       (7 tools)       (rejects writes)   (any dialect)

此服务本身不会调用任何大模型 API,因此无需配置 API 密钥——推理由你连接的客户端完成。通过 SQLAlchemy 支持 SQLite、PostgreSQL 和 MySQL。

经过测试和评估。 每次推送和拉取请求都会运行 336 项测试,CI 中还会针对真实的 PostgreSQL 和 MySQL 额外运行 26 项测试(测试)。在一项 Agent 评估中,Claude 通过这些工具完成了 14 项中的 14 项真实数据库任务,没有任何工具调用失败,每项任务平均调用 2 次,独立评审的平均得分为 4.6 / 5(Agent 评估)。

原因

向助手提供原始数据库凭据,一次误解指令或遭到提示词注入的交互就可能删除表。使用只读副本则会失去架构上下文和分析执行计划的能力。此服务采用折中方案:提供完整的检查和查询能力,同时在解析器层面从结构上杜绝数据修改,而不是寄希望于模型遵守要求。

架构

┌──────────────────────────────────────────────────────────────┐
│  MCP client  (Claude Code / Claude Desktop / Inspector)      │
│  owns the LLM: reads schema, authors SQL, interprets results │
└───────────────────────────┬──────────────────────────────────┘
                            │  MCP  ·  stdio (local)
                            │        ·  streamable HTTP + bearer token (remote)
┌───────────────────────────▼──────────────────────────────────┐
│ server.py  —  FastMCP instance + one engine per token group  │
│                                                              │
│   explore_schema   execute_query    explain_query            │
│   validate_schema  suggest_index    migration_context        │
│   validate_migration                                         │
└──────┬───────────────────────┬───────────────────┬───────────┘
       │                       │                   │
       │  read path            │  metadata path    │  review path
       │                       │                   │
┌──────▼────────────┐  ┌───────▼─────────┐  ┌──────▼──────────┐
│ safety.py         │  │ inspector.py    │  │ migration.py    │
│ ── trust boundary │  │ schema_health.py│  │ parses up/down, │
│ sqlparse AST      │  │ index_suggest.py│  │ never executes  │
│ SELECT-only       │  │ explain.py      │  │                 │
│ 1 stmt · no cmnts │  │                 │  │                 │
│ denylist · LIMIT  │  │                 │  │                 │
└──────┬────────────┘  └───────┬─────────┘  └─────────────────┘
       │                       │
       └───────────┬───────────┘
                   │  SQLAlchemy Core
┌──────────────────▼───────────────────────────────────────────┐
│  Target database   ·   PostgreSQL  /  MySQL  /  SQLite       │
└──────────────────────────────────────────────────────────────┘

传入 safety.py 的 SQL 由客户端模型编写,因此不可信。服务会解析 SQL,而不是进行模式匹配;被拒绝的查询绝不会到达数据库。

模式传输方式身份验证
本地stdio无需身份验证;由客户端启动服务
远程streamable HTTPbearer 令牌,或在 Prefect Horizon 的平台边缘进行 OAuth 身份验证

两种模式运行相同的工具代码——只有 MCP_TRANSPORT 不同。

快速开始

需要 Python 3.11+ 和 uv。

从 PyPI 安装

无需克隆。uvx 会下载此软件包并在独立环境中运行:

claude mcp add db-explorer --env DATABASE_URL="postgresql+psycopg2://user:pass@localhost:5432/example" -- uvx db-explorer-mcp-server

省略 --env,即可先尝试内置的演示数据库。在其他客户端的配置中,使用 "command": "uvx" 和 "args": ["db-explorer-mcp-server"]。

从源码安装
git clone https://github.com/AYUSH-148/DB-Explorer-MCP
cd DB-Explorer-MCP
uv sync
uv run python seed_demo_db.py   # creates sample.db, a small shop with deliberate schema flaws
uv run pytest                   # 336 tests on SQLite; 26 more with TEST_*_URL set

在 Windows 上,如果 uv 不在 PATH 中,请在命令前加上 py -m(py -m uv sync)。

你无需自行启动服务:MCP 客户端会通过 stdio 启动它,因此请继续阅读连接客户端。手动运行时,uv run server.py 不会输出任何内容,并会等待客户端通过 stdin 连接;按 Ctrl+C 停止。

默认数据库为 sample.db。通过 DATABASE_URL 指定你自己的数据库,可在下方的客户端配置中设置,也可以复制 .env.example 到 .env 文件后设置:

postgresql+psycopg2://user:password@localhost:5432/example
mysql+pymysql://user:password@localhost:3306/example
sqlite:///C:/data/example.db

密码中的特殊字符需要进行百分号编码(@ → %40,# → %23,/ → %2F)。

连接客户端

Claude Code — 本地
claude mcp add db-explorer --env DATABASE_URL="postgresql+psycopg2://user:pass@localhost:5432/example" -- uv --directory "C:/path/to/DB-Explorer-MCP" run server.py

然后在会话中运行 /mcp,确认列出了 7 个工具。添加 -s user,即可在所有项目中使用。

Claude Code — 远程
claude mcp add --transport http db-explorer https://your-deployment.fastmcp.app/mcp

运行 /mcp → Authenticate,完成 OAuth 流程;令牌会自动缓存和刷新。

如果使用自己托管且带 bearer 令牌的服务,请通过请求头发送令牌。从环境变量中读取令牌,这样提交到团队代码库的 .mcp.json 就不会包含机密信息:

{
  "mcpServers": {
    "db-explorer": {
      "type": "http",
      "url": "https://dbx.example.com/mcp",
      "headers": { "Authorization": "Bearer ${DBX_TOKEN}" }
    }
  }
}
Claude Desktop

本地配置,写入 claude_desktop_config.json:

{
  "mcpServers": {
    "db-explorer": {
      "command": "uv",
      "args": ["--directory", "C:/path/to/DB-Explorer-MCP", "run", "server.py"],
      "env": { "DATABASE_URL": "postgresql+psycopg2://user:pass@localhost:5432/example" }
    }
  }
}

如需在不使用自定义连接器的情况下访问远程部署,可通过 stdio 代理:

{
  "mcpServers": {
    "db-explorer": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://your-deployment.fastmcp.app/mcp"]
    }
  }
}
VS Code

.vscode/mcp.json 已纳入版本控制,并通过 Windows py 启动器使用 stdio 启动服务。在 macOS 或 Linux 上,将其 command 改为 uv,并将 args 改为 ["run", "server.py"]。

MCP Inspector
npx @modelcontextprotocol/inspector

使用传输方式 Streamable HTTP 和你的 /mcp URL,或使用 stdio 运行 uv run server.py。Inspector 会显示原始工具响应和未经转述的错误,因此能最快分辨问题出在服务还是客户端。

Python
import asyncio
from fastmcp import Client

async def main():
    async with Client("https://your-deployment.fastmcp.app/mcp", auth="oauth") as client:
        print([tool.name for tool in await client.list_tools()])
        print(await client.call_tool("explore_schema", {}))

asyncio.run(main())

试用

连接到 Quickstart 中的演示数据库 sample.db 后,可以直接使用以下提示词:

  • “有哪些表?哪些表缺少主键?”
  • “显示 orders 中总额最高的 5 行。”
  • “为什么这个查询很慢?SELECT * FROM orders WHERE created_at >= '2026-01-01'”
  • “这个数据库中哪些外键缺少索引?给出相应的 CREATE INDEX 语句。”
  • “起草一个迁移,为 orders 添加 shipped_at 列,然后验证 up 和 down 脚本。”

要查看安全防护措施是否生效,可以要求它运行 DELETE FROM users。调用会因 Unsafe query blocked: Only SELECT queries are allowed. Got: DELETE 而失败,数据库不会受到影响。

Agent 技能

skill/db-explorer/SKILL.md 是一项可选的 Agent 技能,旨在教模型如何高效使用这些工具:查询前先读取一次架构,在 SQL 中计算总数,而不是根据返回的行计算;在结果为 truncated 时明确说明;遵循错误提示,而不是反复重试。它只会指导模型,服务仍必须保持连接。

对于 Claude Code,可将 skill/db-explorer 文件夹复制到每个项目的 ~/.claude/skills/db-explorer/,或复制到某个项目内的 .claude/skills/db-explorer/。

工具

工具参数返回内容
explore_schematable_name?、include_sample_data=false、name_pattern?、detail=false、limit=200、offset=0用于编写 SQL 的 dialect,以及表和视图列表(每项包含 kind 和列数);或单个关系的 kind、列、主键、外键、索引、行数(仅表)和最多 3 行示例数据。单个关系的详情或 detail=true 时的分页结果还会附带数据库中存储的表和列的 comment
execute_querysql、row_limit=100(最多 1000)一条经过验证的 SELECT 的 columns、rows、count、truncated 和实际生效的 row_limit;如果结果中有重复列名,还会返回 duplicate_columns 和 note(重复项会标记为 id:1,这不是有效的 SQL);如果为控制在大小限制内而截断了行,也会返回 note
explain_querysql原生执行计划和解析出的 dialect
validate_schematable_name?、name_pattern?、limit=200、offset=0包含 severity、code、message、suggestion 的架构问题;未指定 table_name 时,返回一页表(跳过视图),分页方式与 explore_schema 相同
suggest_indexquery? 异或 table_name?附有理由的 CREATE INDEX 建议
migration_contextname_pattern?、limit=200、offset=0用于客户端起草迁移的方言及一页列、键和索引(不含行数);分页方式与 explore_schema 相同
validate_migrationup_sql、down_sql按脚本列出已解析的语句类型;会拒绝 SELECT、注释和括号不平衡的语句。这不是语法检查,且绝不会执行

validate_schema 会报告四种代码:missing_primary_key、unindexed_foreign_key、wide_table(50 列及以上)和 no_indexes。如果任何索引、主键或唯一约束以外键的列开头,则该外键计为已建立索引;部分索引、GIN/GiST/BRIN 索引以及 FULLTEXT/SPATIAL 索引不计入。

explore_schema 默认开销很低:不带参数时会返回表名和列数。只有指定 table_name 时才会返回行数(仅限表,最多 100,000 行,达到上限时会标记 row_count_capped: true)。可使用 name_pattern 缩小大型架构的范围(order 会匹配名称中包含该字符串的项,order_* 是 glob 模式),使用 limit/offset 分页,并设置 detail=true,一次调用即可获取整页的列、键和索引。视图和物化视图会与表一同列出,每项都包含 kind。

安全模型

每次 execute_query、explain_query 和 suggest_index 调用都会先经过 safety.py 检查,再访问数据库。查询必须满足以下所有条件,否则会被拒绝:

  • 单条语句。 SELECT 1; DROP TABLE users → Exactly one SQL statement is required
  • 仅允许 SELECT,根据解析后的语句类型判断,而不是检查字符串前缀 → Only SELECT queries are allowed. Got: DELETE
  • 不允许 SQL 注释。 在字符串字面量和带引号的名称之外,拒绝 --、/*、*/,以堵住注释注入途径。仅在 MySQL 和 MariaDB 上拒绝 #,因为它在那里用于开始注释。
  • 整个令牌流中不允许出现被屏蔽的关键字: ALTER、CREATE、DELETE、DROP、EXEC、EXECUTE、GRANT、INSERT、INTO、REVOKE、TRUNCATE、UPDATE。按完整令牌匹配,因此 SELECT grant_date FROM permissions 可以通过。其中 4 个关键字至关重要:INSERT、UPDATE 和 DELETE 可能隐藏在 PostgreSQL 数据修改型 CTE 中,而语句类型仍是 SELECT;SELECT * INTO archive FROM users 则会创建表。其余关键字不能出现在类型为 SELECT 的语句中;如果 sqlparse 的类型检测出现回归,它们仍可作为第二道防线。
  • 不允许锁定子句。 拒绝 FOR UPDATE、FOR NO KEY UPDATE、FOR SHARE、FOR KEY SHARE 和 MySQL 的 LOCK IN SHARE MODE,因为锁定读取会阻塞其他写入操作。按子句匹配,而不是按关键字匹配,因此名为 share 的列仍可正常使用。

每个通过检查的查询都会被包装为 SELECT * FROM (<your query>) AS limited_query LIMIT <row_limit + 1>,因此无界扫描不会让客户端上下文被大量数据淹没。你在查询中指定的 LIMIT 会缩小内部结果集,但 row_limit(上限为 1000)仍会限制返回的数据量。如果返回了额外的探测行,该行会被丢弃,结果中会带有 "truncated": true,因此达到上限的结果不会被误认为完整结果。

验证只是三层防护中的第一层,因为关键字屏蔽列表无法识别语法正确但仍有害的查询:

  • 语句超时。 SELECT pg_sleep(600) 可以通过上述所有检查,因此需要独立于语法来限制执行时间:PostgreSQL 使用 statement_timeout(按事务设置,因此在 PgBouncer、Supabase 和 Neon 连接池后也能生效),MySQL 使用 max_execution_time,SQLite 使用进度处理器截止时间。通过 db.py 中的 QUERY_TIMEOUT_SECONDS 配置。

  • 只读事务。 PostgreSQL 通过 BEGIN READ ONLY 执行读取,MySQL 使用 START TRANSACTION READ ONLY,SQLite 使用 PRAGMA query_only。数据库会拒绝写入操作,这是关键字屏蔽列表无法提供的保障。

  • 权限。 这是最外层的防线 — 参见 .env.example。只有 SELECT 权限的用户才能阻止 pg_read_file() 这类服务器端文件读取,而任何关键字检查都无法可靠地捕获这类操作。

    权限也用于限制可读取的内容。令牌可以读取其所属组的数据库用户有权 SELECT 的所有内容,因此应为每个组分配独立的用户(参见命名令牌和组)。应通过授权来隐藏凭据和 PII,而不是使用服务器端的表或列屏蔽列表:解析器无法识别 SELECT *、SELECT u FROM users u 或 row_to_json(u) 背后的列,但数据库会对这些列一一强制执行授权。

    -- PostgreSQL: whole tables, minus one
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO explorer;
    REVOKE SELECT ON users FROM explorer;
    -- then only the safe columns of it
    GRANT SELECT (id, name, created_at) ON users TO explorer;
    

    MySQL 使用相同的列列表:GRANT SELECT (id, name) ON app.users TO 'explorer'@'%';。如果查询访问了已撤销授权的列,就会失败并返回 sql_error(permission denied)。

每次工具调用都会向 stderr 上的 db_explorer.audit 日志记录器写入一行:工具、结束状态(ok 或错误代码)、调用者(HTTP 请求中的 user=<token name> group=<group>)及其参数,参数长度上限为 2000 个字符。参数会按原样记录,因此应将日志视为与数据同等敏感。

错误

工具错误只有在告知模型接下来该怎么做时才有用,因此每次失败都会附带错误代码和修正提示,而不是仅给出一条消息:

[table_not_found] Table not found: usrs. Did you mean: users.
Hint: Call explore_schema() to list the tables in this database.

[sql_error] The database rejected the query: no such column: totl.
Hint: Call explore_schema(table_name=...) to confirm the table and column names before retrying.

[query_timeout] The query exceeded the statement timeout of 15s: interrupted.
Hint: Add a WHERE clause, aggregate instead of scanning, or query a smaller table.
代码原因
table_not_found不存在此表。会附带数据库中最接近的匹配表名
sql_error数据库拒绝了查询——例如缺少列、类型不匹配或语法错误
query_timeout查询运行时间超过 QUERY_TIMEOUT_SECONDS,因此被取消
database_unavailable服务无法连接数据库,或连接在查询过程中断开。详细信息仅写入服务日志
unsafe_query被 safety.py 拦截。提示会指出触发的具体规则
query_too_complexSQL 超过 50,000 个字符,或对解析器而言过长或嵌套过深(例如,IN (...) 中包含约 3,000 个值)
too_many_concurrent_calls调用方的令牌已有 MAX_CONCURRENT_CALLS_PER_CLIENT 个调用正在运行
unknown_group调用方的令牌指定了一个没有数据库登录信息的服务端组
invalid_argument参数超出范围(例如 row_limit 小于 1),或参数不符合工具的输入架构:缺少必填参数、类型错误或名称未知
missing_argument / conflicting_argumentssuggest_index 需要且只能提供 query 或 table_name 中的一个
comments_not_allowed / unparsable_sql / select_in_migrationvalidate_migration 拒绝了脚本
server_busyDB_POOL_TIMEOUT_SECONDS 内所有数据库连接都在使用中;未运行任何查询。请原样重试
internal_error服务端发生错误。回溯信息会写入服务日志;调用方只能看到错误代码

配置

变量默认值说明
DATABASE_URLconfig.py 旁边的 sample.db,只读(仅 stdio)当 MCP_TRANSPORT 不是 stdio 时为必填;否则启动会明确报错
MCP_TRANSPORTstdiostdio、streamable-http 或 sse
MCP_HOST127.0.0.1仅用于 HTTP 传输
MCP_PORT8000仅用于 HTTP 传输
QUERY_TIMEOUT_SECONDS15单条语句的最长运行时间,也是整个调用的时间预算:预算耗尽后,不会再启动任何语句。必须为正整数
DB_POOL_SIZE5保持打开的数据库连接数
DB_MAX_OVERFLOW10负载较高时额外打开的连接数;归还后关闭
DB_POOL_TIMEOUT_SECONDS5调用等待空闲连接的最长时间;超时后以 server_busy 失败
MAX_CONCURRENT_CALLS_PER_CLIENT5单个令牌可同时运行的工具调用数(按服务进程计算);stdio 不受限制
MCP_AUTH_TOKEN—一个共享令牌。通过 HTTP 提供服务时,必须设置此项或 MCP_AUTH_TOKENS,包括由主机自行加载 server.py:mcp 的情况。至少 32 个字符
MCP_AUTH_TOKENS—命名令牌,格式为 name:token 或 name:group:token,以逗号分隔。请用此项替代 MCP_AUTH_TOKEN,不要同时使用。参见命名令牌和服务端组
DATABASE_URL_<GROUP>—某个令牌组的数据库 URL,例如 DATABASE_URL_ANALYST。MCP_AUTH_TOKENS 中列出的每个服务端组都必须设置此项
MCP_ALLOW_UNAUTHENTICATEDfalse明确选择不要求令牌,适用于可信网络,或前置了主机自身身份验证的情况(Horizon)

同时持有连接的调用最多为 DB_POOL_SIZE + DB_MAX_OVERFLOW(默认 15)个;超出此数量的调用会等待 DB_POOL_TIMEOUT_SECONDS,然后以 server_busy 失败。所有服务实例共用的连接池总量应低于数据库的 max_connections。

SQLite 回退方案仅用于本地开发:打开 config.py 旁边的 sample.db,且为只读模式。启动时会加载 .env(先加载 config.py 旁边的文件,再加载工作目录中的文件);真实环境变量的优先级高于两者。

通过 HTTP 提供服务

$env:MCP_TRANSPORT = "streamable-http"
$env:MCP_HOST = "0.0.0.0"
$env:MCP_PORT = "8000"
$env:DATABASE_URL = "postgresql+psycopg2://user:password@host:5432/example"
$env:MCP_AUTH_TOKEN = python -c "import secrets; print(secrets.token_urlsafe(32))"
uv run server.py

macOS 或 Linux 上的命令相同:

export MCP_TRANSPORT=streamable-http MCP_HOST=0.0.0.0 MCP_PORT=8000
export DATABASE_URL="postgresql+psycopg2://user:password@host:5432/example"
export MCP_AUTH_TOKEN="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
uv run server.py

HTTP 端点会向任何能访问该端口的人开放已配置数据库中的 SELECT,因此如果未设置 MCP_AUTH_TOKEN 或 MCP_AUTH_TOKENS,服务会拒绝启动,而不是在未受保护的情况下启动。客户端通过 Authorization: Bearer <token> 发送令牌;系统会在 auth.py 中以恒定时间进行比较。在确实可信的网络上,可设置 MCP_ALLOW_UNAUTHENTICATED=true 以覆盖此行为——此时服务会在每次启动时向 stderr 发出警告。

直接加载 server.py:mcp 的主机(Horizon、fastmcp run)在未设置令牌或 MCP_ALLOW_UNAUTHENTICATED=true 时,会对每个 HTTP 请求返回 401。

绑定到环回地址时,服务会拒绝外部的 Host 和 Origin 标头,防止网页通过 DNS 重绑定访问本地服务。此传输是无状态的,因此负载均衡器后的任何副本都可以响应任何请求。GET /health 无需令牌即可返回 {"status": "ok"},供负载均衡器探测使用;它绝不会访问数据库。

命名令牌和组

为每个人或服务设置独立令牌,还可以选择一个组,由该组的数据库用户决定其可读取的内容:

MCP_AUTH_TOKENS=alice:analyst:<token>,carol:analyst:<token>,fran:finance:<token>,ci:<token>
DATABASE_URL=postgresql+psycopg2://dbx_readonly:...@db/app          # tokens with no group (ci)
DATABASE_URL_ANALYST=postgresql+psycopg2://dbx_analyst:...@db/app
DATABASE_URL_FINANCE=postgresql+psycopg2://dbx_finance:...@db/app

每个条目的格式为 name:token 或 name:group:token。令牌名称会出现在每条审计记录中;令牌所属的组决定调用使用哪个数据库登录账号,因此由数据库自身的授权(以及 PostgreSQL 行级安全策略)决定调用者可以读取的内容。单独设置 MCP_AUTH_TOKEN 时,它会作为一个名为 default、不属于任何组的令牌。

如果条目格式错误、名称或令牌重复、令牌长度不足 32 个字符、某组没有对应的 DATABASE_URL_<GROUP>,或者同时设置了 MCP_AUTH_TOKEN 和 MCP_AUTH_TOKENS,服务都会拒绝启动。令牌会以恒定时间进行比较,并在启动时读取:更改令牌后需要重启。每个组都有自己的连接池。

在 PostgreSQL 上,explore_schema 会列出某个组无法 SELECT 的表(所有人都可以读取目录),但这些表的行仍受保护。DEPLOYMENT.md 中包含两个数据库所需的 GRANT 语句、Horizon 上的 OAuth,以及使用 HTTPS 的虚拟机配置。

**托管 Supabase 说明:**直接连接(db.<ref>.supabase.co)仅支持 IPv6,因此在仅支持 IPv4 的容器中会失败,并显示内容为空的 psycopg2.OperationalError。请使用控制面板 Connect 面板中的连接池主机,并注意用户名会变为 postgres.<project-ref>。

测试

uv run pytest

336 项测试覆盖安全层、序列化、只读限制与超时、HTTP 身份验证和令牌组、每个工具以及错误报告。每项测试都使用临时 SQLite 数据库,因此测试套件无需凭据,也无需运行中的服务。

tests/test_live_databases.py 覆盖 SQLite 无法测试的内容——只读事务、语句超时、NUMERIC/TIMESTAMP 值、目录反射——并使用真实的 PostgreSQL 和 MySQL 进行测试。设置 TEST_POSTGRES_URL 和/或 TEST_MYSQL_URL,即可针对临时数据库运行测试(测试会创建并删除 dbx_live_* 表)。CI 会运行这两种测试。

Agent 评测

测试检查每个工具;评测则检查模型能否熟练使用这些工具。evals/run.py 会通过无头模式下的 Claude Code(claude -p),使用此服务作为唯一工具来源,对 evals/scenarios.py 中的每个任务运行测试,并使用预置的 evals/eval.db。每次运行会根据工具选择、调用预算以及从数据库中读取的答案是否正确进行评分;另一次 claude -p 调用会对整个运行结果进行 1 到 5 分的评分。

uv run python evals/seed_eval_db.py      # build evals/eval.db (the runner does this if it is missing)
uv run python evals/run.py               # every scenario, on Sonnet
uv run python evals/run.py --only top_customers --model opus

需要在 PATH 中安装 claude CLI 并完成登录;运行会计入该登录账号的使用限额,且无需 API 密钥。不同运行的结果会有所差异,因此评测不属于 CI。报告会写入 evals/results/。

最新结果

Claude Sonnet 5.5 作为 Agent 和评审模型,2026-10-14。完整报告(包括每次工具调用及其结果):evals/reports/2026-10-14-sonnet.json。

14/14 通过 · 0 次工具错误 · 0 次重复调用 · 总计 28 次工具调用 · 评审均分 4.64 / 5

任务Agent 选择的工具调用次数 / 预算评审
列出表和视图explore_schema1/35/5
按状态统计订单explore_schema、execute_query2/45/5
按已交付订单金额列出消费最高的 3 位客户explore_schema、execute_query3/54/5
收入最高的类别explore_schema、execute_query2/55/5
平均评分最高的类别explore_schema、execute_query2/55/5
从未下过订单的用户explore_schema、execute_query2/45/5
最近的 5 笔订单及客户姓名explore_schema、execute_query2/45/5
“用户表”的行数(实际名称:users)explore_schema、execute_query2/45/5
为什么带日期筛选条件的查询很慢explore_schema、explain_query2/45/5
审计架构中的结构性问题validate_schema、explore_schema2/44/5
reviews 上哪些外键需要索引suggest_index、explore_schema2/34/5
添加 phone 列的迁移migration_context、validate_migration2/45/5
“删除所有已取消的订单”(必须拒绝:只读)explore_schema、execute_query2/54/5
使用 pro 计划的用户explore_schema、execute_query2/34/5

所有只有一个正确答案的结果都经过数据库核验,而不只是由评审模型评判。得分为 4 的原因是多调用了一次工具,或回答中加入了问题之外的额外说明。

已知限制:explore_schema 目前不会报告 UNIQUE 或 CHECK 约束,因此在架构审计中,模型可能会误称某个已存在的约束缺失。

项目结构

server.py         FastMCP instance, engine, and the 7 tool definitions
safety.py         query validation and row-limited execution
db.py             engine construction, statement timeouts, read-only transactions
serialization.py  driver values to JSON-safe primitives
auth.py           bearer-token verification for HTTP transports
inspector.py      schema reflection (columns, PK, FKs, indexes, samples)
explain.py        dialect-aware EXPLAIN
index_suggest.py  index recommendations from plans or FK metadata
schema_health.py  objective schema issue reporting
indexes.py        index coverage shared by schema_health and index_suggest
migration.py      migration context and non-executing script validation
errors.py         coded errors with hints, and driver-error classification
config.py         environment configuration with fail-fast checks
seed_demo_db.py   builds the demo sample.db used in Quickstart
skill/            optional agent skill for clients that load SKILL.md files
tests/            pytest suite over temporary SQLite databases
evals/            agent evals: a Claude model works scripted tasks through the tools

设计说明与限制

  • 查询模式下的 suggest_index 会标记全表扫描,而不是一般意义上的索引缺失。 它会指出发生扫描的地方,而不会编写 CREATE INDEX,因为应该为哪个列创建索引取决于查询。对小表进行扫描是预期行为,不是问题。
  • 只检查默认架构:PostgreSQL 的 search_path 中第一个架构(通常为 public),或 MySQL 中 DATABASE_URL 指定的数据库。若 SQL 中指定了架构,execute_query 仍会读取 billing.invoices。如需检查其他 PostgreSQL 架构,请将其设为首选:ALTER ROLE explorer SET search_path = billing, public;。
  • 某些只读查询形式会被拒绝。 sqlparse 无法解析带括号的 (SELECT 1)、VALUES (1) 或 TABLE users,因此会拒绝这些查询,并提示改写为普通的 SELECT。
  • SQL 长度上限为 50,000 个字符;超过 sqlparse 的令牌数或嵌套层数限制时,调用会失败并返回 query_too_complex。
  • 行数上限用于控制上下文,而非执行成本。 即使是开销很大的聚合查询仍会完整运行;QUERY_TIMEOUT_SECONDS 用于限制其执行时间。
  • 长文本和大型结果会被截断。 超过 2000 个字符的文本值会被截断返回,并注明完整长度。查询结果达到 500,000 字节时会停止返回,并设置 truncated;架构分页也会在此处停止,并通过 next_offset 继续。
  • 未经 MariaDB 测试。 它与 MySQL 共用代码路径,但可能会丢弃行数上限子查询中的 ORDER BY;在 MariaDB 上执行带排序的查询时,请自行添加 LIMIT。
  • 不超过 256 字节的二进制值会以十六进制编码返回;更大的值只会报告大小。
  • 较大的 NUMERIC 值会以字符串形式返回,这样可以保留精确数字,不会发生舍入。

许可证

MIT。详见 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。