docs(ai): 真库验收记录 + mcp-accept 用例入口
MySQL demo / Redis 真库跑通只读 MCP;补充验收说明与 cmd/opsxdb-mcp-accept。实践长文在 ~/Downloads。
This commit is contained in:
1 parent
0b68c8344d
commit
b8134c4503
2 files changed
+109
No files matched your search
@@ -191,3 +191,8 @@ go build -o ../bin/opsxdb-mcp ./cmd/opsxdb-mcp
|
|||||||
4. **MCP 状态**:设置 → AI/MCP(开关+白名单);有活跃外部会话时标题旁小点「MCP」,悬停摘要,点开最近调用可断开;无活跃不显示。
|
4. **MCP 状态**:设置 → AI/MCP(开关+白名单);有活跃外部会话时标题旁小点「MCP」,悬停摘要,点开最近调用可断开;无活跃不显示。
|
||||||
|
|
||||||
落地次序:MCP 开关+白名单 → Ask|Agent Seg → 时间线与插入 → MCP 状态点。
|
落地次序:MCP 开关+白名单 → Ask|Agent Seg → 时间线与插入 → MCP 状态点。
|
||||||
|
|
||||||
|
|
||||||
|
### 真库验收 2026-10-15
|
||||||
|
|
||||||
|
MySQL 3306 demo + Redis 6379:`go run ./cmd/opsxdb-mcp-accept/` 全过。实践文档:`~/Downloads/AI-Agent接入GUI工具开发实践.md`。
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"opsxdb/server/internal/aitools"
|
||||||
|
)
|
||||||
|
|
||||||
|
func must(ok bool, msg string) {
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("FAIL:", msg)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("OK:", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dump(v any) string {
|
||||||
|
b, _ := json.MarshalIndent(v, "", " ")
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rt, err := aitools.DefaultRuntime()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
mysqlID := "accept-mysql-demo"
|
||||||
|
redisID := "accept-redis-demo"
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
out, err := rt.Call(ctx, aitools.ToolListConnections, map[string]any{})
|
||||||
|
s := dump(out)
|
||||||
|
must(err == nil && strings.Contains(s, mysqlID) && !strings.Contains(s, `"password"`), "list_connections")
|
||||||
|
|
||||||
|
out, err = rt.Call(ctx, aitools.ToolListDatabases, map[string]any{"connection_id": mysqlID})
|
||||||
|
s = dump(out)
|
||||||
|
must(err == nil && strings.Contains(s, "demo"), "list_databases: "+fmt.Sprint(err)+" "+s)
|
||||||
|
|
||||||
|
out, err = rt.Call(ctx, aitools.ToolListTables, map[string]any{"connection_id": mysqlID, "database": "demo"})
|
||||||
|
s = dump(out)
|
||||||
|
must(err == nil, "list_tables: "+fmt.Sprint(err)+" "+s)
|
||||||
|
fmt.Println(s[:min(600, len(s))])
|
||||||
|
|
||||||
|
var tables []string
|
||||||
|
if m, ok := out.(map[string]any); ok {
|
||||||
|
if arr, ok := m["tables"].([]any); ok {
|
||||||
|
for _, x := range arr {
|
||||||
|
tables = append(tables, fmt.Sprint(x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(tables) > 0 {
|
||||||
|
t := tables[0]
|
||||||
|
_, err = rt.Call(ctx, aitools.ToolDescribeTable, map[string]any{"connection_id": mysqlID, "database": "demo", "table": t})
|
||||||
|
must(err == nil, "describe_table "+t+": "+fmt.Sprint(err))
|
||||||
|
out, err = rt.Call(ctx, aitools.ToolGetSchemaContext, map[string]any{"connection_id": mysqlID, "database": "demo"})
|
||||||
|
must(err == nil && len(dump(out)) > 20, "get_schema_context")
|
||||||
|
_ = out
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err = rt.Call(ctx, aitools.ToolExecuteQuery, map[string]any{
|
||||||
|
"connection_id": mysqlID,
|
||||||
|
"database": "demo",
|
||||||
|
"sql": "SELECT 1 AS n, DATABASE() AS db",
|
||||||
|
})
|
||||||
|
s = dump(out)
|
||||||
|
must(err == nil && strings.Contains(s, "demo"), "execute_query SELECT: "+fmt.Sprint(err)+" "+s)
|
||||||
|
fmt.Println(s[:min(500, len(s))])
|
||||||
|
|
||||||
|
_, err = rt.Call(ctx, aitools.ToolExecuteQuery, map[string]any{
|
||||||
|
"connection_id": mysqlID,
|
||||||
|
"sql": "DROP TABLE IF EXISTS __opsxdb_should_deny__",
|
||||||
|
})
|
||||||
|
must(err != nil, "DROP must fail, got nil")
|
||||||
|
|
||||||
|
out, err = rt.Call(ctx, aitools.ToolExecuteRedisCommand, map[string]any{
|
||||||
|
"connection_id": redisID,
|
||||||
|
"command": "PING",
|
||||||
|
})
|
||||||
|
s = dump(out)
|
||||||
|
must(err == nil && strings.Contains(strings.ToUpper(s), "PONG"), "redis PING: "+fmt.Sprint(err)+" "+s)
|
||||||
|
|
||||||
|
_, err = rt.Call(ctx, aitools.ToolExecuteRedisCommand, map[string]any{
|
||||||
|
"connection_id": redisID,
|
||||||
|
"command": "FLUSHDB",
|
||||||
|
})
|
||||||
|
must(err != nil, "FLUSHDB must fail")
|
||||||
|
|
||||||
|
_, err = rt.Call(ctx, aitools.ToolListTables, map[string]any{"connection_id": "not-allowed"})
|
||||||
|
must(err != nil, "whitelist reject")
|
||||||
|
|
||||||
|
fmt.Println("ALL ACCEPTANCE PASSED")
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
Reference in new issue
Block a user