把 AI 代理接入数据库,大多数方案的第一步都一样:把连接字符串粘贴进配置文件。 mysql://root:hunter2@prod-db:3306/shop。从这一刻起,代理拥有了你拥有的一切——每一张表、 每一项权限,没有任何监督,而你的密码正以明文躺在磁盘上。

CroDB v2.1 反其道而行。它内置了 MCP 服务器,但代理拿不到连接字符串,拿不到密码,也根本无法 自己打开数据库连接。它能看到的,只是你已经打开、并且明确共享出来的那些连接;它想做的每一次 改动,都会停在一个需要你亲自过目的对话框前。

这篇文章说明它在实际使用中是什么样子,以及同样重要的——它的边界在哪里。

先说清楚 MCP 是什么

MCP(Model Context Protocol,模型上下文协议)是一套标准, 让 AI 客户端(Claude Code、Claude Desktop、Codex 等)能够发现并调用本地应用暴露出来的工具。 MCP 服务器声明一组工具,代理决定调用哪个、传什么参数,而服务器决定真正会发生什么。

最后半句是整个设计的落点。MCP 服务器不是 API 的一层包装,它是一道策略边界。无论模型决定请求 什么,说「可以」还是「不行」的,始终是服务器。

服务器住在 CroDB 里面

CroDB 没有提供一个独立的命令行程序去连你的数据库。MCP 服务器运行在 App 内部,复用 App 自己的 连接层。

这带来三个值得说清楚的结果:

凭据不会挪窝。 密码留在钥匙串里,也就是 CroDB 本来存放它们的地方。SSH 隧道是 CroDB 已经 建立的那一条。没有任何东西被复制进配置文件供 MCP 客户端读取。

代理继承的是你的会话,不是你的权限。 它只能通过你当前在 CroDB 中已打开的连接工作。关掉 连接,代理的访问能力随之消失。

安全规则和 App 本来就在执行的是同一套。 代理提交的 SQL 会走与 SQL 编辑器相同的分词器、语句 分类器和风险分析器。代理被要求达到 App 现有的标准——并且在此之上还要更严一层。

服务器只监听 127.0.0.1,默认端口 7657,并且要求 Bearer 令牌。本机回环并不等于可信:Mac 上 任何一个进程都能访问回环地址,所以令牌不是可选项。

执行之前的三道关卡

访问控制被刻意分成几层,任何单点失误都不足以造成后果。

第一道:授权。 每个已保存的连接都有一个代理访问级别——无访问权限只读、 读写。新建的连接默认是「无访问权限」,你升级之前就存在的连接也一样。没有被你主动共享的 连接,代理根本看不到:它不会出现在 list_connections 的结果里,就算知道它的 id 也没用。

第二道:连接必须是打开的。 共享出来的连接还必须正在 CroDB 中打开。代理不能替你去连接, 因为连接意味着读取密码。如果连接是关着的,代理得到的回复是「请让用户在 CroDB 里打开它」。

第三道:语句本身。 每一条语句在发送前都会被分类。在只读连接上,只接受 SELECT、 WITH … SELECTSHOWDESCRIBEVALUESTABLE 和 EXPLAIN——这份清单比「通常算读 操作的语句」要窄得多,因为像 CHECK 和游标类语句在某些引擎上做的事远不止读。

这道关卡拦下的,正是只看关键字会放过去的那些情况:

-- 拒绝:藏在 CTE 里的 DELETE,返回结果看起来和 SELECT 一样WITH moved AS (DELETE FROM orders WHERE id = 1 RETURNING *) SELECT * FROM moved -- 拒绝:EXPLAIN ANALYZE 会真正执行被包裹的语句EXPLAIN ANALYZE DELETE FROM orders -- 拒绝:会往服务器上写文件的 SELECTSELECT * FROM orders INTO OUTFILE '/tmp/orders.csv'

而且每次调用只允许一条语句。批量语句会被拒绝——你无法判断自己看不全的东西,而第二条语句正是 藏东西的地方。

代理能读到什么

