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:
@@ -3,6 +3,7 @@ package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
sharedbus "github.com/sundynix/sundynix-shared/bus"
|
||||
@@ -30,6 +32,11 @@ type Gateway struct {
|
||||
history *history.Store
|
||||
rag *rag.Engine
|
||||
tools map[string]toolDef // 工具注册表:唯一事实源,dispatch 与 list_tools 共用,杜绝漂移
|
||||
|
||||
pgDSN string // 平台 PG DSN(sql_query 兜底库;SQL_QUERY_DSN 未设时用它)
|
||||
sqlOnce sync.Once // sql_query 只读连接懒连一次
|
||||
sqlDB *sql.DB //
|
||||
sqlDBErr error //
|
||||
}
|
||||
|
||||
// paramSpec 是一个工具参数的声明(供自主 agent 据此生成调用入参)。
|
||||
@@ -53,8 +60,8 @@ type toolDef struct {
|
||||
handler func(context.Context, *contract.ToolCall) *contract.ToolResult
|
||||
}
|
||||
|
||||
func NewGateway(b *sharedbus.Bus, s *search.Hybrid, m *memory.Store, h *history.Store, r *rag.Engine) *Gateway {
|
||||
g := &Gateway{bus: b, search: s, memory: m, history: h, rag: r}
|
||||
func NewGateway(b *sharedbus.Bus, s *search.Hybrid, m *memory.Store, h *history.Store, r *rag.Engine, pgDSN string) *Gateway {
|
||||
g := &Gateway{bus: b, search: s, memory: m, history: h, rag: r, pgDSN: pgDSN}
|
||||
g.tools = g.buildRegistry()
|
||||
return g
|
||||
}
|
||||
@@ -129,6 +136,12 @@ func (g *Gateway) buildRegistry() map[string]toolDef {
|
||||
params: []paramSpec{{Name: "tz", Type: "string", Desc: "可选时区,如 Asia/Shanghai;缺省服务器本地时区"}},
|
||||
handler: g.currentDatetime,
|
||||
},
|
||||
"sql_query": {
|
||||
cn: "SQL查询", desc: "对数据库执行只读 SQL 查询(仅 SELECT/WITH),返回结果表。需要查业务数据/统计时调用。",
|
||||
agent: true,
|
||||
params: []paramSpec{{Name: "sql", Type: "string", Desc: "只读 SQL,如 SELECT count(*) FROM sundynix_task", Required: true}},
|
||||
handler: g.sqlQuery,
|
||||
},
|
||||
|
||||
// —— 仅内部/流水线/管理用,不暴露给自主 agent ——
|
||||
"kb_ingest": {cn: "知识入库", desc: "文本切块 → 向量化 → 写入 Milvus / Bleve", handler: g.kbIngest},
|
||||
|
||||
Reference in New Issue
Block a user