fix(mcp): expand home paths for local stdio server commands
This commit is contained in:
parent
2da05c2ad3
commit
f4dbac0dcf
4 changed files with 60 additions and 1 deletions
|
|
@ -223,6 +223,9 @@ func buildServerConfig(target string, args []string, opts addOptions) (config.MC
|
|||
if err := validateLocalCommandPath(target); err != nil {
|
||||
return config.MCPServerConfig{}, err
|
||||
}
|
||||
if isLocalCommandPath(command) {
|
||||
command = expandHomePath(command)
|
||||
}
|
||||
|
||||
server.Command = command
|
||||
server.Args = commandArgs
|
||||
|
|
|
|||
|
|
@ -152,6 +152,28 @@ func TestMCPAddRejectsNonExecutableLocalCommand(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "not executable")
|
||||
}
|
||||
|
||||
func TestMCPAddExpandsHomeInSavedLocalCommand(t *testing.T) {
|
||||
configPath := setupMCPConfigEnv(t)
|
||||
|
||||
homeDir := t.TempDir()
|
||||
t.Setenv("HOME", homeDir)
|
||||
t.Setenv("USERPROFILE", homeDir)
|
||||
|
||||
localCmd := filepath.Join(homeDir, "bin", "my-mcp")
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(localCmd), 0o755))
|
||||
require.NoError(t, os.WriteFile(localCmd, []byte("#!/bin/sh\nexit 0\n"), 0o755))
|
||||
|
||||
tildeCmd := "~" + string(os.PathSeparator) + filepath.Join("bin", "my-mcp")
|
||||
|
||||
cmd := NewMCPCommand()
|
||||
_, err := executeCommand(cmd, []string{"add", "local-home", tildeCmd}, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := readMCPConfig(t, configPath)
|
||||
server := cfg.Tools.MCP.Servers["local-home"]
|
||||
assert.Equal(t, localCmd, server.Command)
|
||||
}
|
||||
|
||||
func TestMCPAddShowsClearErrorForRemoteURLWithoutTransport(t *testing.T) {
|
||||
setupMCPConfigEnv(t)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,24 @@ type headerTransport struct {
|
|||
headers map[string]string
|
||||
}
|
||||
|
||||
func expandHomeCommandPath(command string) string {
|
||||
if command == "" || command[0] != '~' {
|
||||
return command
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return command
|
||||
}
|
||||
if command == "~" {
|
||||
return home
|
||||
}
|
||||
if strings.HasPrefix(command, "~/") || strings.HasPrefix(command, "~\\") {
|
||||
return filepath.Join(home, command[2:])
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// Clone the request to avoid modifying the original
|
||||
req = req.Clone(req.Context())
|
||||
|
|
@ -324,7 +342,7 @@ func (m *Manager) ConnectServer(
|
|||
"command": cfg.Command,
|
||||
})
|
||||
// Create command with context
|
||||
cmd := exec.CommandContext(ctx, cfg.Command, cfg.Args...)
|
||||
cmd := exec.CommandContext(ctx, expandHomeCommandPath(cfg.Command), cfg.Args...)
|
||||
|
||||
// Build environment variables with proper override semantics
|
||||
// Use a map to ensure config variables override file variables
|
||||
|
|
|
|||
|
|
@ -136,6 +136,22 @@ func TestLoadEnvFileNotFound(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestExpandHomeCommandPath(t *testing.T) {
|
||||
homeDir := t.TempDir()
|
||||
t.Setenv("HOME", homeDir)
|
||||
t.Setenv("USERPROFILE", homeDir)
|
||||
|
||||
want := filepath.Join(homeDir, "bin", "my-mcp")
|
||||
got := expandHomeCommandPath("~" + string(os.PathSeparator) + filepath.Join("bin", "my-mcp"))
|
||||
if got != want {
|
||||
t.Fatalf("expandHomeCommandPath() = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
if got := expandHomeCommandPath("npx"); got != "npx" {
|
||||
t.Fatalf("expandHomeCommandPath() should leave bare commands unchanged, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvFilePriority(t *testing.T) {
|
||||
// Create a temporary .env file
|
||||
tmpDir := t.TempDir()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue