默认只读 · 写权限由配置声明

database-cli

面向 Agent 的数据库排查 Skill。让 Agent 能查数据库,同时不必把生产库的写权限交到一次提示词的判断上。

v1.1.2 MIT Python 3.10+ Codex / Claude Code
1执行入口
4层写入护栏
6种数据库
11个 MCP 工具

问题

读是低风险的,写不是

Agent 排查线上问题时需要读数据库:找表、看字段、按业务键查记录、对比环境。但一旦同一个入口也能写,风险就不再由「这次查询对不对」决定,而由「护栏是否真的存在」决定。

database-cli 把执行收在一个入口上:scripts/db-query 是唯一真实执行路径,负责 SQL 安全检查、结果集上限、连接管理和审计。MCP 只是它之上的一层可选适配器,不是第二个查询引擎。

安全模型

写入需要四个条件同时成立

其中最关键的一个刻意放在配置文件里——拼命令的一方无法自行主张写权限。

默认只读

只放行无副作用的起始关键字。DDL、权限、事务、存储过程、锁、导出,以及带副作用的函数一律拦截。

环境须在配置声明可写

环境必须写明 "writable": true 才接受 --allow-write。这个声明存在配置文件里,Agent 无法通过构造命令绕过;临时连接没有配置项承载它,需要另传 --writable,因此也无法把受保护的库改写成临时连接来规避。

单次调用显式开关

在上一条成立的前提下,仍需为每次写入显式传入 --allow-writeUPDATE / DELETE 必须带 WHERE

影响面硬上限

执行前先在服务端 COUNT 真实影响行数,超过 max_write_rows(默认 1000)直接拒绝。WHERE 1=1 这类看似有 WHERE、实则全表的语句不会放行。

默认放行

SELECTSHOWDESC DESCRIBEEXPLAINWITH … SELECT

满足全部条件后额外放行

INSERTUPDATE DELETEREPLACE

始终拦截

CREATE / ALTER / DROP / TRUNCATE GRANT / REVOKE BEGIN / COMMIT / ROLLBACK CALL / EXEC LOCK / UNLOCK SELECT … FOR UPDATE EXPLAIN ANALYZE + DML 多语句
为什么 EXPLAIN ANALYZE 对 DML 也拦: MySQL 8.0.18+ 会真的执行被分析的语句,而首关键字是 EXPLAIN 会让它绕过所有按起始 token 生效的检查——WHERE 强制要求、影响面上限、以及审计的读写判定。

护栏演示

被拦下来是什么样

下面的输出取自真实执行,不是示意。点标签切换场景,也会自动轮播。

db-query

场景 ① 与 ② 都以退出码 2 拒绝,数据库上没有发生任何变更。场景 ③ 不需要写权限。

快速开始

四步跑通

依赖一个外部工具:sq,负责实际的数据库连接与输出格式化。

1 · 安装

brew install sq
git clone https://github.com/CassianFlorin/database-cli.git
cd database-cli

2 · 检查环境就绪状态

只读本地配置与 sq 可用性,不连数据库:

scripts/db-query --setup-status

3 · 添加一个只读连接

凭据写入被 gitignore 的本地配置;生产环境建议用 --password-env 而非明文:

scripts/install \
  --env qa01 \
  --url "mysql://db-qa01.example.internal:3306/orders" \
  --username readonly_user \
  --password-env QA01_DB_PASSWORD \
  --non-interactive

4 · 先查元数据,再查数据

# 跨可见 schema 搜索字段名
scripts/db-query --env qa01 --search-objects "%order_no%" --object-type column

# 按业务键取记录
scripts/db-query --env qa01 \
  --sql "SELECT id, order_no, status FROM orders.cc_order WHERE order_no = 'A1001'"

修数流程

默认不执行,先产出可审的修复包

前置检查、变更、回滚、后置检查——四件事在一条命令里生成,供人工复核。

STEP 01

preview-write

返回真实影响行数与写前快照。不需要任何写权限。行数与预期不符时应当停下。

STEP 02

generate-rollback

从写前镜像生成反向 SQL,只生成不执行。字面量按目标 driver 转义。

STEP 03

repair

把四步编排成一条命令。默认 executed: false,加 --allow-write 才执行。

# 先看影响面,无需写权限
scripts/db-query --env qa01 --preview-write "UPDATE cc_order SET status = 1 WHERE order_no = 'A1001'"

# 生成回滚 SQL(只生成,不执行)
scripts/db-query --env qa01 --key-columns id \
  --generate-rollback "UPDATE cc_order SET status = 1 WHERE order_no = 'A1001'"

# 完整修复包
scripts/db-query --env qa01 --key-columns id \
  --repair "UPDATE cc_order SET status = 1 WHERE order_no = 'A1001'"
字面量按 driver 转义有实际原因:MySQL 系把反斜杠当转义符、标准 SQL 不当,同一套规则无法同时正确。因此 driver 未知时宁可拒绝,也不产出可能错位的回滚语句。
修复包不是事务原子的。它把「先验证影响面、留好回滚、执行、再验证」这套人工流程固化成一条命令,而不是替代事务。

MCP 适配层

薄适配器,同一套安全边界

给需要结构化工具调用的客户端准备的可选入口,全部委托给同一个 CLI。

setup_status

就绪状态、缺失前置条件与下一步动作,不连数据库。

list_envs · add_connection

列出连接;运行中新增连接无需重启客户端。

search_objects · inspect

搜索 schema、表、字段、索引、存储过程;查看结构。

query_readonly · execute_sql

只读查询;后者可在获得授权后执行 DML。

preview_write · generate_rollback

影响面预览与回滚生成,均不需要写权限。

repair · check_sql

完整修复包;以及不执行的 SQL 安全校验。

适配层刻意保持很薄:它不实现查询逻辑,也不暴露 CLI 没有的能力。add_connection 有意不支持声明写权限——否则运行中的 Agent 就能给自己开写权限,配置侧的声明也就失去了意义。

支持范围

支持的数据库

通过 sq 连接。也可以直接复用已有的 sq source handle。

数据库driver说明
MySQL / MariaDBmysql mariadb元数据搜索走 INFORMATION_SCHEMA
PostgreSQLpostgres元数据搜索走 information_schemapg_catalog
SQLitesqlite3文件路径连接,端到端测试即用它
DuckDBduckdb文件路径连接
SQL Serversqlserver元数据搜索请用 --inspect
ClickHouseclickhouse字面量按反斜杠转义方言处理

延伸阅读

文档