一共十五个工具,大部分是只读的。实际最常用的是这几个:

  • describe_table——列(含类型与是否可空)、索引、外键、检查约束、触发器。一次调用, MySQL 和 PostgreSQL 返回的结构完全一致,比让模型自己去 information_schema 里拼出这些信息 便宜得多。
  • read_table——带分页、排序和过滤的读表,完全不需要写 SQL。这是查看数据的首选路径: 它不可能写错,也不可能碰到指定表以外的任何东西。
  • run_query——单条只读语句,用于 read_table 表达不了的联表和聚合。
  • explain_query——只拿执行计划,不执行查询。

还有一组 crodb:// 资源,可以把「这个库长什么样」作为上下文直接交给代理,省得它每一轮都花 工具调用重新摸一遍你的表结构。

结果是有上限的:200 行、单元格 4 KB、单次响应 256 KB、单条查询 15 秒,都可以调整。结果被截断 时,响应里会明确写出来。一个看起来完整、实际却不完整的答案,比没有答案更糟。

写入会停在对话框前

execute_statement 并不会执行任何东西。它把语句交给你。

CroDB 会原样展示这条 SQL,旁边是编辑器会给出的那套风险分析——没有 WHERE 的 UPDATE、 破坏性 SQL生产环境连接。你批准,或者你拒绝。在生产环境连接上,你还要先手动输入数据库 名称。用 Esc 关掉对话框算作拒绝——沉默绝不能被当成同意。

工具调用会等你大约 25 秒,足够覆盖「你就在电脑前」这个常见情况;超时之后它会给代理一张票据 去轮询,这样客户端等不下去也不会取消你正在做的判断。请求五分钟后过期。一个放了很久的批准 对话框是个陷阱:一小时之后没人还记得代理当时想干什么。

批准是主要的防线,但不是唯一的。有些语句无论谁批准都会被直接拒绝:

  • GRANTREVOKEREASSIGN——权限
  • CREATE / ALTER / DROP USER 和 ROLE——账号
  • SET GLOBAL——服务器全局配置
  • KILLSHUTDOWNFLUSHINSTALL——服务器状态
  • 任何触及服务器文件的语句:INFILEOUTFILEDUMPFILE

理由很直接:一个疲惫的人一路点「批准」,恰恰是被提示注入的代理最想要的结果,所以一次「同意」 能造成的影响范围,被限制在你眼前的那份数据上。提权不应该是点一下就能买到的东西。

真正值得采用的用法:事务

写入权限最好的用法,不是一条一条地批准语句,而是这样:

begin_transaction → 开一个独立会话,与你自己的标签页互不干扰execute_statement → 执行改动,批准一次read_table / run_query → 把改动的结果读回来 (在事务内部,因此能看到自己尚未提交的行)commit_transaction → 你拿着真实的影响行数,来决定要不要提交

在事务内部,代理能看到自己写入但尚未提交的行——这正是「改完之后,准确告诉我改了什么」得以成立 的前提。事务内的写入会返回 "durable": false,所以代理没法诚实地宣称改动已经落地。

回滚立即执行,不需要批准——丢弃自己尚未提交的工作,不需要征得任何人同意。提交需要批准,因为 那是不可回头的一步。如果你拒绝了提交,事务会保持打开,仍然可以被回滚。

代理事务跑在自己的会话上,永远不会碰到你在自己标签页里开着的事务。如果退出 App 时还有代理事务 没提交,CroDB 会逐个报出连接名,然后才回滚它们。

被低估的那个工具:交回给人

open_in_editor 会把代理写的 SQL 放进一个新的 CroDB 查询标签页,并且什么都不执行。

它在只读连接上同样可用,而这正是重点。一个只读的代理照样可以给你写迁移脚本、回填脚本、清理 脚本——然后由你来运行,在你自己的编辑器里,用你自己的眼睛看过。对大多数值得做的改动来说, 这比批准对话框是更好的流程:同样经过审阅,上下文更完整,而且不必赶时间。

