Skip to content

Commit 12bb11a

Browse files
authored
Add support for --runtime-file (#69)
* Support --runtime-file flag * Support MCPD_RUNTIME_FILE env var * Support XDG Base Directory Specification for XDG_CONFIG_HOME * Update public docs * Fix bug in init flags for loggers
1 parent 0113a78 commit 12bb11a

File tree

20 files changed

+690
-138
lines changed

20 files changed

+690
-138
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ mcpd
3737
# Project config files
3838
.mcpd.toml
3939

40+
# Accidental files that shouldn't end up in the project folder
41+
secrets.dev.toml
42+
4043
# Log files
4144
*.log
4245

cmd/config/args/clear.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package args
22

33
import (
44
"fmt"
5-
"os"
6-
"path/filepath"
75
"strings"
86

97
"github.com/spf13/cobra"
108

119
"github.com/mozilla-ai/mcpd/v2/internal/cmd"
1210
"github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1311
"github.com/mozilla-ai/mcpd/v2/internal/context"
12+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1413
)
1514

1615
type ClearCmd struct {
@@ -28,7 +27,7 @@ func NewClearCmd(baseCmd *cmd.BaseCmd, _ ...options.CmdOption) (*cobra.Command,
2827
Short: "Clears configured command line arguments (flags) for an MCP server.",
2928
Long: `Clears configured command line arguments (flags) for an MCP server,
3029
from the runtime context configuration file
31-
(e.g. ~/.mcpd/secrets.dev.toml).`,
30+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3231
RunE: c.run,
3332
Args: cobra.MinimumNArgs(1), // server-name
3433
}
@@ -54,13 +53,7 @@ func (c *ClearCmd) run(cmd *cobra.Command, args []string) error {
5453
"please re-run the command with the --force flag", serverName)
5554
}
5655

57-
homeDir, err := os.UserHomeDir()
58-
if err != nil {
59-
return fmt.Errorf("failed to get user home directory: %w", err)
60-
}
61-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml") // TODO: Allow configuration via flag
62-
63-
cfg, err := context.LoadExecutionContextConfig(filePath)
56+
cfg, err := context.LoadExecutionContextConfig(flags.RuntimeFile)
6457
if err != nil {
6558
return fmt.Errorf("failed to load execution context config: %w", err)
6659
}
@@ -69,7 +62,7 @@ func (c *ClearCmd) run(cmd *cobra.Command, args []string) error {
6962
// Clear and reassign the server in the config.
7063
s.Args = []string{}
7164
cfg.Servers[serverName] = s
72-
if err := context.SaveExecutionContextConfig(filePath, cfg); err != nil {
65+
if err := context.SaveExecutionContextConfig(flags.RuntimeFile, cfg); err != nil {
7366
return fmt.Errorf("failed to clear argument config for '%s': %w", serverName, err)
7467
}
7568
}

cmd/config/args/list.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package args
22

33
import (
44
"fmt"
5-
"os"
6-
"path/filepath"
75
"strings"
86

97
"github.com/spf13/cobra"
108

119
"github.com/mozilla-ai/mcpd/v2/internal/cmd"
1210
"github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1311
"github.com/mozilla-ai/mcpd/v2/internal/context"
12+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1413
)
1514

1615
type ListCmd struct {
@@ -26,7 +25,7 @@ func NewListCmd(baseCmd *cmd.BaseCmd, _ ...options.CmdOption) (*cobra.Command, e
2625
Use: "list <server-name>",
2726
Short: "Lists the configured command line arguments (flags) for a specific MCP server.",
2827
Long: `Lists the configured command line arguments (flags) for a specific MCP server, using the runtime context configuration file
29-
(e.g. ~/.mcpd/secrets.dev.toml).`,
28+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3029
RunE: c.run,
3130
Args: cobra.MinimumNArgs(1), // server-name
3231
}
@@ -40,13 +39,7 @@ func (c *ListCmd) run(cmd *cobra.Command, args []string) error {
4039
return fmt.Errorf("server-name is required")
4140
}
4241

43-
homeDir, err := os.UserHomeDir()
44-
if err != nil {
45-
return fmt.Errorf("failed to get user home directory: %w", err)
46-
}
47-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml") // TODO: Allow configuration via flag
48-
49-
cfg, err := context.LoadExecutionContextConfig(filePath)
42+
cfg, err := context.LoadExecutionContextConfig(flags.RuntimeFile)
5043
if err != nil {
5144
return fmt.Errorf("failed to load execution context config: %w", err)
5245
}

cmd/config/args/remove.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package args
33
import (
44
"fmt"
55
"maps"
6-
"os"
7-
"path/filepath"
86
"slices"
97
"strings"
108

@@ -14,6 +12,7 @@ import (
1412
"github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1513
"github.com/mozilla-ai/mcpd/v2/internal/config"
1614
"github.com/mozilla-ai/mcpd/v2/internal/context"
15+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1716
)
1817

1918
type RemoveCmd struct {
@@ -31,7 +30,7 @@ func NewRemoveCmd(baseCmd *cmd.BaseCmd, _ ...options.CmdOption) (*cobra.Command,
3130
Example: "remove time -- --local-timezone",
3231
Short: "Remove command line arguments (flags) for an MCP server.",
3332
Long: `Remove command line arguments (flags) for a specified MCP server in the runtime context configuration file
34-
(e.g. ~/.mcpd/secrets.dev.toml).`,
33+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3534
RunE: c.run,
3635
Args: cobra.MinimumNArgs(2), // server-name + --arg ...
3736
}
@@ -52,13 +51,7 @@ func (c *RemoveCmd) run(cmd *cobra.Command, args []string) error {
5251
argMap[key] = struct{}{}
5352
}
5453

55-
homeDir, err := os.UserHomeDir()
56-
if err != nil {
57-
return fmt.Errorf("failed to get user home directory: %w", err)
58-
}
59-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml") // TODO: Allow configuration via flag
60-
61-
cfg, err := context.LoadExecutionContextConfig(filePath)
54+
cfg, err := context.LoadExecutionContextConfig(flags.RuntimeFile)
6255
if err != nil {
6356
return fmt.Errorf("failed to load execution context config: %w", err)
6457
}
@@ -73,7 +66,7 @@ func (c *RemoveCmd) run(cmd *cobra.Command, args []string) error {
7366
serverCtx.Args = filtered
7467
cfg.Servers[serverName] = serverCtx
7568

76-
if err := context.SaveExecutionContextConfig(filePath, cfg); err != nil {
69+
if err := context.SaveExecutionContextConfig(flags.RuntimeFile, cfg); err != nil {
7770
return fmt.Errorf("failed to save config: %w", err)
7871
}
7972
}

cmd/config/args/set.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package args
22

33
import (
44
"fmt"
5-
"os"
6-
"path/filepath"
75
"strings"
86

97
"github.com/spf13/cobra"
@@ -12,6 +10,7 @@ import (
1210
cmdopts "github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1311
"github.com/mozilla-ai/mcpd/v2/internal/config"
1412
"github.com/mozilla-ai/mcpd/v2/internal/context"
13+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1514
)
1615

1716
type SetCmd struct {
@@ -27,7 +26,7 @@ func NewSetCmd(baseCmd *cmd.BaseCmd, _ ...cmdopts.CmdOption) (*cobra.Command, er
2726
Use: "set <server-name> -- --arg=value [--arg=value ...]",
2827
Short: "Set startup command line arguments (flags) for an MCP server.",
2928
Long: `Set startup command line arguments (flags) for an MCP server in the runtime context configuration file
30-
(~/.mcpd/secrets.dev.toml).`,
29+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3130
RunE: c.run,
3231
Args: func(cmd *cobra.Command, args []string) error {
3332
if cmd.ArgsLenAtDash() < 1 || strings.TrimSpace(args[0]) == "" {
@@ -53,14 +52,7 @@ func (c *SetCmd) run(cmd *cobra.Command, args []string) error {
5352
fmt.Fprintf(cmd.OutOrStdout(), "args: %#v\n", args)
5453

5554
normalizedArgs := config.NormalizeArgs(args[1:])
56-
57-
homeDir, err := os.UserHomeDir()
58-
if err != nil {
59-
return fmt.Errorf("failed to get user home directory: %w", err)
60-
}
61-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml")
62-
63-
cfg, err := context.LoadOrInitExecutionContext(filePath)
55+
cfg, err := context.LoadOrInitExecutionContext(flags.RuntimeFile)
6456
if err != nil {
6557
return fmt.Errorf("failed to load execution context config: %w", err)
6658
}
@@ -72,10 +64,11 @@ func (c *SetCmd) run(cmd *cobra.Command, args []string) error {
7264
}
7365
cfg.Servers[serverName] = serverCtx
7466

75-
if err := context.SaveExecutionContextConfig(filePath, cfg); err != nil {
67+
if err := context.SaveExecutionContextConfig(flags.RuntimeFile, cfg); err != nil {
7668
return fmt.Errorf("failed to save config: %w", err)
7769
}
7870

7971
fmt.Fprintf(cmd.OutOrStdout(), "✓ Startup arguments set for server '%s': %v\n", serverName, normalizedArgs)
72+
8073
return nil
8174
}

cmd/config/env/clear.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package env
22

33
import (
44
"fmt"
5-
"os"
6-
"path/filepath"
75
"strings"
86

97
"github.com/spf13/cobra"
108

119
"github.com/mozilla-ai/mcpd/v2/internal/cmd"
1210
"github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1311
"github.com/mozilla-ai/mcpd/v2/internal/context"
12+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1413
)
1514

1615
type ClearCmd struct {
@@ -27,7 +26,7 @@ func NewClearCmd(baseCmd *cmd.BaseCmd, _ ...options.CmdOption) (*cobra.Command,
2726
Use: "clear <server-name>",
2827
Short: "Clears configured environment variables for an MCP server.",
2928
Long: `Clears environment variables for a specified MCP server from the runtime context configuration file
30-
(e.g. ~/.mcpd/secrets.dev.toml).`,
29+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3130
RunE: c.run,
3231
Args: cobra.MinimumNArgs(1), // server-name
3332
}
@@ -53,13 +52,7 @@ func (c *ClearCmd) run(cmd *cobra.Command, args []string) error {
5352
"please re-run the command with the --force flag", serverName)
5453
}
5554

56-
homeDir, err := os.UserHomeDir()
57-
if err != nil {
58-
return fmt.Errorf("failed to get user home directory: %w", err)
59-
}
60-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml") // TODO: Allow configuration via flag
61-
62-
cfg, err := context.LoadExecutionContextConfig(filePath)
55+
cfg, err := context.LoadExecutionContextConfig(flags.RuntimeFile)
6356
if err != nil {
6457
return fmt.Errorf("failed to load execution context config: %w", err)
6558
}
@@ -68,11 +61,12 @@ func (c *ClearCmd) run(cmd *cobra.Command, args []string) error {
6861
// Clear the env map and reassign the server in the config.
6962
s.Env = make(map[string]string)
7063
cfg.Servers[serverName] = s
71-
if err := context.SaveExecutionContextConfig(filePath, cfg); err != nil {
64+
if err := context.SaveExecutionContextConfig(flags.RuntimeFile, cfg); err != nil {
7265
return fmt.Errorf("failed to clear env var config for '%s': %w", serverName, err)
7366
}
7467
}
7568

7669
fmt.Fprintf(cmd.OutOrStdout(), "✓ Environment variables cleared for server '%s'\n", serverName)
70+
7771
return nil
7872
}

cmd/config/env/list.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package env
22

33
import (
44
"fmt"
5-
"os"
6-
"path/filepath"
75
"sort"
86
"strings"
97

@@ -12,6 +10,7 @@ import (
1210
"github.com/mozilla-ai/mcpd/v2/internal/cmd"
1311
"github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1412
"github.com/mozilla-ai/mcpd/v2/internal/context"
13+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1514
)
1615

1716
type ListCmd struct {
@@ -27,7 +26,7 @@ func NewListCmd(baseCmd *cmd.BaseCmd, _ ...options.CmdOption) (*cobra.Command, e
2726
Use: "list <server-name>",
2827
Short: "Lists configured environment variables for a specific MCP server.",
2928
Long: `Lists configured environment variables for a specific MCP server, using the runtime context configuration file
30-
(e.g. ~/.mcpd/secrets.dev.toml).`,
29+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3130
RunE: c.run,
3231
Args: cobra.MinimumNArgs(1), // server-name
3332
}
@@ -41,13 +40,7 @@ func (c *ListCmd) run(cmd *cobra.Command, args []string) error {
4140
return fmt.Errorf("server-name is required")
4241
}
4342

44-
homeDir, err := os.UserHomeDir()
45-
if err != nil {
46-
return fmt.Errorf("failed to get user home directory: %w", err)
47-
}
48-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml") // TODO: Allow configuration via flag
49-
50-
cfg, err := context.LoadExecutionContextConfig(filePath)
43+
cfg, err := context.LoadExecutionContextConfig(flags.RuntimeFile)
5144
if err != nil {
5245
return fmt.Errorf("failed to load execution context config: %w", err)
5346
}

cmd/config/env/remove.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package env
22

33
import (
44
"fmt"
5-
"os"
6-
"path/filepath"
75
"strings"
86

97
"github.com/spf13/cobra"
108

119
"github.com/mozilla-ai/mcpd/v2/internal/cmd"
1210
"github.com/mozilla-ai/mcpd/v2/internal/cmd/options"
1311
"github.com/mozilla-ai/mcpd/v2/internal/context"
12+
"github.com/mozilla-ai/mcpd/v2/internal/flags"
1413
)
1514

1615
type RemoveCmd struct {
@@ -28,7 +27,7 @@ func NewRemoveCmd(baseCmd *cmd.BaseCmd, _ ...options.CmdOption) (*cobra.Command,
2827
Use: "remove <server-name> KEY [KEY ...]",
2928
Short: "Remove environment variables for an MCP server.",
3029
Long: `Remove environment variables for a specified MCP server in the runtime context configuration file
31-
(e.g. ~/.mcpd/secrets.dev.toml).`,
30+
(e.g. ~/.config/mcpd/secrets.dev.toml).`,
3231
RunE: c.run,
3332
Args: cobra.MinimumNArgs(2), // server-name + KEY ...
3433
}
@@ -49,13 +48,7 @@ func (c *RemoveCmd) run(cmd *cobra.Command, args []string) error {
4948
envMap[key] = struct{}{}
5049
}
5150

52-
homeDir, err := os.UserHomeDir()
53-
if err != nil {
54-
return fmt.Errorf("failed to get user home directory: %w", err)
55-
}
56-
filePath := filepath.Join(homeDir, ".mcpd", "secrets.dev.toml") // TODO: Allow configuration via flag
57-
58-
cfg, err := context.LoadExecutionContextConfig(filePath)
51+
cfg, err := context.LoadExecutionContextConfig(flags.RuntimeFile)
5952
if err != nil {
6053
return fmt.Errorf("failed to load execution context config: %w", err)
6154
}
@@ -67,7 +60,7 @@ func (c *RemoveCmd) run(cmd *cobra.Command, args []string) error {
6760
}
6861
cfg.Servers[serverName] = serverCtx
6962

70-
if err := context.SaveExecutionContextConfig(filePath, cfg); err != nil {
63+
if err := context.SaveExecutionContextConfig(flags.RuntimeFile, cfg); err != nil {
7164
return fmt.Errorf("failed to save config: %w", err)
7265
}
7366
}

0 commit comments

Comments
 (0)