redis.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package data
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "github.com/mark3labs/mcp-go/mcp"
  7. "github.com/mark3labs/mcp-go/server"
  8. "fwh-toolbox-server/cache/redisx"
  9. "fwh-toolbox-server/cmd/mcp/tools/helper"
  10. )
  11. func registerRedisTools(s *server.MCPServer) {
  12. s.AddTool(
  13. mcp.NewTool("data_redis_get",
  14. mcp.WithDescription("读取 Redis 中指定 key 的值(只读,仅支持 string 和 hash 类型)"),
  15. mcp.WithString("key", mcp.Required(), mcp.Description("Redis key")),
  16. mcp.WithString("type", mcp.Description("key 的数据类型:string(默认)、hash、list、set")),
  17. mcp.WithNumber("limit", mcp.Description("list/set 类型返回的最大元素数,默认100,最大1000。string/hash 忽略此参数。")),
  18. ),
  19. handleRedisGet,
  20. )
  21. s.AddTool(
  22. mcp.NewTool("data_redis_exists",
  23. mcp.WithDescription("检查 Redis key 是否存在,并返回 TTL 信息"),
  24. mcp.WithString("key", mcp.Required(), mcp.Description("Redis key")),
  25. ),
  26. handleRedisExists,
  27. )
  28. }
  29. func handleRedisGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  30. key, err := req.RequireString("key")
  31. if err != nil {
  32. return helper.ParamError("key"), nil
  33. }
  34. keyType := req.GetString("type", "string")
  35. // limit 仅对 list/set 生效,默认100,上限1000
  36. limit := int(req.GetFloat("limit", 100))
  37. if limit < 1 {
  38. limit = 100
  39. }
  40. if limit > 1000 {
  41. limit = 1000
  42. }
  43. switch keyType {
  44. case "string":
  45. val, err := redisx.Get(ctx, key)
  46. if err != nil {
  47. return helper.EmptyResult(fmt.Sprintf("key 不存在或读取失败: %s (%v)", key, err)), nil
  48. }
  49. return helper.JSONResultOrFileAuto(map[string]any{
  50. "key": key,
  51. "type": "string",
  52. "value": val,
  53. }), nil
  54. case "hash":
  55. val, err := redisx.HGetAll(ctx, key)
  56. if err != nil {
  57. return helper.ServiceError("读取Redis hash", err), nil
  58. }
  59. if len(val) == 0 {
  60. return helper.EmptyResult(fmt.Sprintf("key 不存在或为空: %s", key)), nil
  61. }
  62. return helper.JSONResultOrFileAuto(map[string]any{
  63. "key": key,
  64. "type": "hash",
  65. "value": val,
  66. }), nil
  67. case "list":
  68. val, err := redisx.LRange(ctx, key, 0, int64(limit-1))
  69. if err != nil {
  70. return helper.ServiceError("读取Redis list", err), nil
  71. }
  72. if len(val) == 0 {
  73. return helper.EmptyResult(fmt.Sprintf("key 不存在或为空: %s", key)), nil
  74. }
  75. return helper.JSONResultOrFileAuto(map[string]any{
  76. "key": key,
  77. "type": "list",
  78. "count": len(val),
  79. "limit": limit,
  80. "truncated": len(val) == limit,
  81. "value": val,
  82. }), nil
  83. case "set":
  84. val, err := redisx.SMembers(ctx, key)
  85. if err != nil {
  86. return helper.ServiceError("读取Redis set", err), nil
  87. }
  88. if len(val) == 0 {
  89. return helper.EmptyResult(fmt.Sprintf("key 不存在或为空: %s", key)), nil
  90. }
  91. total := len(val)
  92. truncated := false
  93. if total > limit {
  94. val = val[:limit]
  95. truncated = true
  96. }
  97. return helper.JSONResultOrFileAuto(map[string]any{
  98. "key": key,
  99. "type": "set",
  100. "total": total,
  101. "count": len(val),
  102. "limit": limit,
  103. "truncated": truncated,
  104. "value": val,
  105. }), nil
  106. default:
  107. return helper.ParamInvalidError("type", "可选值: string, hash, list, set"), nil
  108. }
  109. }
  110. func handleRedisExists(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  111. key, err := req.RequireString("key")
  112. if err != nil {
  113. return helper.ParamError("key"), nil
  114. }
  115. // 安全检查:不允许通配符
  116. if strings.Contains(key, "*") {
  117. return helper.ParamInvalidError("key", "不支持通配符,请提供精确 key"), nil
  118. }
  119. exists, err := redisx.Exists(ctx, key)
  120. if err != nil {
  121. return helper.ServiceError("检查Redis key", err), nil
  122. }
  123. if !exists {
  124. return helper.EmptyResult(fmt.Sprintf("key 不存在: %s", key)), nil
  125. }
  126. ttl, err := redisx.TTL(ctx, key)
  127. if err != nil {
  128. return helper.ServiceError("获取TTL", err), nil
  129. }
  130. ttlStr := "永不过期"
  131. if ttl > 0 {
  132. ttlStr = ttl.String()
  133. } else if ttl == -2 {
  134. ttlStr = "key 不存在"
  135. }
  136. return helper.JSONResultOrFileAuto(map[string]any{
  137. "key": key,
  138. "exists": true,
  139. "ttl": ttlStr,
  140. }), nil
  141. }