- aitools 共用 runtime:list/describe/schema/query/redis(纯只读,无写确认) - 白名单 ~/.opsxdb/mcp.json;审计 mcp-audit.jsonl;复用 danger.go - 桌面 connections 双写 ~/.opsxdb/connections.json(ox1. 解码) - Cursor 配置示例与任务书验收勾选更新;写操作与 Agent UI 留 P1
110 lines
3.6 KiB
Go
110 lines
3.6 KiB
Go
// opsxdb-mcp:stdio MCP Server,暴露只读数据库工具(P0)。 // Logan.Li
|
||
//
|
||
// 配置:
|
||
// ~/.opsxdb/mcp.json — allowed_connection_ids;mode(P0 执行层强制只读)
|
||
// ~/.opsxdb/connections.json — 桌面双写的连接列表(密码 ox1. 混淆)
|
||
// ~/.opsxdb/logs/mcp-audit.jsonl
|
||
//
|
||
// Cursor 示例见 docs/mcp-cursor.example.json
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
|
||
"github.com/mark3labs/mcp-go/mcp"
|
||
"github.com/mark3labs/mcp-go/server"
|
||
|
||
"opsxdb/server/internal/aitools"
|
||
)
|
||
|
||
func main() {
|
||
rt, err := aitools.DefaultRuntime()
|
||
if err != nil {
|
||
fmt.Fprintf(os.Stderr, "opsxdb-mcp: load policy: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
s := server.NewMCPServer(
|
||
"opsxdb",
|
||
"0.1.0",
|
||
server.WithToolCapabilities(true),
|
||
)
|
||
|
||
register := func(name, desc string, props map[string]any, required []string) {
|
||
schema := map[string]any{
|
||
"type": "object",
|
||
"properties": props,
|
||
}
|
||
if len(required) > 0 {
|
||
schema["required"] = required
|
||
}
|
||
raw, _ := json.Marshal(schema)
|
||
t := mcp.NewToolWithRawSchema(name, desc, raw)
|
||
toolName := name
|
||
s.AddTool(t, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||
args := req.GetArguments()
|
||
out, err := rt.Call(ctx, toolName, args)
|
||
if err != nil {
|
||
return mcp.NewToolResultError(err.Error()), nil
|
||
}
|
||
return mcp.NewToolResultText(aitools.ResultJSON(out)), nil
|
||
})
|
||
}
|
||
|
||
connID := map[string]any{"type": "string", "description": "Saved connection id from opsxdb_list_connections"}
|
||
database := map[string]any{"type": "string", "description": "Database / schema / Redis DB index"}
|
||
|
||
register(aitools.ToolListConnections,
|
||
"List whitelisted OpsxDB connections (id/name/driver/host/port/database; never passwords).",
|
||
map[string]any{}, nil)
|
||
|
||
register(aitools.ToolListDatabases,
|
||
"List databases for a connection.",
|
||
map[string]any{"connection_id": connID}, []string{"connection_id"})
|
||
|
||
register(aitools.ToolListTables,
|
||
"List tables/views (or engine equivalents) in a database.",
|
||
map[string]any{"connection_id": connID, "database": database}, []string{"connection_id"})
|
||
|
||
register(aitools.ToolDescribeTable,
|
||
"Describe columns and metadata for a table.",
|
||
map[string]any{
|
||
"connection_id": connID,
|
||
"database": database,
|
||
"table": map[string]any{"type": "string", "description": "Table name"},
|
||
}, []string{"connection_id", "table"})
|
||
|
||
register(aitools.ToolGetSchemaContext,
|
||
"Compact schema text for LLM context (capped ~4k chars).",
|
||
map[string]any{
|
||
"connection_id": connID,
|
||
"database": database,
|
||
"max_tables": map[string]any{"type": "integer", "description": "Max tables to include (default 40)"},
|
||
}, []string{"connection_id"})
|
||
|
||
register(aitools.ToolExecuteQuery,
|
||
"Execute a READ-ONLY SQL query (SELECT/SHOW/EXPLAIN/WITH…SELECT). Writes and DDL are rejected in P0. Default limit 100, max 500.",
|
||
map[string]any{
|
||
"connection_id": connID,
|
||
"sql": map[string]any{"type": "string", "description": "Read-only SQL"},
|
||
"database": database,
|
||
"limit": map[string]any{"type": "integer", "description": "Row limit (default 100, max 500)"},
|
||
}, []string{"connection_id", "sql"})
|
||
|
||
register(aitools.ToolExecuteRedisCommand,
|
||
"Execute a READ-ONLY Redis command (GET/SCAN/TYPE/TTL/…). FLUSH*/KEYS and writes are rejected in P0.",
|
||
map[string]any{
|
||
"connection_id": connID,
|
||
"command": map[string]any{"type": "string", "description": "Single Redis command line"},
|
||
"database": database,
|
||
}, []string{"connection_id", "command"})
|
||
|
||
if err := server.ServeStdio(s); err != nil {
|
||
fmt.Fprintf(os.Stderr, "opsxdb-mcp: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
}
|