open_table 对数据做同样的事:直接打开数据网格,展示代理正在描述的那些行。

让发生过的事情看得见

一个能读你数据库的 MCP 服务器,绝不应该是隐形的。

窗口工具栏上有一个实时状态指示器:无共享时是灰色,代理可读时是绿色,有事需要你处理时是橙色, 端口和数量显示在悬停提示里。点击它直接打开「AI 代理」设置。

在这之下,每一次调用——成功、被拒绝、失败——都会进入设置里的活动列表;代理执行的每一条语句 也会和你自己的语句一起存进查询历史。另外还有一个按钮,断开所有代理:停止服务器、拒绝所有 等待批准的请求、回滚代理留下的任何未提交事务。

如何配置

设置 → AI 代理中打开服务器,把至少一个连接设为只读,然后复制生成的配置。CroDB 会为 你的客户端生成完全对应的片段,端口和令牌都已经填好。

Claude Code 走 HTTP:

claude mcp add --transport http crodb http://127.0.0.1:7657/mcp \ --header "Authorization: Bearer <token>"

Claude Desktop 和 Codex 启动一个小的 stdio 桥接程序——也就是以 --mcp-stdio 模式运行 的 CroDB 可执行文件,它只做传输转发,把协议交给正在运行的 App:

[mcp_servers.crodb]command = "/Applications/CroDB.app/Contents/MacOS/CroDB"args = ["--mcp-stdio"]env = { CRODB_MCP_TOKEN = "…", CRODB_MCP_PORT = "7657" }

因为 CroDB 运行在沙盒里,无法写入其他 App 的配置文件,所以它生成文本、由你粘贴。内置四种预设: Claude Code、Claude Desktop、Codex,以及给其他任何支持 MCP 的客户端用的通用说明。

同时还附带四个提示词——optimize_queryexplain_schemawrite_migration、 investigate_slow_query——每一个都在引导代理拿证据说话,而不是靠猜。其中「写迁移」的提示词 明确要求代理不要使用 execute_statement:迁移属于你自己的版本化工具链,不该在聊天窗口里 临时执行掉。

它不做什么

  • CroDB 必须在运行。 服务器住在 App 里。App 没开时 stdio 桥接会把它拉起来,但没有无界面 的独立运行模式。
  • 代理不能打开连接。 这是刻意的,也不会改。打开连接意味着读取密码。
  • 没有任何数据离开你的 Mac。 没有账号、没有云端、没有遥测。服务器绑定在回环地址上,没有 令牌一律拒绝。
  • 结果默认是不完整的。 上限的存在有其理由,但一个无视截断提示的代理,会基于残缺的数据做 推理。提示写得很明确,只是模型未必总是仔细的读者。
  • 行数据是不可信输入。 数据库里的文本可以被写成看起来像指令的样子。CroDB 限制了返回量, 并把它标注为数据,而且没有任何写入会在无人值守时发生——但提示注入是一个真实存在的问题, 这套设计缩小的是影响范围,而不是消除问题本身。
  • 批准疲劳是真实的。 这套设计尽量让每一次打扰都值得:读操作从不打断你;事务把一整组改动 收敛成一次决定;open_in_editor 的存在,就是为了让常规改动根本不需要弹窗。如果你发现自己 在不看内容地点「批准」,那是一个信号——把这类工作挪回编辑器。

为什么是这个形态

数据库类的 MCP 服务器已经有很多了。它们几乎都是接收一个连接字符串,然后把原始 SQL 交给模型 ——这让它们成为一根通往生产环境的、细而带凭据的管道。

CroDB 不想做一根更快的管道。它想做的是那个让人始终留在环路里的地方:代理可以广泛地读、自由地 提议,但在一个理解后果的人点头之前,什么都改不了。数据库客户端本来就握有连接、凭据、风险分析 和审计记录。把 MCP 服务器放在它里面、而不是放在它旁边,正是安全的那个版本得以成立的原因。

CroDB v2.1 起提供,需要 macOS 15 及以上,支持 MySQL 与 PostgreSQL。