//go:build summercms_mcp package surf import ( "fmt" "net/http" "net/netip" "strings" "git.golem15.com/golem15/summercms/modules/backpack" "git.golem15.com/golem15/summercms/modules/pact" "git.golem15.com/golem15/summercms/modules/party" "git.golem15.com/golem15/summercms/modules/pier" ) // Binaries built with the summercms_mcp tag (MCPBuildTag) link the MCP // module: this file sets the mount hook BuildRouter calls when mcp.enabled // is true, with an adapter that gives pier the route table, the compiled // handler lookup and the two mount kinds without pier importing surf. func init() { mountPier = func(r *Router, app *backpack.App, plugins []party.Plugin) error { return pier.Mount(pierHost{r: r, trusted: TrustedProxies(app.Config)}, app, plugins) } } // pierHost adapts a Router to pier.Host. trusted holds http.trusted_proxies // for ClientIP. type pierHost struct { r *Router trusted []netip.Prefix } // ClientIP answers the request's client address with the application's // trusted proxies applied, the same rule the throttle middleware uses; pier // puts it on the token resolvers' context. func (h pierHost) ClientIP(req *http.Request) string { return ClientIP(req, h.trusted) } func (h pierHost) Routes() []pier.RouteInfo { routes := h.r.Routes() out := make([]pier.RouteInfo, len(routes)) for i, ri := range routes { out[i] = pier.RouteInfo{Method: ri.Method, Pattern: ri.Pattern, PluginID: ri.PluginID, Middleware: ri.Middleware} } return out } func (h pierHost) Handler(method, pattern string) (http.Handler, bool) { return h.r.Handler(method, pattern) } func (h pierHost) MountStream(method, path string, handler http.Handler, middleware ...string) error { h.r.BindPlugin(pier.PluginID) return h.r.MountStream(method, path, handler, middleware...) } // MountJSON registers a plain GET route (no middleware) under the pier // pseudo-plugin; only GET is supported. func (h pierHost) MountJSON(method, path string, handler http.Handler) error { if strings.ToUpper(strings.TrimSpace(method)) != http.MethodGet { return fmt.Errorf("surf: pier may mount JSON routes with GET only, got %q", method) } if owner, ok := h.Owner(http.MethodGet, path); ok { return fmt.Errorf("surf: route GET %s (plugin %q) collides with a route the MCP module mounts", path, owner) } h.r.BindPlugin(pier.PluginID) h.r.GroupRaw("", nil, func(g pact.Router) { g.Get(path, handler.ServeHTTP) }) return nil } func (h pierHost) Owner(method, path string) (string, bool) { owner, ok := h.r.seen[strings.ToUpper(strings.TrimSpace(method))+" "+path] return owner, ok }