main.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "go.uber.org/zap"
  6. "go.uber.org/zap/zapcore"
  7. "github.com/mark3labs/mcp-go/server"
  8. "fwh-toolbox-server/cache"
  9. "fwh-toolbox-server/config"
  10. "fwh-toolbox-server/database"
  11. "fwh-toolbox-server/logger"
  12. "fwh-toolbox-server/repositories"
  13. "fwh-toolbox-server/utils/filestore"
  14. minioUtil "fwh-toolbox-server/utils/minio"
  15. "fwh-toolbox-server/cmd/mcp/tools"
  16. "fwh-toolbox-server/cmd/mcp/tools/storage"
  17. )
  18. const (
  19. serverName = "fwh-tools"
  20. serverVersion = "2.0.0"
  21. logDir = "/var/log/fwh-mcp-server"
  22. logFile = "/var/log/fwh-mcp-server/mcp.log"
  23. )
  24. func main() {
  25. // 初始化 MCP 专用 logger(写入文件,不污染 stdio)
  26. initMCPLogger()
  27. defer zap.L().Sync()
  28. // 加载配置(复用现有 config)
  29. cfg, err := config.LoadConfig()
  30. if err != nil {
  31. zap.L().Fatal("加载配置失败", zap.Error(err))
  32. }
  33. // 初始化基础设施(与 HTTP Server 相同)
  34. if err := database.Init(&cfg.App); err != nil {
  35. zap.L().Fatal("数据库初始化失败", zap.Error(err))
  36. }
  37. cache.Init(cfg.App.RedisAddr)
  38. minioUtil.InitClient(&cfg.App)
  39. repositories.PlaneWebHttpInit(&cfg.PlaneWebApi)
  40. // 初始化 MCP FileStore
  41. mcpFS := filestore.NewFileStore("download")
  42. storage.InitFileStore(mcpFS)
  43. // 创建 MCP Server
  44. s := server.NewMCPServer(
  45. serverName,
  46. serverVersion,
  47. server.WithToolCapabilities(false),
  48. server.WithRecovery(),
  49. server.WithInstructions(serverInstructions),
  50. )
  51. // 注册所有 Tools(唯一入口)
  52. tools.RegisterAll(s)
  53. // 启动 stdio 通信
  54. zap.L().Info("[fwh-mcp-server] started", zap.String("version", serverVersion))
  55. fmt.Fprintln(os.Stderr, "[fwh-mcp-server] started (stdio) v"+serverVersion)
  56. if err := server.ServeStdio(s); err != nil {
  57. zap.L().Fatal("[fwh-mcp-server] error", zap.Error(err))
  58. }
  59. }
  60. // initMCPLogger 初始化 MCP Server 专用 logger
  61. // 日志写入文件,不输出到 stdout/stderr(避免污染 stdio JSON-RPC 通道)
  62. func initMCPLogger() {
  63. // 确保日志目录存在
  64. if err := os.MkdirAll(logDir, 0755); err != nil {
  65. fmt.Fprintf(os.Stderr, "[fwh-mcp-server] 创建日志目录失败: %v\n", err)
  66. }
  67. config := zap.Config{
  68. Level: zap.NewAtomicLevelAt(zap.InfoLevel),
  69. Development: false,
  70. Encoding: "json",
  71. EncoderConfig: zapcore.EncoderConfig{
  72. TimeKey: "ts",
  73. LevelKey: "level",
  74. NameKey: "logger",
  75. CallerKey: "caller",
  76. MessageKey: "msg",
  77. StacktraceKey: "stacktrace",
  78. LineEnding: zapcore.DefaultLineEnding,
  79. EncodeLevel: zapcore.LowercaseLevelEncoder,
  80. EncodeTime: zapcore.ISO8601TimeEncoder,
  81. EncodeDuration: zapcore.SecondsDurationEncoder,
  82. EncodeCaller: zapcore.ShortCallerEncoder,
  83. },
  84. OutputPaths: []string{logFile},
  85. ErrorOutputPaths: []string{logFile},
  86. }
  87. zapLogger, err := config.Build(zap.AddStacktrace(zap.ErrorLevel))
  88. if err != nil {
  89. fmt.Fprintf(os.Stderr, "[fwh-mcp-server] logger 初始化失败: %v\n", err)
  90. // fallback: 使用 nop logger
  91. zapLogger = zap.NewNop()
  92. }
  93. // 设置全局 logger(zap.L() 和 zap.S())
  94. zap.ReplaceGlobals(zapLogger)
  95. // 同时设置 logger 包变量(database 等模块直接引用 logger.Sugar)
  96. logger.Log = zapLogger
  97. logger.Sugar = zapLogger.Sugar()
  98. logger.Std = zap.NewStdLog(zapLogger)
  99. }
  100. // serverInstructions MCP Server 使用说明,帮助 AI 理解工具分类和使用优先级
  101. const serverInstructions = `FranklinWH 工具箱 MCP Server — 内部工具平台的 AI 接口层。
  102. ## 工具分类(按访问层级)
  103. ### service_* — 业务逻辑层(优先使用)
  104. 调用后端 Service 层函数,包含业务校验、数据聚合和格式化。
  105. 适用场景:常规的业务数据查询和操作。
  106. - service_site_*: 站点设备管理(查详情、搜索、功率流)
  107. - service_sim_*: SIM 卡管理(查详情、搜索)
  108. - service_monitor_*: 运维监控(告警统计、停机统计)
  109. - service_feedback_*: 反馈协作(创建/查询 Plane Issue)
  110. ### data_* — 数据直查层(灵活查询)
  111. 直接读取数据库或缓存,跳过业务逻辑层。仅限只读操作。
  112. 适用场景:当 service 层工具无法满足查询需求时(自定义 filter、聚合统计等)。
  113. - data_mongo_*: MongoDB 集合查询
  114. - data_redis_*: Redis 缓存查看
  115. ### storage_* — 对象存储层
  116. 文件和对象存储操作。
  117. - storage_file_*: 用户上传文件的读取和写入
  118. - storage_minio_*: MinIO bucket 对象浏览和读取(只读)
  119. ### util_* — 辅助工具
  120. 纯函数工具,不涉及数据持久化。
  121. - util_parse_sn: 解析设备 SN 序列号含义
  122. ## 使用原则
  123. 1. 优先使用 service_* 工具,它们包含完整的业务逻辑
  124. 2. 仅当 service 层无法满足时才使用 data_* 直查
  125. 3. data_* 工具仅支持只读操作
  126. 4. storage_file_* 支持读写,storage_minio_* 仅只读
  127. 5. 查询 MongoDB 时注意 collection 白名单限制
  128. 6. MinIO 操作受 bucket 白名单限制`