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:
1 parent
5416e86379
commit
1513f4bc2d
20 files changed
+1567
-98
No files matched your search
@@ -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()
|
||||
}
|
||||
Reference in new issue
Block a user