package mcp import ( "net/http" "time" mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/rs/zerolog/log" "github.com/spf13/viper" "git.coopgo.io/coopgo-apps/parcoursmob/core/application" "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/geo" cache "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage" "git.coopgo.io/coopgo-apps/parcoursmob/services" ) // MCPServer represents the MCP HTTP server type MCPServer struct { cfg *viper.Viper services *services.ServicesHandler kv cache.KVHandler filestorage cache.FileStorage applicationHandler *application.ApplicationHandler mcpServer *mcpsdk.Server geoService *geo.GeoService } // NewMCPServer creates a new MCP server instance func NewMCPServer( cfg *viper.Viper, svc *services.ServicesHandler, applicationHandler *application.ApplicationHandler, kv cache.KVHandler, filestorage cache.FileStorage, ) *MCPServer { // Initialize geocoding service geoType := cfg.GetString("geo.type") baseURL := cfg.GetString("geo." + geoType + ".url") autocompleteEndpoint := cfg.GetString("geo." + geoType + ".autocomplete") geoService := geo.NewGeoService(geoType, baseURL, autocompleteEndpoint) server := &MCPServer{ cfg: cfg, services: svc, kv: kv, filestorage: filestorage, applicationHandler: applicationHandler, geoService: geoService, } // Create MCP server with implementation info server.mcpServer = mcpsdk.NewServer(&mcpsdk.Implementation{ Name: "parcoursmob-mcp-server", Version: "1.0.0", }, nil) // Register journey search tool server.registerJourneySearchTool() return server } // Run starts the MCP HTTP server with SSE transport func Run( cfg *viper.Viper, svc *services.ServicesHandler, applicationHandler *application.ApplicationHandler, kv cache.KVHandler, filestorage cache.FileStorage, ) { address := cfg.GetString("server.mcp.listen") service_name := cfg.GetString("service_name") mcpServer := NewMCPServer(cfg, svc, applicationHandler, kv, filestorage) // Create HTTP server with SSE transport mux := http.NewServeMux() // Health check endpoint mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"healthy"}`)) }) // MCP Streamable HTTP endpoint (preferred over SSE as of 2025-03-26 spec) streamHandler := mcpsdk.NewStreamableHTTPHandler(func(r *http.Request) *mcpsdk.Server { return mcpServer.mcpServer }, nil) mux.Handle("/", streamHandler) // Also support legacy SSE endpoint for backwards compatibility sseHandler := mcpsdk.NewSSEHandler(func(r *http.Request) *mcpsdk.Server { return mcpServer.mcpServer }, nil) mux.Handle("/sse", sseHandler) srv := &http.Server{ Handler: mux, Addr: address, WriteTimeout: 60 * time.Second, ReadTimeout: 30 * time.Second, } log.Info().Str("service_name", service_name).Str("address", address).Msg("Running MCP HTTP server with SSE transport") err := srv.ListenAndServe() log.Error().Err(err).Msg("MCP server error") }