diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4ee3c3..c3c5e93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,6 +48,16 @@ jobs: chmod +x mcp-linux-amd64 zip -j mcp-linux-amd64.zip mcp-linux-amd64 + - name: Build for Windows (AMD64) + run: | + GOOS=windows GOARCH=amd64 go build -o mcp-windows-amd64.exe -ldflags="-X 'main.Version=${{ env.VERSION }}'" . + zip -j mcp-windows-amd64.zip mcp-windows-amd64.exe + + - name: Build for Windows (ARM64) + run: | + GOOS=windows GOARCH=arm64 go build -o mcp-windows-arm64.exe -ldflags="-X 'main.Version=${{ env.VERSION }}'" . + zip -j mcp-windows-arm64.zip mcp-windows-arm64.exe + - name: Create Release id: create_release uses: softprops/action-gh-release@v1 @@ -60,6 +70,8 @@ jobs: mcp-darwin-amd64.zip mcp-darwin-arm64.zip mcp-linux-amd64.zip + mcp-windows-amd64.zip + mcp-windows-arm64.zip generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index bfa4b88..dddbe22 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ app mcp .env mcp.json +*.exe diff --git a/.kiro/specs/windows-support/design.md b/.kiro/specs/windows-support/design.md new file mode 100644 index 0000000..39133ff --- /dev/null +++ b/.kiro/specs/windows-support/design.md @@ -0,0 +1,366 @@ +# Design Document: Windows Support for MCP CLI + +## Overview + +This design document outlines the approach for adding comprehensive Windows platform support to the MCP CLI tool. The implementation will enable Windows users to manage MCP server configurations using platform-appropriate file paths, configuration locations, and build artifacts while maintaining backward compatibility with existing Unix-like system support. + +The design follows a platform-agnostic approach using Go's standard library capabilities for cross-platform path handling and runtime OS detection. This ensures the codebase remains maintainable while supporting multiple platforms seamlessly. + +## Architecture + +### Platform Detection Strategy + +The MCP CLI will use Go's `runtime.GOOS` constant to detect the operating system at runtime. This approach eliminates the need for user-specified flags and ensures automatic platform-appropriate behavior. + +```go +import "runtime" + +func getPlatformConfigDir() string { + if runtime.GOOS == "windows" { + return getWindowsConfigDir() + } + return getUnixConfigDir() +} +``` + +### Path Handling Strategy + +All path operations will use Go's `filepath` package, which automatically handles platform-specific path separators and conventions. The key principles are: + +1. **Use `filepath.Join()` for all path construction** - automatically uses correct separators +2. **Use `os.UserHomeDir()` for home directory** - works across platforms +3. **Consistent configuration directories** - use `.config/mcp` on all platforms +4. **Environment variable expansion** - use Unix-style syntax (`$VAR`, `${VAR}`) on all platforms + +### Configuration Directory Structure + +**All platforms (macOS, Linux, Windows):** +- Config directory: `$HOME/.config/mcp/` (Unix) or `%USERPROFILE%\.config\mcp\` (Windows) +- Compose file: `$HOME/.config/mcp/mcp-compose.yml` (Unix) or `%USERPROFILE%\.config\mcp\mcp-compose.yml` (Windows) +- CLI config: `$HOME/.config/mcp/config.json` (Unix) or `%USERPROFILE%\.config\mcp\config.json` (Windows) + +**Note:** All platforms use the same `.config/mcp` directory structure relative to the user's home directory. This simplifies implementation and provides a consistent experience across platforms. + +## Components and Interfaces + +### 1. Platform Utilities Module (`cmd/platform.go`) + +A new file containing platform-specific utility functions: + +```go +package cmd + +import ( + "os" + "path/filepath" + "runtime" +) + +// Note: getConfigDir() already exists in cmd/config.go and works for all platforms +// It returns filepath.Join(homeDir, ".config", "mcp") which is cross-platform compatible + +// getPlatformToolPath returns the platform-appropriate path for a tool +// Hard fails on error, consistent with getConfigDir() in config.go +func getPlatformToolPath(tool string) string { + homeDir, err := os.UserHomeDir() + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting user home directory: %v\n", err) + os.Exit(1) + } + + switch tool { + case "q-cli": + return filepath.Join(homeDir, ".aws", "amazonq", "mcp.json") + case "claude-desktop": + if runtime.GOOS == "windows" { + return filepath.Join(homeDir, "AppData", "Roaming", "Claude", "claude_desktop_config.json") + } + return filepath.Join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json") + case "cursor": + return filepath.Join(homeDir, ".cursor", "mcp.json") + case "kiro": + return filepath.Join(homeDir, ".kiro", "settings", "mcp.json") + default: + return "" + } +} +``` + +**Note:** The existing `expandEnvVars()` in `cmd/env.go` handles Unix-style syntax (`$VAR`, `${VAR}`) which works on all platforms including Windows. No changes needed for environment variable expansion. + +### 2. Modified Existing Files + +**`cmd/root.go`:** +- No changes needed - `getDefaultComposeFile()` already uses `filepath.Join()` and works cross-platform + +**`cmd/config.go`:** +- No changes needed - `getConfigDir()` already exists and works cross-platform +- Already returns `filepath.Join(homeDir, ".config", "mcp")` which works on all platforms + +**`cmd/set.go`:** +- Update `toolShortcuts` map to use `getPlatformToolPath()` function +- Ensure path handling uses `filepath` package consistently (review only) + +**`cmd/env.go`:** +- No changes needed - `expandEnvVars()` already works on all platforms with Unix-style syntax + +**`cmd/types.go`:** +- No changes needed - review to ensure all path operations use `filepath.Join()` + +### 3. Build System Updates + +**`Makefile`:** +Add Windows build targets: + +```makefile +## build-windows-amd64: build Windows AMD64 binary +.PHONY: build-windows-amd64 +build-windows-amd64: test + GOOS=windows GOARCH=amd64 go build -o ./mcp.exe -v + +## build-windows-arm64: build Windows ARM64 binary +.PHONY: build-windows-arm64 +build-windows-arm64: test + GOOS=windows GOARCH=arm64 go build -o ./mcp-arm64.exe -v + +## build-all: build binaries for all platforms +.PHONY: build-all +build-all: build build-windows-amd64 build-windows-arm64 +``` + +**`.github/workflows/release.yml`:** +Add Windows build steps: + +```yaml +- name: Build for Windows (AMD64) + run: | + GOOS=windows GOARCH=amd64 go build -o mcp-windows-amd64.exe -ldflags="-X 'main.Version=${{ env.VERSION }}'" . + zip -j mcp-windows-amd64.zip mcp-windows-amd64.exe + +- name: Build for Windows (ARM64) + run: | + GOOS=windows GOARCH=arm64 go build -o mcp-windows-arm64.exe -ldflags="-X 'main.Version=${{ env.VERSION }}'" . + zip -j mcp-windows-arm64.zip mcp-windows-arm64.exe +``` + +Update release files section to include Windows artifacts. + +## Data Models + +No new data models are required. Existing structures (`ComposeConfig`, `Service`, `MCPConfig`, `MCPServer`, `CLIConfig`) remain unchanged. + +## Error Handling + +### Platform-Specific Error Messages + +Error messages will include platform context when relevant: + +```go +// Note: getConfigDir() already exists in cmd/config.go +// No changes needed - it already works cross-platform: +// +// func getConfigDir() string { +// homeDir, err := os.UserHomeDir() +// if err != nil { +// fmt.Fprintf(os.Stderr, "Error getting user home directory: %v\n", err) +// os.Exit(1) +// } +// return filepath.Join(homeDir, ".config", "mcp") +// } +``` + +### Path Validation + +Add validation for Windows-specific path edge cases: + +```go +func validatePath(path string) error { + // Check for invalid characters on Windows + if runtime.GOOS == "windows" { + invalidChars := []string{"<", ">", ":", "\"", "|", "?", "*"} + for _, char := range invalidChars { + if strings.Contains(path, char) { + return fmt.Errorf("path contains invalid character: %s", char) + } + } + } + return nil +} +``` + + + +## Testing Strategy + +### Unit Tests + +Create platform-specific unit tests in `cmd/platform_test.go`: + +```go +// Note: getConfigDir() already exists and doesn't need testing changes +// It already works cross-platform using filepath.Join() + +func TestGetConfigDirCrossPlatform(t *testing.T) { + dir := getConfigDir() + + // All platforms should use .config/mcp + if !strings.Contains(dir, ".config") || !strings.Contains(dir, "mcp") { + t.Errorf("Expected .config/mcp path, got: %s", dir) + } + + // Verify filepath.Join() used correct separator automatically + homeDir, _ := os.UserHomeDir() + expected := filepath.Join(homeDir, ".config", "mcp") + if dir != expected { + t.Errorf("Expected %s, got: %s", expected, dir) + } +} + +func TestGetPlatformToolPath(t *testing.T) { + tests := []struct { + tool string + contains string + }{ + {"q-cli", ".aws"}, + {"claude-desktop", "Claude"}, + {"cursor", ".cursor"}, + {"kiro", ".kiro"}, + } + + for _, tt := range tests { + t.Run(tt.tool, func(t *testing.T) { + path := getPlatformToolPath(tt.tool) + if !strings.Contains(path, tt.contains) { + t.Errorf("Expected path to contain %s, got: %s", tt.contains, path) + } + }) + } +} + +// Note: expandEnvVars() already exists in cmd/env.go and has tests +// Only add this test if implementing expandEnvVarsMultiPlatform() + +func TestExpandEnvVarsCrossPlatform(t *testing.T) { + envVars := map[string]string{ + "TEST_VAR": "test_value", + } + + // Test Unix-style expansion (works on all platforms) + result := expandEnvVars("${TEST_VAR}/path", envVars) + if !strings.Contains(result, "test_value") { + t.Errorf("Failed to expand ${VAR} style variable") + } + + result = expandEnvVars("$TEST_VAR/path", envVars) + if !strings.Contains(result, "test_value") { + t.Errorf("Failed to expand $VAR style variable") + } +} +``` + +### Integration Tests + +Update existing integration tests to be platform-aware: + +```go +func TestEndToEndWindows(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("Skipping Windows-specific test") + } + + // Test Windows-specific functionality + // - Path handling with backslashes + // - AppData directory usage + // - .exe binary execution +} +``` + +### Manual Testing Checklist + +1. **Path Handling:** + - Verify config files are created in `%USERPROFILE%\.config\mcp\` on Windows + - Test with paths containing spaces + - Test with UNC paths (\\server\share) + - Test with different drive letters + - Verify path separators are correct (backslashes on Windows) + +2. **Tool Shortcuts:** + - Verify each tool shortcut writes to correct Windows location + - Test Claude Desktop path in `%USERPROFILE%\AppData\Roaming\Claude\` + - Test Amazon Q CLI path in `%USERPROFILE%\.aws\amazonq\` + - Test Cursor path in `%USERPROFILE%\.cursor\` + - Test Kiro path in `%USERPROFILE%\.kiro\settings\` + +3. **Environment Variables:** + - Test $HOME expansion (Unix-style works on Windows too) + - Test ${USERPROFILE} expansion + - Verify Unix-style syntax works correctly on Windows + +4. **Binary Execution:** + - Test .exe binary runs on Windows + - Verify command-line arguments work correctly + - Test signal handling (Ctrl+C) + +## Implementation Phases + +### Phase 1: Core Platform Utilities +- Create `cmd/platform.go` with platform detection and path utilities +- Update existing path handling to use new utilities +- Add unit tests for platform utilities + +### Phase 2: Configuration Path Updates +- Update `cmd/root.go` to use platform-aware default paths +- Update `cmd/config.go` to use platform config directory +- Update `cmd/set.go` tool shortcuts for Windows paths +- Add integration tests + +### Phase 3: Build System Updates +- Add Windows build targets to Makefile +- Update CI/CD workflow to build Windows binaries +- Test cross-compilation from Unix systems + +### Phase 4: Documentation and Testing +- Update README with Windows installation instructions +- Add Windows-specific troubleshooting guide +- Perform comprehensive manual testing on Windows +- Update examples to show Windows paths + +## Design Decisions and Rationales + +### Decision 1: Use Home Directory (.config/mcp) on All Platforms +**Rationale:** Using the same directory structure across all platforms simplifies implementation, reduces platform-specific code, and provides a consistent user experience. The `.config` directory is a well-established convention that works on all platforms. This eliminates the need for `getPlatformConfigDir()` and makes the codebase more maintainable. + +### Decision 2: Maintain Backward Compatibility +**Rationale:** Existing Unix users should not be affected. All changes are additive, using runtime detection to choose appropriate behavior. + +### Decision 3: Use Unix-style Environment Variable Syntax on All Platforms +**Rationale:** The existing `expandEnvVars()` function supports Unix-style syntax (`$VAR`, `${VAR}`) which works on all platforms including Windows. Most cross-platform CLI tools use Unix-style syntax universally for consistency. No need to add Windows `%VAR%` support. + +### Decision 4: Use Go's filepath Package Exclusively +**Rationale:** Ensures consistent, platform-appropriate path handling without manual string manipulation. Reduces bugs and improves maintainability. + +### Decision 5: Build Both AMD64 and ARM64 Windows Binaries +**Rationale:** Windows on ARM is growing (Surface devices, VMs). Providing native ARM64 binaries improves performance and user experience. + +## Security Considerations + +1. **Path Traversal:** Validate user-provided paths to prevent directory traversal attacks +2. **Environment Variable Injection:** Sanitize environment variables before expansion +3. **File Permissions:** Use appropriate permissions (0644 for files, 0755 for directories) on all platforms +4. **UNC Path Handling:** Be cautious with UNC paths to prevent network-based attacks + +## Performance Considerations + +- Platform detection using `runtime.GOOS` is a compile-time constant, zero runtime overhead +- `filepath.Join()` is optimized and has minimal performance impact +- No additional dependencies required, keeping binary size small + +## Compatibility Matrix + +| Platform | Architecture | Status | Binary Name | +|----------|-------------|--------|-------------| +| macOS | AMD64 | Existing | mcp-darwin-amd64 | +| macOS | ARM64 | Existing | mcp-darwin-arm64 | +| Linux | AMD64 | Existing | mcp-linux-amd64 | +| Windows | AMD64 | New | mcp-windows-amd64.exe | +| Windows | ARM64 | New | mcp-windows-arm64.exe | diff --git a/.kiro/specs/windows-support/requirements.md b/.kiro/specs/windows-support/requirements.md new file mode 100644 index 0000000..23cef90 --- /dev/null +++ b/.kiro/specs/windows-support/requirements.md @@ -0,0 +1,89 @@ +# Requirements Document + +## Introduction + +This document specifies the requirements for adding Windows platform support to the MCP CLI tool. The tool currently supports Unix-like systems (macOS, Linux) and needs to be extended to work seamlessly on Windows, including proper file path handling, platform-specific configuration locations, and Windows binary builds. + +## Glossary + +- **MCP CLI**: The command-line tool for managing Model Context Protocol server configurations +- **Platform-specific paths**: File system paths that differ between operating systems (e.g., `$HOME/.config` vs `%USERPROFILE%\AppData\Local`) +- **Build system**: The automated compilation and packaging infrastructure (Make, CI/CD) +- **Binary**: The compiled executable file for the MCP CLI tool +- **Configuration file**: YAML or JSON files storing MCP server settings +- **AI tool**: Applications like Amazon Q CLI, Claude Desktop, or Cursor that consume MCP configurations + +## Requirements + +### Requirement 1 + +**User Story:** As a Windows user, I want to use the MCP CLI tool on my Windows machine, so that I can manage MCP server configurations without switching to a Unix-like system + +#### Acceptance Criteria + +1. WHEN a Windows user runs the MCP CLI binary, THE MCP CLI SHALL execute successfully on Windows operating systems +2. THE MCP CLI SHALL use Windows-style file paths with backslashes when operating on Windows +3. THE MCP CLI SHALL resolve user home directories using Windows environment variables on Windows systems +4. THE MCP CLI SHALL handle both forward slashes and backslashes in user-provided paths on Windows +5. WHEN the MCP CLI accesses configuration files, THE MCP CLI SHALL use platform-appropriate path separators + +### Requirement 2 + +**User Story:** As a Windows user, I want the MCP CLI to store and read configuration files from my home directory, so that my configurations are consistent with Unix-like systems and easily accessible + +#### Acceptance Criteria + +1. THE MCP CLI SHALL store the default compose file at `%USERPROFILE%\.config\mcp\mcp-compose.yml` on Windows +2. THE MCP CLI SHALL store the CLI config at `%USERPROFILE%\.config\mcp\config.json` on Windows +3. THE MCP CLI SHALL read environment variables from `.env` files in the same directory as the compose file on Windows +4. WHEN writing Amazon Q CLI configuration, THE MCP CLI SHALL write to `%USERPROFILE%\.aws\amazonq\mcp.json` on Windows +5. WHEN writing Claude Desktop configuration, THE MCP CLI SHALL write to `%USERPROFILE%\AppData\Roaming\Claude\claude_desktop_config.json` on Windows +6. WHEN writing Cursor configuration, THE MCP CLI SHALL write to `%USERPROFILE%\.cursor\mcp.json` on Windows +7. THE MCP CLI SHALL use the same `.config\mcp` directory structure on Windows as on Unix-like systems + +### Requirement 3 + +**User Story:** As a developer, I want to build Windows binaries for the MCP CLI, so that I can distribute the tool to Windows users + +#### Acceptance Criteria + +1. THE build system SHALL support cross-compilation to Windows AMD64 architecture +2. THE build system SHALL support cross-compilation to Windows ARM64 architecture +3. THE build system SHALL produce executables with `.exe` extension for Windows +4. WHEN running `make build-windows`, THE build system SHALL create Windows binaries in the output directory +5. THE Makefile SHALL include targets for building Windows binaries from any platform + +### Requirement 4 + +**User Story:** As a maintainer, I want the CI/CD pipeline to automatically build Windows binaries, so that releases include Windows support without manual intervention + +#### Acceptance Criteria + +1. THE CI/CD pipeline SHALL build Windows AMD64 binaries on every release +2. THE CI/CD pipeline SHALL build Windows ARM64 binaries on every release +3. THE CI/CD pipeline SHALL include Windows binaries in release artifacts +4. WHEN a release is published, THE CI/CD pipeline SHALL upload Windows executables with clear naming conventions +5. THE CI/CD pipeline SHALL verify Windows binaries are executable before publishing + +### Requirement 5 + +**User Story:** As a Windows user, I want the MCP CLI to handle Windows-specific path edge cases, so that the tool works reliably with various path formats + +#### Acceptance Criteria + +1. THE MCP CLI SHALL handle UNC paths (e.g., `\\server\share`) on Windows +2. THE MCP CLI SHALL handle drive letters (e.g., `C:\`, `D:\`) on Windows +3. THE MCP CLI SHALL expand Unix-style environment variables in paths (e.g., `$USERPROFILE`, `${HOME}`) on Windows +4. WHEN a user provides a relative path, THE MCP CLI SHALL resolve it correctly on Windows + +### Requirement 6 + +**User Story:** As a user on any platform, I want the MCP CLI to automatically detect my operating system, so that I don't need to specify platform-specific flags + +#### Acceptance Criteria + +1. THE MCP CLI SHALL automatically detect the operating system at runtime +2. THE MCP CLI SHALL select platform-appropriate default paths without user configuration +3. THE MCP CLI SHALL use platform-appropriate path operations without user intervention +4. WHEN running on Windows, THE MCP CLI SHALL use Windows-specific behavior automatically +5. WHEN running on Unix-like systems, THE MCP CLI SHALL use Unix-specific behavior automatically diff --git a/.kiro/specs/windows-support/tasks.md b/.kiro/specs/windows-support/tasks.md new file mode 100644 index 0000000..39a8539 --- /dev/null +++ b/.kiro/specs/windows-support/tasks.md @@ -0,0 +1,87 @@ +# Implementation Plan: Windows Support + +- [x] 1. Create platform utilities module + + + + + + - Create `cmd/platform.go` with `getPlatformToolPath()` function + - Function should handle platform-specific tool paths (Claude Desktop differs between Windows/macOS) + - Hard fail on home directory errors, consistent with `getConfigDir()` in `config.go` + - _Requirements: 2.4, 2.5, 2.6, 6.1, 6.2, 6.3_ + +- [x] 2. Update set command to use platform utilities + + + + + + - Modify `cmd/set.go` to replace hardcoded `toolShortcuts` map with calls to `getPlatformToolPath()` + - Update `getOutputPath()` function to use the new platform utilities + - Verify all path operations use `filepath.Join()` for cross-platform compatibility + - _Requirements: 1.2, 1.5, 2.4, 2.5, 2.6_ + +- [x] 3. Add Windows build targets to Makefile + + + + + + + + + + - Add `build-windows-amd64` target for Windows AMD64 architecture + - Add `build-windows-arm64` target for Windows ARM64 architecture + - Add `build-all` target to build binaries for all platforms + - Ensure `.exe` extension is used for Windows binaries + - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_ + +- [x] 4. Update CI/CD pipeline for Windows builds + + + + + + - Modify `.github/workflows/release.yml` to build Windows AMD64 binaries + - Add Windows ARM64 binary build step + - Package Windows binaries as `.zip` files for release + - Update release artifact upload to include Windows executables + - Ensure Windows binaries have clear naming conventions (e.g., `mcp-windows-amd64.exe`) + - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5_ + +- [ ] 5. Add platform-specific tests +- [ ] 5.1 Create unit tests for `getPlatformToolPath()` + - Test each tool shortcut returns correct path + - Verify Claude Desktop path differs between Windows and macOS + - Test error handling when home directory cannot be determined + - _Requirements: 2.4, 2.5, 2.6_ + +- [ ] 5.2 Add cross-platform integration tests + - Test config directory resolution on Windows + - Test tool path resolution for all supported tools + - Verify path separators are correct for the platform + - _Requirements: 1.1, 1.2, 1.5, 6.1, 6.2, 6.3_ + +- [x] 6. Update documentation + + + + + + +- [x] 6.1 Add Windows installation instructions to README + + - Document how to download and install Windows binaries + - Include instructions for both AMD64 and ARM64 architectures + - Add Windows-specific configuration examples + - _Requirements: 1.1, 3.1, 3.2_ + +- [x] 6.2 Create Windows troubleshooting guide + + + - Document common Windows path issues (UNC paths, drive letters) + - Explain Unix-style environment variable syntax usage on Windows + - Add examples of Windows-specific configurations + - _Requirements: 5.1, 5.2, 5.3, 5.4_ diff --git a/Makefile b/Makefile index f701db6..d6191e9 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,20 @@ test: build: test go build -o ./app -v +## build-windows-amd64: build Windows AMD64 binary +.PHONY: build-windows-amd64 +build-windows-amd64: test + GOOS=windows GOARCH=amd64 go build -o ./mcp.exe -v + +## build-windows-arm64: build Windows ARM64 binary +.PHONY: build-windows-arm64 +build-windows-arm64: test + GOOS=windows GOARCH=arm64 go build -o ./mcp-arm64.exe -v + +## build-all: build binaries for all platforms +.PHONY: build-all +build-all: build build-windows-amd64 build-windows-arm64 + ## autobuild: auto build when source files change .PHONY: autobuild autobuild: diff --git a/README.md b/README.md index 002ac59..6c2caaa 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,38 @@ Model Context Protocol (MCP) is a new technology and still evolving. As I've bee I decided to write up some specs for a tool (written in Go) that could help with these pain points and try to "vibe code" it. This is the result. Please don't judge the code quality. I didn't write or edit a single line :) +## Installation + +### macOS and Linux + +Download the appropriate binary for your platform from the [releases page](https://github.com/ +jritsema/mcp-cli/releases): + +- macOS AMD64: `mcp-darwin-amd64` +- macOS ARM64: `mcp-darwin-arm64` +- Linux AMD64: `mcp-linux-amd64` + +Make the binary executable and move it to your PATH: + +```sh +chmod +x mcp-darwin-arm64 +sudo mv mcp-darwin-arm64 /usr/local/bin/mcp +``` + +### Windows + +Download the appropriate Windows binary for your architecture from the [releases page](https://github.com/ +jritsema/mcp-cli/releases): + +- **Windows AMD64** (most common): `mcp-windows-amd64.zip` +- **Windows ARM64** (Surface devices, ARM VMs): `mcp-windows-arm64.zip` + +#### Installation Steps + +1. Download the appropriate `.zip` file for your architecture +2. Extract the `.exe` file from the archive +3. Move `mcp.exe` to a directory in your PATH, or add the directory to your PATH + ## Usage MCP CLI simplifies managing MCP server configurations through a YAML-based approach. @@ -111,10 +143,18 @@ mcp clear -c /path/to/output/mcp.json MCP CLI supports these predefined tool shortcuts for popular AI tools: -- `q-cli` - Amazon Q CLI (`$HOME/.aws/amazonq/mcp.json`) -- `claude-desktop` - Claude Desktop (`$HOME/Library/Application Support/Claude/claude_desktop_config.json`) -- `cursor` - Cursor IDE (`$HOME/.cursor/mcp.json`) -- `kiro` - Kiro IDE (`$HOME/.kiro/settings/mcp.json`) +- `q-cli` - Amazon Q CLI + - macOS/Linux: `$HOME/.aws/amazonq/mcp.json` + - Windows: `%USERPROFILE%\.aws\amazonq\mcp.json` +- `claude-desktop` - Claude Desktop + - macOS: `$HOME/Library/Application Support/Claude/claude_desktop_config.json` + - Windows: `%USERPROFILE%\AppData\Roaming\Claude\claude_desktop_config.json` +- `cursor` - Cursor IDE + - macOS/Linux: `$HOME/.cursor/mcp.json` + - Windows: `%USERPROFILE%\.cursor\mcp.json` +- `kiro` - Kiro IDE + - macOS/Linux: `$HOME/.kiro/settings/mcp.json` + - Windows: `%USERPROFILE%\.kiro\settings\mcp.json` ### Setting Default AI Tool @@ -284,6 +324,7 @@ services: build build a binary autobuild auto build when source files change dockerbuild build project into a docker container image + build-all build binaries for all platforms start build and run local project deploy build code into a container and deploy it to the cloud dev environment ``` diff --git a/cmd/platform.go b/cmd/platform.go new file mode 100644 index 0000000..3a8a350 --- /dev/null +++ b/cmd/platform.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "runtime" +) + +// getPlatformToolPath returns the platform-appropriate path for a tool +// Hard fails on error, consistent with getConfigDir() in config.go +func getPlatformToolPath(tool string) string { + homeDir, err := os.UserHomeDir() + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting user home directory: %v\n", err) + os.Exit(1) + } + + switch tool { + case "q-cli": + return filepath.Join(homeDir, ".aws", "amazonq", "mcp.json") + case "claude-desktop": + if runtime.GOOS == "windows" { + return filepath.Join(homeDir, "AppData", "Roaming", "Claude", "claude_desktop_config.json") + } + return filepath.Join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json") + case "cursor": + return filepath.Join(homeDir, ".cursor", "mcp.json") + case "kiro": + return filepath.Join(homeDir, ".kiro", "settings", "mcp.json") + default: + return "" + } +} diff --git a/cmd/set.go b/cmd/set.go index 1510748..d3077fe 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -16,14 +16,6 @@ var ( singleServer string ) -// Tool shortcuts mapping -var toolShortcuts = map[string]string{ - "q-cli": filepath.Join("${HOME}", ".aws", "amazonq", "mcp.json"), - "claude-desktop": filepath.Join("${HOME}", "Library", "Application Support", "Claude", "claude_desktop_config.json"), - "cursor": filepath.Join("${HOME}", ".cursor", "mcp.json"), - "kiro": filepath.Join("${HOME}", ".kiro", "settings", "mcp.json"), -} - // setCmd represents the set command var setCmd = &cobra.Command{ Use: "set [profile]", @@ -111,18 +103,11 @@ func getOutputPath(envVars map[string]string) (string, error) { } if toolShortcut != "" { - path, exists := toolShortcuts[toolShortcut] - if !exists { + path := getPlatformToolPath(toolShortcut) + if path == "" { return "", fmt.Errorf("unknown tool shortcut: %s", toolShortcut) } - // Replace ${HOME} with actual home directory - homeDir, err := os.UserHomeDir() - if err != nil { - return "", err - } - path = strings.Replace(path, "${HOME}", homeDir, 1) - // Create directory if it doesn't exist dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0755); err != nil { diff --git a/cmd/tool_shortcuts_test.go b/cmd/tool_shortcuts_test.go index 863d1ca..702582b 100644 --- a/cmd/tool_shortcuts_test.go +++ b/cmd/tool_shortcuts_test.go @@ -16,26 +16,24 @@ func TestToolShortcutPaths(t *testing.T) { } // Test all existing tool shortcuts - expectedPaths := map[string]string{ - "q-cli": filepath.Join(homeDir, ".aws", "amazonq", "mcp.json"), - "claude-desktop": filepath.Join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json"), - "cursor": filepath.Join(homeDir, ".cursor", "mcp.json"), - "kiro": filepath.Join(homeDir, ".kiro", "settings", "mcp.json"), - } - - for tool, expectedPath := range expectedPaths { - // Test path expansion - shortcutPath, exists := toolShortcuts[tool] - if !exists { - t.Errorf("Tool shortcut '%s' not found", tool) + tools := []string{"q-cli", "claude-desktop", "cursor", "kiro"} + + for _, tool := range tools { + // Test path from getPlatformToolPath + path := getPlatformToolPath(tool) + if path == "" { + t.Errorf("Tool shortcut '%s' returned empty path", tool) continue } - // Expand ${HOME} in the shortcut path - expandedPath := strings.Replace(shortcutPath, "${HOME}", homeDir, 1) + // Verify path starts with home directory + if !strings.HasPrefix(path, homeDir) { + t.Errorf("Tool '%s': path should start with home directory, got '%s'", tool, path) + } - if expandedPath != expectedPath { - t.Errorf("Tool '%s': expected path '%s', got '%s'", tool, expectedPath, expandedPath) + // Verify path is absolute + if !filepath.IsAbs(path) { + t.Errorf("Tool '%s': path should be absolute, got '%s'", tool, path) } } } @@ -43,7 +41,9 @@ func TestToolShortcutPaths(t *testing.T) { // TestGetOutputPathWithToolShortcuts tests the getOutputPath function with tool shortcuts func TestGetOutputPathWithToolShortcuts(t *testing.T) { // Test that getOutputPath works with tool shortcuts (using real paths) - for tool := range toolShortcuts { + tools := []string{"q-cli", "claude-desktop", "cursor", "kiro"} + + for _, tool := range tools { // Simulate setting the tool shortcut originalToolShortcut := toolShortcut toolShortcut = tool @@ -100,7 +100,8 @@ func TestToolCompatibilityWithLocalServers(t *testing.T) { } // Test that all tool shortcuts work with local servers - for tool := range toolShortcuts { + tools := []string{"q-cli", "claude-desktop", "cursor", "kiro"} + for _, tool := range tools { err := ValidateToolSupport(tool, localServers) if err != nil { t.Errorf("Tool '%s' should support local servers: %v", tool, err)