parcoursmob/servers/mcp/mcp.go

110 lines
3.1 KiB
Go

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")
}