From 0424256e1fc91f12f5b2d2698c53c9d5ab1e5a5b Mon Sep 17 00:00:00 2001 From: aniongithub Date: Sat, 25 Apr 2026 00:48:57 -0700 Subject: [PATCH 1/2] feat: add Windows support via WSL bridge - Add install.ps1 PowerShell installer for Windows (irm ... | iex) - Add --skip-mcp-config flag to install.sh for WSL delegation - Configure Windows-side MCP clients with wsl bridge command - Update release.yml to upload install.ps1 as release asset - Update README with Windows install instructions and WSL config Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release.yml | 8 +- README.md | 25 ++++- install.ps1 | 193 ++++++++++++++++++++++++++++++++++ install.sh | 19 +++- 4 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 install.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d5ffe8..a4eea12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,11 +78,13 @@ jobs: files: ${{ matrix.artifact }}.tar.gz upload-install-script: - name: Upload install script + name: Upload install scripts runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - name: Upload install.sh + - name: Upload install.sh and install.ps1 uses: softprops/action-gh-release@v3 with: - files: install.sh + files: | + install.sh + install.ps1 diff --git a/README.md b/README.md index 6050419..91d0879 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,20 @@ Agent: "Let me build this project..." ## Quick Install +### Linux / macOS + ```bash curl -fsSL https://raw.githubusercontent.com/aniongithub/devcontainer-mcp/main/install.sh | bash ``` +### Windows (via WSL) + +```powershell +irm https://raw.githubusercontent.com/aniongithub/devcontainer-mcp/main/install.ps1 | iex +``` + +> **How it works:** The binary runs inside WSL; MCP clients on Windows launch it via `wsl ~/.local/bin/devcontainer-mcp serve`. The stdio transport works transparently across the WSL boundary. WSL 2 is required — install it with `wsl --install` if you haven't already. + Backend CLIs (`devpod`, `devcontainer`, `gh`) are detected at runtime — if one is missing, the MCP server returns a helpful error with install instructions. Binaries available for **linux-x64**, **linux-arm64**, **darwin-x64**, and **darwin-arm64**. @@ -145,7 +155,7 @@ Supported providers: **GitHub**, **AWS**, **Azure**, **GCP**, **Kubernetes** ## MCP Server Configuration -### Claude Desktop / Copilot / Cursor +### Linux / macOS ```json { @@ -158,6 +168,19 @@ Supported providers: **GitHub**, **AWS**, **Azure**, **GCP**, **Kubernetes** } ``` +### Windows (WSL bridge) + +```json +{ + "mcpServers": { + "devcontainer-mcp": { + "command": "wsl", + "args": ["~/.local/bin/devcontainer-mcp", "serve"] + } + } +} +``` + ## Prerequisites Install backend CLIs as needed — the MCP server detects them at runtime and returns helpful errors if missing: diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..653b304 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,193 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Windows installer for devcontainer-mcp (via WSL). +.DESCRIPTION + Installs the devcontainer-mcp Linux binary inside WSL and configures + Windows-side MCP clients to use the WSL bridge ("command": "wsl"). +.EXAMPLE + irm https://github.com/aniongithub/devcontainer-mcp/releases/latest/download/install.ps1 | iex +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$Repo = "aniongithub/devcontainer-mcp" +$WslBinaryPath = "~/.local/bin/devcontainer-mcp" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +function Write-Step { param([string]$Message) Write-Host "==> $Message" -ForegroundColor Cyan } +function Write-Ok { param([string]$Message) Write-Host " $([char]0x2713) $Message" -ForegroundColor Green } +function Write-Warn { param([string]$Message) Write-Host " $([char]0x26A0) $Message" -ForegroundColor Yellow } +function Write-Fail { param([string]$Message) Write-Host " $([char]0x2717) $Message" -ForegroundColor Red } + +# --------------------------------------------------------------------------- +# 1. Verify WSL is available +# --------------------------------------------------------------------------- + +Write-Step "Checking for WSL..." + +try { + $wslStatus = wsl --status 2>&1 + if ($LASTEXITCODE -ne 0) { throw "WSL returned non-zero exit code" } + Write-Ok "WSL is available" +} catch { + Write-Host "" + Write-Host "Error: WSL (Windows Subsystem for Linux) is required but not found." -ForegroundColor Red + Write-Host "" + Write-Host "Install WSL with: wsl --install" -ForegroundColor Yellow + Write-Host "Then restart your computer and run this script again." + Write-Host "More info: https://learn.microsoft.com/en-us/windows/wsl/install" + exit 1 +} + +# --------------------------------------------------------------------------- +# 2. Install binary inside WSL (reuse install.sh) +# --------------------------------------------------------------------------- + +Write-Step "Installing devcontainer-mcp binary inside WSL..." + +$installUrl = "https://raw.githubusercontent.com/$Repo/main/install.sh" +$wslResult = wsl bash -c "curl -fsSL '$installUrl' | bash -s -- --skip-mcp-config" 2>&1 +$wslResult | ForEach-Object { Write-Host " $_" } + +if ($LASTEXITCODE -ne 0) { + Write-Host "" + Write-Host "Error: Binary installation inside WSL failed." -ForegroundColor Red + Write-Host "Try running manually in WSL: curl -fsSL $installUrl | bash" + exit 1 +} + +# Verify the binary works +$versionCheck = wsl bash -lc "$WslBinaryPath --version" 2>&1 +if ($LASTEXITCODE -eq 0) { + Write-Ok "Installed: $($versionCheck.Trim())" +} else { + Write-Warn "Binary installed but could not verify version" +} + +# --------------------------------------------------------------------------- +# 3. Install SKILL.md for Windows-side agent discovery +# --------------------------------------------------------------------------- + +Write-Step "Installing SKILL.md for agent discovery..." + +$skillUrl = "https://raw.githubusercontent.com/$Repo/main/SKILL.md" +$skillDirs = @( + "$env:USERPROFILE\.copilot\skills\devcontainer-mcp" + "$env:USERPROFILE\.claude\skills\devcontainer-mcp" + "$env:USERPROFILE\.agents\skills\devcontainer-mcp" +) + +foreach ($dir in $skillDirs) { + try { + New-Item -ItemType Directory -Path $dir -Force | Out-Null + Invoke-RestMethod -Uri $skillUrl -OutFile "$dir\SKILL.md" + Write-Ok $dir + } catch { + Write-Warn "Could not write to $dir" + } +} + +# --------------------------------------------------------------------------- +# 4. Detect backend CLIs available in WSL +# --------------------------------------------------------------------------- + +Write-Host "" +Write-Host "Backend CLIs detected in WSL (install as needed — MCP server gives helpful errors if missing):" + +$backends = @( + @{ Name = "devpod"; Url = "https://devpod.sh/docs/getting-started/install" } + @{ Name = "devcontainer"; Url = "npm install -g @devcontainers/cli" } + @{ Name = "gh"; Url = "https://cli.github.com/" } +) + +foreach ($b in $backends) { + $check = wsl bash -lc "command -v $($b.Name)" 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Ok "$($b.Name)" + } else { + $label = $b.Name + if ($label -eq "gh") { $label = "gh (codespaces)" } + Write-Fail "$label — $($b.Url)" + } +} + +# --------------------------------------------------------------------------- +# 5. Configure Windows-side MCP clients (with WSL bridge) +# --------------------------------------------------------------------------- + +Write-Step "Configuring MCP clients..." + +$mcpServerEntry = @{ + command = "wsl" + args = @($WslBinaryPath, "serve") +} + +function Set-McpConfig { + param( + [string]$ConfigPath, + [string]$ClientName + ) + + try { + if (Test-Path $ConfigPath) { + $content = Get-Content -Raw $ConfigPath | ConvertFrom-Json + if (-not $content.mcpServers) { + $content | Add-Member -NotePropertyName "mcpServers" -NotePropertyValue ([PSCustomObject]@{}) + } + if ($content.mcpServers.PSObject.Properties.Name -contains "devcontainer-mcp") { + Write-Ok "$ClientName — already configured" + return + } + $content.mcpServers | Add-Member -NotePropertyName "devcontainer-mcp" -NotePropertyValue ([PSCustomObject]$mcpServerEntry) + $content | ConvertTo-Json -Depth 10 | Set-Content $ConfigPath -Encoding UTF8 + Write-Ok "$ClientName — added to $ConfigPath" + } else { + $dir = Split-Path $ConfigPath -Parent + if ($dir) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } + $config = [PSCustomObject]@{ + mcpServers = [PSCustomObject]@{ + "devcontainer-mcp" = [PSCustomObject]$mcpServerEntry + } + } + $config | ConvertTo-Json -Depth 10 | Set-Content $ConfigPath -Encoding UTF8 + Write-Ok "$ClientName — created $ConfigPath" + } + } catch { + Write-Warn "$ClientName — could not update $ConfigPath" + } +} + +# Claude Code +Set-McpConfig "$env:USERPROFILE\.claude.json" "Claude Code" + +# GitHub Copilot (if .copilot dir exists) +if (Test-Path "$env:USERPROFILE\.copilot") { + Set-McpConfig "$env:USERPROFILE\.copilot\mcp-config.json" "GitHub Copilot" +} + +# VS Code (if config dir exists) +$vscodeDir = "$env:APPDATA\Code\User" +if (Test-Path $vscodeDir) { + Set-McpConfig "$vscodeDir\mcp.json" "VS Code" +} + +# Cursor (if installed) +if (Test-Path "$env:USERPROFILE\.cursor") { + Set-McpConfig "$env:USERPROFILE\.cursor\mcp.json" "Cursor" +} + +# --------------------------------------------------------------------------- +# Done +# --------------------------------------------------------------------------- + +Write-Host "" +Write-Host "Done! devcontainer-mcp is ready to use." -ForegroundColor Green +Write-Host "" +Write-Host "MCP clients are configured to launch the server via WSL:" -ForegroundColor DarkGray +Write-Host " command: wsl" -ForegroundColor DarkGray +Write-Host " args: [$WslBinaryPath, serve]" -ForegroundColor DarkGray diff --git a/install.sh b/install.sh index 555ca51..07b0c54 100755 --- a/install.sh +++ b/install.sh @@ -12,14 +12,17 @@ set -euo pipefail REPO="aniongithub/devcontainer-mcp" INSTALL_DIR="${HOME}/.local/bin" +SKIP_MCP_CONFIG=false # Parse args while [[ $# -gt 0 ]]; do case "$1" in - --install-dir) INSTALL_DIR="$2"; shift 2 ;; + --install-dir) INSTALL_DIR="$2"; shift 2 ;; + --skip-mcp-config) SKIP_MCP_CONFIG=true; shift ;; --help|-h) - echo "Usage: install.sh [--install-dir DIR]" - echo " --install-dir Installation directory (default: ~/.local/bin)" + echo "Usage: install.sh [--install-dir DIR] [--skip-mcp-config]" + echo " --install-dir Installation directory (default: ~/.local/bin)" + echo " --skip-mcp-config Skip MCP client configuration (used by install.ps1)" exit 0 ;; *) echo "Unknown option: $1"; exit 1 ;; @@ -121,9 +124,17 @@ command -v devcontainer >/dev/null 2>&1 && echo " ✓ devcontainer" || echo " command -v gh >/dev/null 2>&1 && echo " ✓ gh (codespaces)" || echo " ✗ gh (codespaces) — https://cli.github.com/" # --------------------------------------------------------------------------- -# Auto-configure MCP clients +# Auto-configure MCP clients (skipped when called from install.ps1) # --------------------------------------------------------------------------- +if [ "$SKIP_MCP_CONFIG" = true ]; then + echo "" + echo "==> Skipping MCP client configuration (--skip-mcp-config)" + echo "" + echo "Done! devcontainer-mcp binary is installed." + exit 0 +fi + configure_mcp_client() { local config_file="$1" local client_name="$2" From 08d481dc7e1adaf940c8ce6ca3b9f56d6f1d6441 Mon Sep 17 00:00:00 2001 From: aniongithub Date: Sat, 25 Apr 2026 00:53:11 -0700 Subject: [PATCH 2/2] fix: use releases URL and full cmdlet names for Windows install - Point install.ps1 download at releases/latest/download (release artifact) - Use Invoke-RestMethod | Invoke-Expression instead of irm | iex aliases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91d0879..7e69d04 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ curl -fsSL https://raw.githubusercontent.com/aniongithub/devcontainer-mcp/main/i ### Windows (via WSL) ```powershell -irm https://raw.githubusercontent.com/aniongithub/devcontainer-mcp/main/install.ps1 | iex +Invoke-RestMethod https://github.com/aniongithub/devcontainer-mcp/releases/latest/download/install.ps1 | Invoke-Expression ``` > **How it works:** The binary runs inside WSL; MCP clients on Windows launch it via `wsl ~/.local/bin/devcontainer-mcp serve`. The stdio transport works transparently across the WSL boundary. WSL 2 is required — install it with `wsl --install` if you haven't already.