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 {
|
if err := validateLocalCommandPath(target); err != nil {
|
||||||
return config.MCPServerConfig{}, err
|
return config.MCPServerConfig{}, err
|
||||||
}
|
}
|
||||||
|
if isLocalCommandPath(command) {
|
||||||
|
command = expandHomePath(command)
|
||||||
|
}
|
||||||
|
|
||||||
server.Command = command
|
server.Command = command
|
||||||
server.Args = commandArgs
|
server.Args = commandArgs
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,28 @@ func TestMCPAddRejectsNonExecutableLocalCommand(t *testing.T) {
|
||||||
assert.Contains(t, err.Error(), "not executable")
|
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) {
|
func TestMCPAddShowsClearErrorForRemoteURLWithoutTransport(t *testing.T) {
|
||||||
setupMCPConfigEnv(t)
|
setupMCPConfigEnv(t)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,24 @@ type headerTransport struct {
|
||||||
headers map[string]string
|
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) {
|
func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
// Clone the request to avoid modifying the original
|
// Clone the request to avoid modifying the original
|
||||||
req = req.Clone(req.Context())
|
req = req.Clone(req.Context())
|
||||||
|
|
@ -324,7 +342,7 @@ func (m *Manager) ConnectServer(
|
||||||
"command": cfg.Command,
|
"command": cfg.Command,
|
||||||
})
|
})
|
||||||
// Create command with context
|
// 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
|
// Build environment variables with proper override semantics
|
||||||
// Use a map to ensure config variables override file variables
|
// 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) {
|
func TestEnvFilePriority(t *testing.T) {
|
||||||
// Create a temporary .env file
|
// Create a temporary .env file
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue