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
@@ -0,0 +1,68 @@
|
||||
package aitools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSaveLoadPolicyRoundTrip(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
t.Setenv("HOME", dir)
|
||||
// PolicyPath uses UserHomeDir → ~/.opsxdb/mcp.json
|
||||
p := Policy{
|
||||
AllowedConnectionIDs: []string{" a ", "b", "a", ""},
|
||||
Mode: "READ_WRITE",
|
||||
AllowDangerous: true,
|
||||
DefaultLimit: 50,
|
||||
MaxLimit: 200,
|
||||
}
|
||||
if err := SavePolicy(p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := filepath.Join(dir, ".opsxdb", "mcp.json")
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := LoadPolicy()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Mode != ModeReadWrite {
|
||||
t.Fatalf("mode=%s", got.Mode)
|
||||
}
|
||||
if !got.AllowDangerous {
|
||||
t.Fatal("want allow_dangerous")
|
||||
}
|
||||
if len(got.AllowedConnectionIDs) != 2 || got.AllowedConnectionIDs[0] != "a" || got.AllowedConnectionIDs[1] != "b" {
|
||||
t.Fatalf("ids=%v", got.AllowedConnectionIDs)
|
||||
}
|
||||
if got.DefaultLimit != 50 || got.MaxLimit != 200 {
|
||||
t.Fatalf("limits %d/%d", got.DefaultLimit, got.MaxLimit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDangerousSQLRequiresAllowFlag(t *testing.T) {
|
||||
rt := NewRuntime(Policy{
|
||||
AllowedConnectionIDs: []string{"c1"},
|
||||
Mode: ModeReadWrite,
|
||||
AllowDangerous: false,
|
||||
})
|
||||
rt.FindConn = func(id string) (*SavedConnection, error) {
|
||||
return &SavedConnection{ID: id, Name: "c1", Driver: "mysql", Host: "127.0.0.1", Port: 3306}, nil
|
||||
}
|
||||
rt.Audit = func(AuditEntry) {}
|
||||
rt.AllowConfirmedWrite = true
|
||||
_, err := rt.Call(context.Background(), ToolExecuteQuery, map[string]any{
|
||||
"connection_id": "c1",
|
||||
"sql": "DROP TABLE users",
|
||||
"confirm_danger": true,
|
||||
})
|
||||
if err == nil || !contains(err.Error(), "dangerous sql disabled") {
|
||||
t.Fatalf("want dangerous disabled, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(s, sub string) bool { return strings.Contains(s, sub) }
|
||||
Reference in new issue
Block a user