package agent import ( "context" "fmt" "log/slog" "path/filepath" "git.wntrmute.dev/mc/mc-proxy/client/mcproxy" "git.wntrmute.dev/mc/mcp/internal/registry" ) // ProxyRouter registers and removes routes with mc-proxy. // If the mc-proxy socket is not configured, it logs and returns nil // (route registration is optional). type ProxyRouter struct { client *mcproxy.Client certDir string logger *slog.Logger } // NewProxyRouter connects to mc-proxy via Unix socket. Returns nil // if socketPath is empty (route registration disabled). func NewProxyRouter(socketPath, certDir string, logger *slog.Logger) (*ProxyRouter, error) { if socketPath == "" { logger.Info("mc-proxy socket not configured, route registration disabled") return nil, nil } client, err := mcproxy.Dial(socketPath) if err != nil { return nil, fmt.Errorf("connect to mc-proxy at %s: %w", socketPath, err) } logger.Info("connected to mc-proxy", "socket", socketPath) return &ProxyRouter{ client: client, certDir: certDir, logger: logger, }, nil } // Close closes the mc-proxy connection. func (p *ProxyRouter) Close() error { if p == nil || p.client == nil { return nil } return p.client.Close() } // RegisterRoutes registers all routes for a service component with mc-proxy. // It uses the assigned host ports from the registry. func (p *ProxyRouter) RegisterRoutes(ctx context.Context, serviceName string, routes []registry.Route, hostPorts map[string]int) error { if p == nil { return nil } for _, r := range routes { hostPort, ok := hostPorts[r.Name] if !ok || hostPort == 0 { continue } hostname := r.Hostname if hostname == "" { hostname = serviceName + ".svc.mcp.metacircular.net" } listenerAddr := listenerForMode(r.Mode, r.Port) backend := fmt.Sprintf("127.0.0.1:%d", hostPort) route := mcproxy.Route{ Hostname: hostname, Backend: backend, Mode: r.Mode, BackendTLS: r.Mode == "l4", // L4 passthrough: backend handles TLS. L7: mc-proxy terminates. } // L7 routes need TLS cert/key for mc-proxy to terminate TLS. if r.Mode == "l7" { route.TLSCert = filepath.Join(p.certDir, serviceName+".pem") route.TLSKey = filepath.Join(p.certDir, serviceName+".key") } p.logger.Info("registering route", "service", serviceName, "hostname", hostname, "listener", listenerAddr, "backend", backend, "mode", r.Mode, ) if err := p.client.AddRoute(ctx, listenerAddr, route); err != nil { return fmt.Errorf("register route %s on %s: %w", hostname, listenerAddr, err) } } return nil } // RemoveRoutes removes all routes for a service component from mc-proxy. func (p *ProxyRouter) RemoveRoutes(ctx context.Context, serviceName string, routes []registry.Route) error { if p == nil { return nil } for _, r := range routes { hostname := r.Hostname if hostname == "" { hostname = serviceName + ".svc.mcp.metacircular.net" } listenerAddr := listenerForMode(r.Mode, r.Port) p.logger.Info("removing route", "service", serviceName, "hostname", hostname, "listener", listenerAddr, ) if err := p.client.RemoveRoute(ctx, listenerAddr, hostname); err != nil { // Log but don't fail — the route may already be gone. p.logger.Warn("failed to remove route", "hostname", hostname, "listener", listenerAddr, "err", err, ) } } return nil } // listenerForMode returns the mc-proxy listener address for a given // route mode and external port. func listenerForMode(mode string, port int) string { return fmt.Sprintf(":%d", port) }