feat(mcp): 设置 → MCP 子页 + 策略 API + 官网/文档接入

借鉴 DBX:安装检测(Node/npm/包/Go 二进制)、只读与 allow_dangerous、
连接白名单、多客户端配置片段;GET/PUT /api/ai/mcp-policy、/mcp-install;
文档 /docs/mcp 与落地页 AI/MCP 块;对照 docs/16 勾选设置项、列仍未完清单。
This commit is contained in:
logan committed 2026-10-15 14:13:20 +08:00
1 parent 5416e86379
commit 1513f4bc2d
20 files changed
+1567 -98

No files matched your search

+64 -2
View File
@@ -18,8 +18,11 @@ const (
type Policy struct {
AllowedConnectionIDs []string `json:"allowed_connection_ids"`
Mode string `json:"mode"` // read_only | read_write
DefaultLimit int `json:"default_limit,omitempty"`
MaxLimit int `json:"max_limit,omitempty"`
// AllowDangerous 为 true 时,在 mode=read_write 且已确认后允许 DROP/TRUNCATE/ALTER 等危险 SQL。
// 默认 false:普通 INSERT/UPDATE/DELETE(有 WHERE)仍可写,DDL/无 WHERE 等仍拒。
AllowDangerous bool `json:"allow_dangerous,omitempty"`
DefaultLimit int `json:"default_limit,omitempty"`
MaxLimit int `json:"max_limit,omitempty"`
}
func DefaultPolicy() Policy {
@@ -114,3 +117,62 @@ func (p Policy) ClampLimit(n int) int {
}
return n
}
// SavePolicy 原子写入 ~/.opsxdb/mcp.json(先写临时文件再 rename)。
func SavePolicy(p Policy) error {
p.Mode = strings.ToLower(strings.TrimSpace(p.Mode))
if p.Mode != ModeReadWrite {
p.Mode = ModeReadOnly
}
if p.DefaultLimit <= 0 {
p.DefaultLimit = 100
}
if p.MaxLimit <= 0 {
p.MaxLimit = 500
}
if p.DefaultLimit > p.MaxLimit {
p.DefaultLimit = p.MaxLimit
}
if p.AllowedConnectionIDs == nil {
p.AllowedConnectionIDs = []string{}
}
// 去空白、去重保序
seen := map[string]struct{}{}
clean := make([]string, 0, len(p.AllowedConnectionIDs))
for _, id := range p.AllowedConnectionIDs {
id = strings.TrimSpace(id)
if id == "" {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
clean = append(clean, id)
}
p.AllowedConnectionIDs = clean
path, err := PolicyPath()
if err != nil {
return err
}
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0o700); err != nil {
return err
}
b, err := json.MarshalIndent(p, "", " ")
if err != nil {
return err
}
b = append(b, '\n')
tmp := path + ".tmp"
if err := os.WriteFile(tmp, b, 0o600); err != nil {
return err
}
return os.Rename(tmp, path)
}
// AllowsDangerousDDL 危险 SQL(DROP/TRUNCATE/…)是否在确认后放行。
func (p Policy) AllowsDangerousDDL() bool {
return p.AllowDangerous && p.WritesEnabled()
}