mongo.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package data
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/mark3labs/mcp-go/mcp"
  7. "github.com/mark3labs/mcp-go/server"
  8. "go.mongodb.org/mongo-driver/bson"
  9. "go.mongodb.org/mongo-driver/mongo/options"
  10. "fwh-toolbox-server/cmd/mcp/tools/helper"
  11. "fwh-toolbox-server/database"
  12. )
  13. // mongoAllowedCollections 允许查询的 collection 白名单
  14. var mongoAllowedCollections = []string{
  15. "site_info",
  16. "sim_card_manage",
  17. "batch_download",
  18. "site_analysis",
  19. "shutdown_statistics",
  20. "alarm_statistics",
  21. "sites_monitor_config",
  22. "user_config",
  23. }
  24. func registerMongoTools(s *server.MCPServer) {
  25. s.AddTool(
  26. mcp.NewTool("data_mongo_find",
  27. mcp.WithDescription("直接查询 MongoDB 集合数据(只读)。用于 service 层无法满足的灵活查询场景。"),
  28. mcp.WithString("collection", mcp.Required(), mcp.Description(
  29. fmt.Sprintf("集合名,可选值: %v", mongoAllowedCollections),
  30. )),
  31. mcp.WithString("filter", mcp.Description("查询条件,JSON 格式的 MongoDB filter(如 {\"sn\": \"xxx\"}),不传则查全部")),
  32. mcp.WithNumber("limit", mcp.Description("返回数量限制,默认10,最大100")),
  33. mcp.WithNumber("skip", mcp.Description("跳过数量(分页偏移),默认0")),
  34. mcp.WithString("sort", mcp.Description("排序条件,JSON 格式(如 {\"createdAt\": -1})")),
  35. ),
  36. handleMongoFind,
  37. )
  38. s.AddTool(
  39. mcp.NewTool("data_mongo_count",
  40. mcp.WithDescription("统计 MongoDB 集合中符合条件的文档数量"),
  41. mcp.WithString("collection", mcp.Required(), mcp.Description(
  42. fmt.Sprintf("集合名,可选值: %v", mongoAllowedCollections),
  43. )),
  44. mcp.WithString("filter", mcp.Description("查询条件,JSON 格式的 MongoDB filter,不传则统计全部")),
  45. ),
  46. handleMongoCount,
  47. )
  48. }
  49. func handleMongoFind(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  50. collection, err := req.RequireString("collection")
  51. if err != nil {
  52. return helper.ParamError("collection"), nil
  53. }
  54. if !isAllowedCollection(collection) {
  55. return helper.ParamInvalidError("collection", fmt.Sprintf("不允许查询此集合,可选: %v", mongoAllowedCollections)), nil
  56. }
  57. // 解析 filter
  58. filterStr := req.GetString("filter", "{}")
  59. var filter bson.M
  60. if err := json.Unmarshal([]byte(filterStr), &filter); err != nil {
  61. return helper.ParamInvalidError("filter", fmt.Sprintf("JSON 解析失败: %v", err)), nil
  62. }
  63. // limit
  64. limit := int64(req.GetFloat("limit", 10))
  65. if limit > 100 {
  66. limit = 100
  67. }
  68. if limit < 1 {
  69. limit = 10
  70. }
  71. // skip
  72. skip := int64(req.GetFloat("skip", 0))
  73. if skip < 0 {
  74. skip = 0
  75. }
  76. // sort
  77. var sort bson.M
  78. sortStr := req.GetString("sort", "")
  79. if sortStr != "" {
  80. if err := json.Unmarshal([]byte(sortStr), &sort); err != nil {
  81. return helper.ParamInvalidError("sort", fmt.Sprintf("JSON 解析失败: %v", err)), nil
  82. }
  83. }
  84. // 查询
  85. coll := database.GetCollection(collection)
  86. findOpts := options.Find().SetLimit(limit).SetSkip(skip)
  87. if sort != nil {
  88. findOpts.SetSort(sort)
  89. }
  90. cursor, err := coll.Find(ctx, filter, findOpts)
  91. if err != nil {
  92. return helper.ServiceError("MongoDB查询", err), nil
  93. }
  94. defer cursor.Close(ctx)
  95. var results []bson.M
  96. if err := cursor.All(ctx, &results); err != nil {
  97. return helper.ServiceError("读取MongoDB结果", err), nil
  98. }
  99. if len(results) == 0 {
  100. return helper.EmptyResult("查询结果为空"), nil
  101. }
  102. return helper.JSONResult(results), nil
  103. }
  104. func handleMongoCount(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  105. collection, err := req.RequireString("collection")
  106. if err != nil {
  107. return helper.ParamError("collection"), nil
  108. }
  109. if !isAllowedCollection(collection) {
  110. return helper.ParamInvalidError("collection", fmt.Sprintf("不允许查询此集合,可选: %v", mongoAllowedCollections)), nil
  111. }
  112. filterStr := req.GetString("filter", "{}")
  113. var filter bson.M
  114. if err := json.Unmarshal([]byte(filterStr), &filter); err != nil {
  115. return helper.ParamInvalidError("filter", fmt.Sprintf("JSON 解析失败: %v", err)), nil
  116. }
  117. coll := database.GetCollection(collection)
  118. count, err := coll.CountDocuments(ctx, filter)
  119. if err != nil {
  120. return helper.ServiceError("MongoDB统计", err), nil
  121. }
  122. return helper.JSONResult(map[string]any{
  123. "collection": collection,
  124. "count": count,
  125. }), nil
  126. }
  127. func isAllowedCollection(name string) bool {
  128. for _, c := range mongoAllowedCollections {
  129. if c == name {
  130. return true
  131. }
  132. }
  133. return false
  134. }