summaryrefslogtreecommitdiff
path: root/internal/hexaimcp/run.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/hexaimcp/run.go')
-rw-r--r--internal/hexaimcp/run.go64
1 files changed, 51 insertions, 13 deletions
diff --git a/internal/hexaimcp/run.go b/internal/hexaimcp/run.go
index b6fed8c..eb4fe83 100644
--- a/internal/hexaimcp/run.go
+++ b/internal/hexaimcp/run.go
@@ -15,6 +15,15 @@ import (
"codeberg.org/snonux/hexai/internal/slashcommands"
)
+// MCPOverrides holds CLI flag values that override config settings.
+// These are passed explicitly from the CLI entrypoint instead of using
+// environment variables, avoiding the code smell of os.Setenv in production code.
+type MCPOverrides struct {
+ PromptsDir string
+ SlashCommandSync bool
+ SlashCommandDir string
+}
+
// ServerRunner interface allows dependency injection for testing.
type ServerRunner interface {
Run() error
@@ -34,16 +43,19 @@ func defaultServerFactory(r io.Reader, w io.Writer, logger *log.Logger, store pr
return mcp.NewServer(r, w, logger, store, syncer)
}
-// Run starts the MCP server with the given configuration.
+// Run starts the MCP server with the given configuration and overrides.
// This is the main entry point called from cmd/hexai-mcp-server/main.go.
-func Run(logPath, configPath string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
- return RunWithFactory(logPath, configPath, stdin, stdout, stderr, defaultServerFactory)
+func Run(logPath, configPath string, overrides MCPOverrides, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ return RunWithFactory(logPath, configPath, overrides, stdin, stdout, stderr, defaultServerFactory)
}
// RunWithFactory allows test injection of server factory.
+// Overrides are applied to the loaded config before use, allowing CLI flags
+// to take precedence over config file and environment variable settings.
func RunWithFactory(
logPath string,
configPath string,
+ overrides MCPOverrides,
stdin io.Reader,
stdout io.Writer,
stderr io.Writer,
@@ -63,10 +75,16 @@ func RunWithFactory(
logger.Printf("hexai-mcp-server starting")
logger.Printf("WARNING: hexai-mcp-server is DEPRECATED and experimental - not actively maintained")
- // Load configuration
+ // Load configuration and apply CLI overrides
cfg := loadConfig(logger, configPath)
+ applyOverrides(&cfg, overrides)
+
+ return runServer(cfg, logger, stdin, stdout, factory)
+}
- // Determine prompts directory
+// runServer creates the prompt store, syncer, and runs the MCP server.
+func runServer(cfg appconfig.App, logger *log.Logger, stdin io.Reader, stdout io.Writer, factory ServerFactory) error {
+ // Determine prompts directory from config (overrides already applied)
promptsDir, err := getPromptsDir(cfg)
if err != nil {
return fmt.Errorf("cannot determine prompts directory: %w", err)
@@ -127,15 +145,27 @@ func loadConfig(logger *log.Logger, configPath string) appconfig.App {
return appconfig.LoadWithOptions(logger, opts)
}
-// getPromptsDir determines the prompts directory from config or environment.
-// Precedence: CLI flag (via config) > env var > config file > default XDG location.
-func getPromptsDir(cfg appconfig.App) (string, error) {
- // Check environment variable first
- if envDir := strings.TrimSpace(os.Getenv("HEXAI_MCP_PROMPTS_DIR")); envDir != "" {
- return expandPath(envDir)
+// applyOverrides applies CLI flag overrides to the loaded config.
+// This replaces the previous approach of using os.Setenv to pass values.
+func applyOverrides(cfg *appconfig.App, overrides MCPOverrides) {
+ if overrides.PromptsDir != "" {
+ cfg.MCPPromptsDir = overrides.PromptsDir
+ }
+ if overrides.SlashCommandSync {
+ cfg.MCPSlashCommandSync = true
}
+ if overrides.SlashCommandDir != "" {
+ cfg.MCPSlashCommandDir = overrides.SlashCommandDir
+ }
+}
- // Check config file
+// getPromptsDir determines the prompts directory from config.
+// Precedence: CLI flag (via overrides applied to config) > env var (via
+// applyMCPEnv in config loading) > config file > default XDG location.
+// The env var HEXAI_MCP_PROMPTS_DIR is still supported through the config
+// loading pipeline in appconfig, not read directly here.
+func getPromptsDir(cfg appconfig.App) (string, error) {
+ // Check config (which already includes env var and CLI overrides)
if cfgDir := strings.TrimSpace(cfg.MCPPromptsDir); cfgDir != "" {
return expandPath(cfgDir)
}
@@ -182,7 +212,8 @@ func createSyncer(cfg appconfig.App, logger *log.Logger) (*slashcommands.Syncer,
}
// RunBackfill performs a one-time sync of all prompts and exits.
-func RunBackfill(logPath, configPath string) error {
+// Overrides are applied to the loaded config before use.
+func RunBackfill(logPath, configPath string, overrides MCPOverrides) error {
logger, err := setupLogger(logPath)
if err != nil {
return fmt.Errorf("cannot setup logger: %w", err)
@@ -195,7 +226,9 @@ func RunBackfill(logPath, configPath string) error {
logger.Printf("hexai-mcp-server backfill starting")
+ // Load configuration and apply CLI overrides
cfg := loadConfig(logger, configPath)
+ applyOverrides(&cfg, overrides)
// Force enable sync for backfill
if cfg.MCPSlashCommandDir == "" {
@@ -203,6 +236,11 @@ func RunBackfill(logPath, configPath string) error {
}
cfg.MCPSlashCommandSync = true
+ return executeBackfill(cfg, logger)
+}
+
+// executeBackfill creates the syncer, store, and performs the backfill sync.
+func executeBackfill(cfg appconfig.App, logger *log.Logger) error {
syncer, err := createSyncer(cfg, logger)
if err != nil {
return fmt.Errorf("cannot create syncer: %w", err)