Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.

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**.
Expand Down Expand Up @@ -145,7 +155,7 @@ Supported providers: **GitHub**, **AWS**, **Azure**, **GCP**, **Kubernetes**

## MCP Server Configuration

### Claude Desktop / Copilot / Cursor
### Linux / macOS

```json
{
Expand All @@ -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:
Expand Down
193 changes: 193 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -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
19 changes: 15 additions & 4 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 ;;
Expand Down Expand Up @@ -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"
Expand Down