Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 3 additions & 20 deletions cmd/mcpproxy/activity_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -780,14 +780,7 @@ func init() {

// getActivityClient creates an HTTP client for the daemon
func getActivityClient(logger *zap.SugaredLogger) (*cliclient.Client, error) {
// Load config - use explicit config file if provided via -c flag
var cfg *config.Config
var err error
if configFile != "" {
cfg, err = config.LoadFromFile(configFile)
} else {
cfg, err = config.Load()
}
cfg, err := loadActivityConfig()
if err != nil {
return nil, fmt.Errorf("failed to load config: %w", err)
}
Expand Down Expand Up @@ -966,12 +959,7 @@ func runActivityWatch(cmd *cobra.Command, _ []string) error {
defer func() { _ = logger.Sync() }()

// Load config to get endpoint - use same logic as getActivityClient
var cfg *config.Config
if configFile != "" {
cfg, err = config.LoadFromFile(configFile)
} else {
cfg, err = config.Load()
}
cfg, err := loadActivityConfig()
if err != nil {
return outputActivityError(err, "CONFIG_ERROR")
}
Expand Down Expand Up @@ -1566,12 +1554,7 @@ func runActivityExport(cmd *cobra.Command, _ []string) error {
}

// Load config - use explicit config file if provided via -c flag
var cfg *config.Config
if configFile != "" {
cfg, err = config.LoadFromFile(configFile)
} else {
cfg, err = config.Load()
}
cfg, err := loadActivityConfig()
if err != nil {
return outputActivityError(err, "CONFIG_ERROR")
}
Expand Down
52 changes: 52 additions & 0 deletions cmd/mcpproxy/cli_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"github.com/smart-mcp-proxy/mcpproxy-go/internal/config"
)

// loadCLIConfig loads a CLI command's config from explicitPath (the command's
// --config flag) when set, falling back to the default search path, and applies
// the global --data-dir flag on top (GH #854/#897/#908). Without the DataDir
// override, socket.DetectSocketPath probes the default data dir and the command
// either reports "daemon is not reachable" or silently talks to the wrong
// daemon instance. Every per-command loader below must go through this helper
// (enforced by TestLoadersHonorGlobalDataDirFlag).
func loadCLIConfig(explicitPath string) (*config.Config, error) {
var cfg *config.Config
var err error
if explicitPath != "" {
cfg, err = config.LoadFromFile(explicitPath)
} else {
cfg, err = config.Load()
}
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
}

// Per-command loaders for commands whose config flow previously used bare
// config.Load() and ignored --data-dir (GH #908).

func loadActivityConfig() (*config.Config, error) {
return loadCLIConfig(configFile)
}

func loadCredentialConfig() (*config.Config, error) {
return loadCLIConfig(configFile)
}

func loadSecurityConfig() (*config.Config, error) {
return loadCLIConfig(configFile)
}

func loadTrustCertConfig() (*config.Config, error) {
return loadCLIConfig(configFile)
}

func loadTUIConfig() (*config.Config, error) {
return loadCLIConfig(configFile)
}
19 changes: 1 addition & 18 deletions cmd/mcpproxy/connect_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,22 +272,5 @@ func printConnectResult(result *connect.ConnectResult, formatter clioutput.Outpu
}

func loadConnectConfig() (*config.Config, error) {
if configFile != "" {
cfg, err := config.LoadFromFile(configFile)
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
}
cfg, err := config.Load()
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
return loadCLIConfig(configFile)
}
51 changes: 36 additions & 15 deletions cmd/mcpproxy/credential_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"go.uber.org/zap"

"github.com/smart-mcp-proxy/mcpproxy-go/internal/cliclient"
"github.com/smart-mcp-proxy/mcpproxy-go/internal/config"
)

// Server-edition flags for the credential command group. The credential broker
Expand Down Expand Up @@ -119,22 +118,29 @@ Examples:
// --- client wiring ---

// resolveCredentialBaseURL determines the server base URL from the --url flag,
// the MCPPROXY_SERVER_URL env var, or the local listen address in config.
func resolveCredentialBaseURL() string {
// the MCPPROXY_SERVER_URL env var, or the local listen address in config. An
// explicitly passed --config must fail loudly; only the implicit default-path
// load falls back to the default URL.
func resolveCredentialBaseURL() (string, error) {
if credServerURL != "" {
return strings.TrimRight(credServerURL, "/")
return strings.TrimRight(credServerURL, "/"), nil
}
if env := os.Getenv("MCPPROXY_SERVER_URL"); env != "" {
return strings.TrimRight(env, "/")
return strings.TrimRight(env, "/"), nil
}
if cfg, err := config.Load(); err == nil && cfg.Listen != "" {
cfg, err := loadCredentialConfig()
if err != nil {
if configFile != "" {
return "", fmt.Errorf("failed to load config: %w", err)
}
} else if cfg.Listen != "" {
listen := cfg.Listen
if strings.HasPrefix(listen, ":") {
listen = "127.0.0.1" + listen
}
return "http://" + listen
return "http://" + listen, nil
}
return "http://127.0.0.1:8080"
return "http://127.0.0.1:8080", nil
}

// resolveCredentialToken returns the user JWT from --token or MCPPROXY_TOKEN.
Expand All @@ -145,16 +151,22 @@ func resolveCredentialToken() string {
return os.Getenv("MCPPROXY_TOKEN")
}

func newCredentialClient() (*cliclient.Client, string) {
baseURL := resolveCredentialBaseURL()
func newCredentialClient() (*cliclient.Client, string, error) {
baseURL, err := resolveCredentialBaseURL()
if err != nil {
return nil, "", err
}
logger, _ := zap.NewProduction()
return cliclient.NewClientWithBearer(baseURL, resolveCredentialToken(), logger.Sugar()), baseURL
return cliclient.NewClientWithBearer(baseURL, resolveCredentialToken(), logger.Sugar()), baseURL, nil
}

// --- run functions ---

func runCredentialList(_ *cobra.Command, _ []string) error {
client, _ := newCredentialClient()
client, _, err := newCredentialClient()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

Expand All @@ -166,7 +178,10 @@ func runCredentialList(_ *cobra.Command, _ []string) error {
}

func runCredentialStatus(_ *cobra.Command, args []string) error {
client, _ := newCredentialClient()
client, _, err := newCredentialClient()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

Expand All @@ -182,7 +197,10 @@ func runCredentialStatus(_ *cobra.Command, args []string) error {
}

func runCredentialConnect(_ *cobra.Command, args []string) error {
baseURL := resolveCredentialBaseURL()
baseURL, err := resolveCredentialBaseURL()
if err != nil {
return err
}
connectURL := credentialConnectURL(baseURL, args[0])

format := ResolveOutputFormat()
Expand All @@ -196,7 +214,10 @@ func runCredentialConnect(_ *cobra.Command, args []string) error {
}

func runCredentialRemove(_ *cobra.Command, args []string) error {
client, _ := newCredentialClient()
client, _, err := newCredentialClient()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

Expand Down
20 changes: 16 additions & 4 deletions cmd/mcpproxy/credential_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package main

import (
"path/filepath"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -93,13 +94,24 @@ func TestCredentialConnectURL_EscapesServer(t *testing.T) {
func TestResolveCredentialBaseURL_FlagAndEnv(t *testing.T) {
t.Setenv("MCPPROXY_SERVER_URL", "https://env.example.com/")
credServerURL = ""
if got := resolveCredentialBaseURL(); got != "https://env.example.com" {
t.Errorf("env base URL = %q", got)
if got, err := resolveCredentialBaseURL(); err != nil || got != "https://env.example.com" {
t.Errorf("env base URL = %q, err = %v", got, err)
}
credServerURL = "https://flag.example.com/"
defer func() { credServerURL = "" }()
if got := resolveCredentialBaseURL(); got != "https://flag.example.com" {
t.Errorf("flag base URL = %q (flag should win)", got)
if got, err := resolveCredentialBaseURL(); err != nil || got != "https://flag.example.com" {
t.Errorf("flag base URL = %q, err = %v (flag should win)", got, err)
}
}

func TestResolveCredentialBaseURL_ExplicitConfigErrors(t *testing.T) {
t.Setenv("MCPPROXY_SERVER_URL", "")
credServerURL = ""
oldConfigFile := configFile
configFile = filepath.Join(t.TempDir(), "missing.json")
defer func() { configFile = oldConfigFile }()
if got, err := resolveCredentialBaseURL(); err == nil {
t.Errorf("expected error for missing explicit --config, got base URL %q", got)
}
}

Expand Down
11 changes: 11 additions & 0 deletions cmd/mcpproxy/datadir_flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ func TestLoadersHonorGlobalDataDirFlag(t *testing.T) {
{"code", func(p string) { codeConfigPath = p }, loadCodeConfig},
{"tools", func(p string) { configPath = p }, loadToolsConfig},
{"token", func(p string) { tokenConfigPath = p }, loadTokenConfig},
{"activity", func(p string) { configFile = p }, loadActivityConfig},
{"connect", func(p string) { configFile = p }, loadConnectConfig},
{"credential", func(p string) { configFile = p }, loadCredentialConfig},
{"feedback", func(p string) { configFile = p }, loadFeedbackConfig},
{"registry", func(p string) { registryConfigPath = p }, loadRegistryConfig},
{"security", func(p string) { configFile = p }, loadSecurityConfig},
{"status", func(p string) { configFile = p }, loadStatusConfig},
{"telemetry", func(p string) { configFile = p }, loadTelemetryConfig},
{"trust-cert", func(p string) { configFile = p }, loadTrustCertConfig},
{"tui", func(p string) { configFile = p }, loadTUIConfig},
{"upstream", func(p string) { upstreamConfigPath = p }, loadUpstreamConfig},
}

for _, tc := range cases {
Expand Down
21 changes: 1 addition & 20 deletions cmd/mcpproxy/doctor_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,26 +527,7 @@ func outputDiagnostics(diag map[string]interface{}, info map[string]interface{},
}

func loadDoctorConfig() (*config.Config, error) {
if doctorConfigPath != "" {
cfg, err := config.LoadFromFile(doctorConfigPath)
if err != nil {
return nil, err
}
// Respect global --data-dir flag
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
}
cfg, err := config.Load()
if err != nil {
return nil, err
}
// Respect global --data-dir flag
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
return loadCLIConfig(doctorConfigPath)
}

func createDoctorLogger(level string) (*zap.Logger, error) {
Expand Down
19 changes: 1 addition & 18 deletions cmd/mcpproxy/feedback_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,5 @@ func runFeedback(cmd *cobra.Command, args []string) error {
}

func loadFeedbackConfig() (*config.Config, error) {
if configFile != "" {
cfg, err := config.LoadFromFile(configFile)
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
}
cfg, err := config.Load()
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
return loadCLIConfig(configFile)
}
13 changes: 1 addition & 12 deletions cmd/mcpproxy/security_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,10 @@ Examples:
// `mcpproxy security ...` commands behave consistently with `mcpproxy serve`,
// `mcpproxy status`, and `mcpproxy upstream ...`.
func newSecurityCLIClient() (*cliclient.Client, *config.Config, error) {
var (
cfg *config.Config
err error
)
if configFile != "" {
cfg, err = config.LoadFromFile(configFile)
} else {
cfg, err = config.Load()
}
cfg, err := loadSecurityConfig()
if err != nil {
return nil, nil, fmt.Errorf("failed to load config: %w", err)
}
if dataDir != "" {
cfg.DataDir = dataDir
}

logger, _ := zap.NewProduction()
defer func() { _ = logger.Sync() }()
Expand Down
19 changes: 1 addition & 18 deletions cmd/mcpproxy/status_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,22 +530,5 @@ func printStatusTable(info *StatusInfo) {
}

func loadStatusConfig() (*config.Config, error) {
if configFile != "" {
cfg, err := config.LoadFromFile(configFile)
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
}
cfg, err := config.Load()
if err != nil {
return nil, err
}
if dataDir != "" {
cfg.DataDir = dataDir
}
return cfg, nil
return loadCLIConfig(configFile)
}
Loading
Loading