@@ -21,12 +21,12 @@ type globalCfg struct {
21
21
}
22
22
23
23
type Paths struct {
24
- Linux string `yaml:"linux"`
25
- Darwin string `yaml:"darwin"`
26
- Windows string `yaml:"windows"`
24
+ Linux [] string `yaml:"linux"`
25
+ Darwin [] string `yaml:"darwin"`
26
+ Windows [] string `yaml:"windows"`
27
27
}
28
28
29
- func (c * globalCfg ) GetPathsForCurrentOS () string {
29
+ func (c * globalCfg ) GetPathsForCurrentOS () [] string {
30
30
switch runtime .GOOS {
31
31
case "darwin" :
32
32
return c .Darwin
@@ -35,7 +35,7 @@ func (c *globalCfg) GetPathsForCurrentOS() string {
35
35
case "windows" :
36
36
return c .Windows
37
37
}
38
- return ""
38
+ return [] string {}
39
39
}
40
40
41
41
func (c * globalCfg ) isInstalled () (bool , error ) {
@@ -58,7 +58,13 @@ type GlobalCfgProcessor struct {
58
58
}
59
59
60
60
func NewGlobalCfgProcessor (g globalCfg ) (* GlobalCfgProcessor , error ) {
61
- p , err := newYQProcessor (g .YQ , g .GetPathsForCurrentOS ())
61
+ paths := g .GetPathsForCurrentOS ()
62
+ if len (paths ) == 0 {
63
+ return nil , fmt .Errorf ("no paths configured for OS %s" , runtime .GOOS )
64
+ }
65
+ // All paths for a client must use same file format (json/yaml) since YQ processor
66
+ // determines encoding from first path but may operate on any path
67
+ p , err := newYQProcessor (g .YQ , paths [0 ])
62
68
if err != nil {
63
69
return nil , err
64
70
}
@@ -71,52 +77,68 @@ func NewGlobalCfgProcessor(g globalCfg) (*GlobalCfgProcessor, error) {
71
77
func (c * GlobalCfgProcessor ) ParseConfig () MCPClientCfg {
72
78
result := MCPClientCfg {MCPClientCfgBase : MCPClientCfgBase {DisplayName : c .DisplayName , Source : c .Source , Icon : c .Icon }}
73
79
74
- path := c .GetPathsForCurrentOS ()
75
- if path == "" {
80
+ paths := c .GetPathsForCurrentOS ()
81
+ if len ( paths ) == 0 {
76
82
return result
77
83
}
78
- fullPath := os .ExpandEnv (path )
79
84
result .IsOsSupported = true
80
85
81
- data , err := os .ReadFile (fullPath )
82
- if os .IsNotExist (err ) {
83
- // it's not an error for us, it just means nothing is configured/connected
84
- installed , installCheckErr := c .isInstalled ()
85
- result .IsInstalled = installed
86
- result .Err = classifyError (installCheckErr )
87
- return result
88
- }
89
-
90
- // The file was found but can't be read. Because of an old bug, it could be a directory.
91
- // In which case, we want to delete it.
92
- stat , err := os .Stat (fullPath )
93
- if err == nil && stat .IsDir () {
94
- if err := os .RemoveAll (fullPath ); err != nil {
95
- result .Err = classifyError (err )
86
+ for _ , path := range paths {
87
+ fullPath := os .ExpandEnv (path )
88
+ data , err := os .ReadFile (fullPath )
89
+ if err == nil {
90
+ result .IsInstalled = true
91
+ result .setParseResult (c .p .Parse (data ))
96
92
return result
97
93
}
98
- installed , installCheckErr := c .isInstalled ()
99
- result .IsInstalled = installed
100
- result .Err = classifyError (installCheckErr )
101
- return result
102
- }
103
94
104
- // config exists for us means it's installed (we then don't care if it's actually installed or not)
105
- result .IsInstalled = true
106
- if err != nil {
95
+ if os .IsNotExist (err ) {
96
+ continue
97
+ }
98
+
99
+ // File exists but can't be read. Because of an old bug, it could be a directory.
100
+ // In which case, we want to delete it.
101
+ stat , statErr := os .Stat (fullPath )
102
+ if statErr == nil && stat .IsDir () {
103
+ if rmErr := os .RemoveAll (fullPath ); rmErr != nil {
104
+ result .Err = classifyError (rmErr )
105
+ return result
106
+ }
107
+ continue
108
+ }
109
+
110
+ result .IsInstalled = true
107
111
result .Err = classifyError (err )
108
112
return result
109
113
}
110
- result .setParseResult (c .p .Parse (data ))
114
+
115
+ // No files found - check if the application is installed
116
+ installed , installCheckErr := c .isInstalled ()
117
+ result .IsInstalled = installed
118
+ result .Err = classifyError (installCheckErr )
111
119
return result
112
120
}
113
121
114
122
func (c * GlobalCfgProcessor ) Update (key string , server * MCPServerSTDIO ) error {
115
- file := c .GetPathsForCurrentOS ()
116
- if file == "" {
123
+ paths := c .GetPathsForCurrentOS ()
124
+ if len ( paths ) == 0 {
117
125
return fmt .Errorf ("unknown config path for OS %s" , runtime .GOOS )
118
126
}
119
- return updateConfig (os .ExpandEnv (file ), c .p .Add , c .p .Del , key , server )
127
+
128
+ // Use first existing path, or first path if none exist
129
+ var targetPath string
130
+ for _ , path := range paths {
131
+ fullPath := os .ExpandEnv (path )
132
+ if _ , err := os .Stat (fullPath ); err == nil {
133
+ targetPath = fullPath
134
+ break
135
+ }
136
+ }
137
+ if targetPath == "" {
138
+ targetPath = os .ExpandEnv (paths [0 ])
139
+ }
140
+
141
+ return updateConfig (targetPath , c .p .Add , c .p .Del , key , server )
120
142
}
121
143
122
144
func containsMCPDocker (in []MCPServerSTDIO ) bool {
0 commit comments