Skip to main content
Glama
settings_controller.go8.96 kB
package tui import ( "encoding/json" "fmt" "os" "github.com/charmbracelet/bubbles/list" "github.com/standardbeagle/brummer/internal/config" "github.com/standardbeagle/brummer/internal/logs" "github.com/standardbeagle/brummer/internal/mcp" "github.com/standardbeagle/brummer/internal/process" "github.com/standardbeagle/brummer/internal/proxy" "github.com/standardbeagle/brummer/internal/tui/filebrowser" ) // SettingsController manages settings view state and rendering type SettingsController struct { // Dependencies config *config.Config mcpServer MCPServerInterface processMgr *process.Manager workingDir string fileBrowser *filebrowser.Controller settingsList list.Model logStore *logs.Store proxyServer *proxy.Server // View state width int height int headerHeight int footerHeight int } // NewSettingsController creates a new settings controller func NewSettingsController(cfg *config.Config, mcpServer MCPServerInterface, processMgr *process.Manager, workingDir string, fileBrowserController *filebrowser.Controller, logStore *logs.Store, proxyServer *proxy.Server) *SettingsController { settingsList := list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0) settingsList.Title = "Package Manager Settings" settingsList.SetShowStatusBar(false) return &SettingsController{ config: cfg, mcpServer: mcpServer, processMgr: processMgr, workingDir: workingDir, fileBrowser: fileBrowserController, logStore: logStore, proxyServer: proxyServer, settingsList: settingsList, } } // UpdateSize updates the viewport dimensions with pre-calculated content height func (s *SettingsController) UpdateSize(width, height, headerHeight, footerHeight, contentHeight int) { s.width = width s.height = height s.headerHeight = headerHeight s.footerHeight = footerHeight // Update settings list size s.settingsList.SetSize(width, contentHeight) } // GetSettingsListPointer returns a pointer to the settings list (for backward compatibility) func (s *SettingsController) GetSettingsListPointer() *list.Model { return &s.settingsList } // GetSettingsList returns the settings list func (s *SettingsController) GetSettingsList() *list.Model { return &s.settingsList } // UpdateSettingsList updates the settings list with all configuration options func (s *SettingsController) UpdateSettingsList() { // settingsList is now a value type, no need for nil check installedMgrs := s.processMgr.GetInstalledPackageManagers() currentMgr := s.processMgr.GetCurrentPackageManager() items := make([]list.Item, 0) // Server Information section (prominently displayed at top) items = append(items, settingsSectionItem{title: "🔗 Server Information"}) // MCP Server info mcpStatus := "🔴 Not Running" if s.mcpServer != nil && s.mcpServer.IsRunning() { mcpStatus = "🟢 Running" } // Get actual port from MCP server if running actualPort := 7777 // default if s.mcpServer != nil && s.mcpServer.IsRunning() { actualPort = s.mcpServer.GetPort() } items = append(items, mcpServerInfoItem{ port: actualPort, status: mcpStatus, }) // Add MCP endpoint information - always show URL for easy access mcpURL := fmt.Sprintf("http://localhost:%d/mcp", actualPort) items = append(items, infoDisplayItem{ title: "🔗 MCP Endpoint", description: fmt.Sprintf("%s (JSON-RPC 2.0 - all tools, resources & prompts)", mcpURL), value: mcpURL, copyable: true, }) // Proxy Server info (if running and in full mode for PAC) if s.proxyServer != nil && s.proxyServer.IsRunning() && s.proxyServer.GetMode() == proxy.ProxyModeFull { items = append(items, proxyInfoItem{ pacURL: s.proxyServer.GetPACURL(), mode: s.proxyServer.GetMode(), port: s.proxyServer.GetPort(), }) } // Package Manager section items = append(items, settingsSectionItem{title: "📦 Package Managers"}) for _, mgr := range installedMgrs { item := packageManagerSettingsItem{packageManagerItem{ manager: mgr, current: mgr.Manager == currentMgr, fromJSON: s.processMgr.IsPackageManagerFromJSON(mgr.Manager), }} items = append(items, item) } // MCP Integration section items = append(items, settingsSectionItem{title: "🛠 MCP Integration"}) mcpTools := mcp.GetSupportedTools() installedTools := mcp.GetInstalledTools() installedSet := make(map[string]bool) for _, tool := range installedTools { installedSet[tool] = true } for _, tool := range mcpTools { if tool.Supported { item := mcpInstallItem{ tool: tool, installed: installedSet[tool.Name], } items = append(items, item) } } // Add custom file browser option items = append(items, mcpFileBrowserItem{}) s.settingsList.SetItems(items) } // UpdateFileBrowserList updates the file browser list func (s *SettingsController) UpdateFileBrowserList() { // This method is delegated to the Model since it needs access to fileBrowserList // The Model will handle updating the list items from the file browser controller } // InstallMCPForTool installs an MCP tool func (s *SettingsController) InstallMCPForTool(tool mcp.Tool) { // Get current executable path execPath, err := os.Executable() if err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error getting executable path: %v", err), true) } return } // Generate config config := mcp.GenerateBrummerConfig(execPath, 7777) // Install if err := mcp.InstallForTool(tool, config); err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error installing MCP for %s: %v", tool.Name, err), true) } } else { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Successfully configured %s with Brummer!", tool.Name), false) } s.UpdateSettingsList() } } // InstallMCPToFile installs MCP configuration to a specific file func (s *SettingsController) InstallMCPToFile(filePath string) { s.installMCPToFile(filePath) } // installMCPToFile is the internal implementation func (s *SettingsController) installMCPToFile(filePath string) { // Get current executable path execPath, err := os.Executable() if err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error getting executable path: %v", err), true) } return } // Generate config config := mcp.GenerateBrummerConfig(execPath, 7777) // Read existing file data, err := os.ReadFile(filePath) if err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error reading file %s: %v", filePath, err), true) } return } var existingData map[string]interface{} if err := json.Unmarshal(data, &existingData); err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error parsing JSON in %s: %v", filePath, err), true) } return } // Try common MCP config formats if existingData["mcpServers"] == nil { existingData["mcpServers"] = make(map[string]interface{}) } servers := existingData["mcpServers"].(map[string]interface{}) servers["brummer"] = map[string]interface{}{ "command": config.Command, "args": config.Args, "env": config.Env, } // Write back newData, err := json.MarshalIndent(existingData, "", " ") if err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error marshaling JSON: %v", err), true) } return } if err := os.WriteFile(filePath, newData, 0644); err != nil { if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Error writing to %s: %v", filePath, err), true) } return } if s.logStore != nil { s.logStore.Add("system", "System", fmt.Sprintf("Successfully configured %s with Brummer!", filePath), false) } } // GetCLICommandFromConfig gets the CLI command for a specific task // Note: This is a placeholder implementation since the config structure // doesn't currently have Tools configuration func (s *SettingsController) GetCLICommandFromConfig(configKey string, task string) (string, []string, error) { // TODO: Implement when Tools configuration is available in Config return "", nil, fmt.Errorf("tools configuration not implemented") } // getCLICommand gets the CLI command for a tool // Note: This is a placeholder implementation func (s *SettingsController) getCLICommand(configKey string) string { // TODO: Implement when Tools configuration is available in Config return "" } // Render renders the settings view func (s *SettingsController) Render() string { // Calculate available height for the list - no local headers availableHeight := s.height - s.headerHeight - s.footerHeight // Update list size and render s.settingsList.SetSize(s.width, availableHeight) return s.settingsList.View() } // Settings item types are defined in model.go to avoid circular dependencies // since they implement list.Item interface and are used in the settings list

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/standardbeagle/brummer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server