feat(tools): 新增 sql_query 只读 SQL 查询工具(agent 可见)

agent 可对数据库执行只读查询。三重防护:
- 静态校验:仅单条 SELECT/WITH,词边界拒 insert/update/delete/drop/alter/create/
  truncate/grant 等写/DDL 关键字(不误伤 created_at 之类列名)。
- 只读事务(sql.TxOptions{ReadOnly:true}):Postgres 引擎级强制只读,硬兜底。
- 行数(100)+超时(10s)+单元格(200 rune)上限。
连接:SQL_QUERY_DSN 优先(生产应指向专用只读库/账号),未设回退服务已解析的平台 PG DSN
(经 NewGateway 传入 g.pgDSN,不再裸读 env)。独立小连接池(8)。

测试:单测 validateReadOnlySQL(放行 SELECT/WITH/列名含 created;拒写/DDL/多语句)。
live 自主 agent 实测:查 sundynix_task=157 行、sundynix_model=2 行。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-24 17:07:34 +08:00
parent e503965a83
commit ca38dcd0c9
4 changed files with 213 additions and 3 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ func main() {
go b.RequestConfigWithRetry(ctx, contract.ConfigKindEmbedding, applyEmbed)
go b.RequestConfigWithRetry(ctx, contract.ConfigKindChat, applyChat)
gw := mcp.NewGateway(b, engine, mem, hist, ragEngine)
gw := mcp.NewGateway(b, engine, mem, hist, ragEngine, pgDSN)
log.Println("[mcp_go] serving MCP over sundynix.tools.go.* (Ctrl-C to quit)")
if err := gw.Serve(ctx); err != nil && err != context.Canceled {