From 51f4b3c8eb2193c12fa26cb7391ac0638b94f2eb Mon Sep 17 00:00:00 2001 From: cnphpbb Date: Thu, 11 Sep 2025 10:34:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=20=E9=87=8D=E6=9E=84=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=92=8C=E9=94=99=E8=AF=AF=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor: 重构日志处理和错误输出格式 fix: 修正配置加载路径和温度参数类型 docs: 更新结构体字段注释格式 --- .gitignore | 2 + .../{ => examples}/client_example.go | 26 ++-- mcp_server_go/go.mod | 121 ++++-------------- mcp_server_go/main.go | 88 +++++++------ 4 files changed, 95 insertions(+), 142 deletions(-) rename mcp_server_go/{ => examples}/client_example.go (81%) diff --git a/.gitignore b/.gitignore index e76e344..54f0755 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ tests/ # 日志文件 *.log logs/ +mcp_server_go/go.sum +mcp_server_go/mcp_server_go diff --git a/mcp_server_go/client_example.go b/mcp_server_go/examples/client_example.go similarity index 81% rename from mcp_server_go/client_example.go rename to mcp_server_go/examples/client_example.go index 3f8f640..0d5150c 100644 --- a/mcp_server_go/client_example.go +++ b/mcp_server_go/examples/client_example.go @@ -11,12 +11,20 @@ import ( // MCPRequest 定义MCP请求结构 type MCPRequest struct { - Data interface{} `json:"data"` Type string `json:"type"` Metadata map[string]string `json:"metadata,omitempty"` Timestamp int64 `json:"timestamp"` + Data interface{} `json:"data"` + Type string `json:"type"` + Metadata map[string]string `json:"metadata,omitempty"` + Timestamp int64 `json:"timestamp"` } // MCPResponse 定义MCP响应结构 type MCPResponse struct { - Success bool `json:"success"` Message string `json:"message,omitempty"` Data interface{} `json:"data,omitempty"` AIResult interface{} `json:"ai_result,omitempty"` RequestID string `json:"request_id,omitempty"` Timestamp int64 `json:"timestamp"` + Success bool `json:"success"` + Message string `json:"message,omitempty"` + Data interface{} `json:"data,omitempty"` + AIResult interface{} `json:"ai_result,omitempty"` + RequestID string `json:"request_id,omitempty"` + Timestamp int64 `json:"timestamp"` } func main() { @@ -24,10 +32,10 @@ func main() { diskData := map[string]interface{}{ "disk_id": "sda", "smart_data": map[string]interface{}{ - "temperature": 38, - "power_on_hours": 12345, - "read_errors": 0, - "write_errors": 0, + "temperature": 38, + "power_on_hours": 12345, + "read_errors": 0, + "write_errors": 0, "reallocated_sectors": 0, }, "performance_data": map[string]interface{}{ @@ -85,8 +93,8 @@ func main() { fmt.Printf(" 成功状态: %v\n", mcpResponse.Success) fmt.Printf(" 消息: %s\n", mcpResponse.Message) fmt.Printf(" 请求ID: %s\n", mcpResponse.RequestID) - fmt.Printf(" 时间戳: %d (%s)\n", - mcpResponse.Timestamp, + fmt.Printf(" 时间戳: %d (%s)\n", + mcpResponse.Timestamp, time.Unix(mcpResponse.Timestamp, 0).Format("2006-01-02 15:04:05")) // 输出AI分析结果(如果有) @@ -122,4 +130,4 @@ func testHealthCheck() { fmt.Printf("健康检查状态码: %d\n", resp.StatusCode) fmt.Printf("健康检查响应: %s\n", string(body)) -} \ No newline at end of file +} diff --git a/mcp_server_go/go.mod b/mcp_server_go/go.mod index 72eb4bc..e11992a 100644 --- a/mcp_server_go/go.mod +++ b/mcp_server_go/go.mod @@ -1,107 +1,34 @@ module mcp_server_go -go 1.21 +go 1.25.0 require ( - github.com/fsnotify/fsnotify v1.7.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/google/uuid v1.6.0 - github.com/prometheus/client_golang v1.18.0 - github.com/sashabaranov/go-openai v1.17.4 - github.com/spf13/viper v1.17.0 - golang.org/x/time v0.5.0 + github.com/prometheus/client_golang v1.23.2 + github.com/sashabaranov/go-openai v1.41.1 + github.com/spf13/viper v1.21.0 + golang.org/x/time v0.13.0 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.3 // indirect - github.com/OneOfOne/xxhash v1.2.2 // indirect - github.com/alecthomas/kingpin v1.3.1 // indirect - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.8.0 // indirect - github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fatih/color v1.15.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-ini/ini v1.67.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/gopherjs/gopherjs v1.17.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/itchyny/gojq v0.12.13 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jessevdk/go-flags v1.5.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.2 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pierrec/lz4 v4.1.17+incompatible // indirect - github.com/pierrec/lz4/v4 v4.1.18 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.18.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sashabaranov/go-openai v1.17.4 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/afero v1.10.0 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/cobra v1.7.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.17.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tidwall/gjson v1.17.1 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect - github.com/toml-lang/toml v1.0.0 // indirect - github.com/xdg-go/scram v1.1.2 // indirect - github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/yosssi/gohtml v0.0.0-20230121090043-345651034a4d // indirect - github.com/yosssi/gi v0.0.0-20180507055002-ea47d430d263 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.13.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231019153415-0f05c00091f9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231019153415-0f05c00091f9 // indirect - google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) \ No newline at end of file + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect +) diff --git a/mcp_server_go/main.go b/mcp_server_go/main.go index 920f145..3bb3558 100644 --- a/mcp_server_go/main.go +++ b/mcp_server_go/main.go @@ -8,6 +8,7 @@ import ( "log/slog" "net/http" "os" + "os/signal" "path/filepath" "strings" "syscall" @@ -25,42 +26,69 @@ import ( // Config 结构体用于存储配置 type Config struct { - Server ServerConfig `mapstructure:"server"` OpenAI OpenAIConfig `mapstructure:"openai"` Logging LoggingConfig `mapstructure:"logging"` Security SecurityConfig `mapstructure:"security"` + Server ServerConfig `mapstructure:"server"` + OpenAI OpenAIConfig `mapstructure:"openai"` + Logging LoggingConfig `mapstructure:"logging"` + Security SecurityConfig `mapstructure:"security"` } // ServerConfig 服务器配置 type ServerConfig struct { - ListenAddr string `mapstructure:"listen_addr"` ReadTimeout int `mapstructure:"read_timeout"` WriteTimeout int `mapstructure:"write_timeout"` MaxHeaderBytes int `mapstructure:"max_header_bytes"` + ListenAddr string `mapstructure:"listen_addr"` + ReadTimeout int `mapstructure:"read_timeout"` + WriteTimeout int `mapstructure:"write_timeout"` + MaxHeaderBytes int `mapstructure:"max_header_bytes"` } // OpenAIConfig OpenAI API配置 type OpenAIConfig struct { - APIKey string `mapstructure:"api_key"` BaseURL string `mapstructure:"base_url"` Model string `mapstructure:"model"` Temperature float64 `mapstructure:"temperature"` MaxTokens int `mapstructure:"max_tokens"` RequestTimeout int `mapstructure:"request_timeout"` + APIKey string `mapstructure:"api_key"` + BaseURL string `mapstructure:"base_url"` + Model string `mapstructure:"model"` + Temperature float64 `mapstructure:"temperature"` + MaxTokens int `mapstructure:"max_tokens"` + RequestTimeout int `mapstructure:"request_timeout"` } // LoggingConfig 日志配置 type LoggingConfig struct { - Level string `mapstructure:"level"` Format string `mapstructure:"format"` OutputPath string `mapstructure:"output_path"` MaxSize int `mapstructure:"max_size"` MaxAge int `mapstructure:"max_age"` MaxBackups int `mapstructure:"max_backups"` Compress bool `mapstructure:"compress"` + Level string `mapstructure:"level"` + Format string `mapstructure:"format"` + OutputPath string `mapstructure:"output_path"` + MaxSize int `mapstructure:"max_size"` + MaxAge int `mapstructure:"max_age"` + MaxBackups int `mapstructure:"max_backups"` + Compress bool `mapstructure:"compress"` } // SecurityConfig 安全配置 type SecurityConfig struct { - AllowedOrigins []string `mapstructure:"allowed_origins"` AllowedMethods []string `mapstructure:"allowed_methods"` AllowedHeaders []string `mapstructure:"allowed_headers"` + AllowedOrigins []string `mapstructure:"allowed_origins"` + AllowedMethods []string `mapstructure:"allowed_methods"` + AllowedHeaders []string `mapstructure:"allowed_headers"` } // MCPRequest MCP请求结构体 type MCPRequest struct { - Data interface{} `json:"data"` Type string `json:"type"` Metadata map[string]string `json:"metadata,omitempty"` Timestamp int64 `json:"timestamp"` + Data interface{} `json:"data"` + Type string `json:"type"` + Metadata map[string]string `json:"metadata,omitempty"` + Timestamp int64 `json:"timestamp"` } // MCPResponse MCP响应结构体 type MCPResponse struct { - Success bool `json:"success"` Message string `json:"message,omitempty"` Data interface{} `json:"data,omitempty"` AIResult interface{} `json:"ai_result,omitempty"` RequestID string `json:"request_id,omitempty"` Timestamp int64 `json:"timestamp"` + Success bool `json:"success"` + Message string `json:"message,omitempty"` + Data interface{} `json:"data,omitempty"` + AIResult interface{} `json:"ai_result,omitempty"` + RequestID string `json:"request_id,omitempty"` + Timestamp int64 `json:"timestamp"` } var ( - config Config - logger *slog.Logger + config Config + logger *slog.Logger openaiClient *openai.Client // 速率限制器 limiter = rate.NewLimiter(rate.Limit(10), 20) @@ -69,7 +97,7 @@ var ( prometheus.CounterOpts{ Name: "mcp_server_requests_total", Help: "Total number of MCP server requests", - }, + }, []string{"endpoint", "status"}, ) requestDuration = prometheus.NewHistogramVec( @@ -77,7 +105,7 @@ var ( Name: "mcp_server_request_duration_seconds", Help: "Duration of MCP server requests in seconds", Buckets: prometheus.DefBuckets, - }, + }, []string{"endpoint"}, ) ) @@ -95,6 +123,7 @@ func loadConfig() error { viper.AddConfigPath(".") viper.AddConfigPath("/etc/mcp_server/") viper.AddConfigPath("$HOME/.mcp_server/") + viper.AddConfigPath("$HOME/.config/mcp_server/") if err := viper.ReadInConfig(); err != nil { return fmt.Errorf("读取配置文件失败: %w", err) @@ -149,20 +178,7 @@ func initLogger() error { logLevel = slog.LevelInfo } - // 创建日志处理器 - var handler slog.Handler - if config.Logging.Format == "json" { - handler = slog.NewJSONHandler(logFile, &slog.HandlerOptions{ - Level: logLevel, - }) - } else { - handler = slog.NewTextHandler(logFile, &slog.HandlerOptions{ - Level: logLevel, - }) - } - - // 同时输出到控制台和文件 - multiHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel}) + // 创建日志器,同时输出到控制台和文件 logger = slog.New(slog.NewTextHandler( io.MultiWriter(os.Stdout, logFile), &slog.HandlerOptions{Level: logLevel}, @@ -225,7 +241,7 @@ func loggingMiddleware(next http.Handler) http.Handler { wrappedWriter := &responseWriter{ ResponseWriter: w, - statusCode: http.StatusOK, + statusCode: http.StatusOK, } next.ServeHTTP(wrappedWriter, r) @@ -272,9 +288,9 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) { } response := map[string]interface{}{ - "status": "ok", - "version": "1.0.0", - "timestamp": time.Now().Unix(), + "status": "ok", + "version": "1.0.0", + "timestamp": time.Now().Unix(), "openai_health": oaiHealthy, } @@ -296,7 +312,7 @@ func submitHandler(w http.ResponseWriter, r *http.Request) { // 解析请求体 var mcpRequest MCPRequest if err := json.NewDecoder(r.Body).Decode(&mcpRequest); err != nil { - logger.Error("解析请求体失败", slog.String("request_id", requestID), slog.Error(err)) + logger.Error("解析请求体失败", slog.String("request_id", requestID), slog.Any("error", err)) http.Error(w, "无效的请求体", http.StatusBadRequest) requestCounter.WithLabelValues("/mcp/v1/submit", "400").Inc() return @@ -310,7 +326,7 @@ func submitHandler(w http.ResponseWriter, r *http.Request) { // 调用OpenAI API进行分析 aIResult, err := analyzeWithOpenAI(r.Context(), mcpRequest, requestID) if err != nil { - logger.Error("OpenAI API调用失败", slog.String("request_id", requestID), slog.Error(err)) + logger.Error("OpenAI API调用失败", slog.String("request_id", requestID), slog.Any("error", err)) http.Error(w, "AI分析失败", http.StatusInternalServerError) requestCounter.WithLabelValues("/mcp/v1/submit", "500").Inc() return @@ -329,7 +345,7 @@ func submitHandler(w http.ResponseWriter, r *http.Request) { // 发送响应 w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { - logger.Error("发送响应失败", slog.String("request_id", requestID), slog.Error(err)) + logger.Error("发送响应失败", slog.String("request_id", requestID), slog.Any("error", err)) } requestCounter.WithLabelValues("/mcp/v1/submit", "200").Inc() @@ -371,7 +387,7 @@ func analyzeWithOpenAI(ctx context.Context, request MCPRequest, requestID string Content: prompt, }, }, - Temperature: config.OpenAI.Temperature, + Temperature: float32(config.OpenAI.Temperature), MaxTokens: config.OpenAI.MaxTokens, } @@ -445,7 +461,7 @@ func main() { // 启动服务器 go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - logger.Error("服务器启动失败", slog.Error(err)) + logger.Error("服务器启动失败", slog.Any("error", err)) os.Exit(1) } }() @@ -465,9 +481,9 @@ func main() { // 优雅关闭服务器 if err := srv.Shutdown(ctx); err != nil { - logger.Error("服务器关闭失败", slog.Error(err)) + logger.Error("服务器关闭失败", slog.Any("error", err)) os.Exit(1) } logger.Info("MCP服务器已安全关闭") -} \ No newline at end of file +}