# Installing and Configuring OpenCode on Windows

**Date:** 2026-09-21  
**Author:** Kees C. Bakker  
**Categories:** Automation, Installation Notes, Windows  
**Original:** https://keestalkstech.com/installing-and-configuring-opencode-on-windows/

![Installing and Configuring OpenCode on Windows](https://keestalkstech.com/wp-content/uploads/2026/09/keestalkstech-opencode.png)

---

If you want to work with AI and large language models, you'll need a coding-agent harness. My weapon of choice is [OpenCode](https://opencode.ai/) on Windows. This article will guide you through installing and configuring the tool itself, AI providers, MCPs, and skills.

## Install

We need to install some software:

1. OpenCode (ofcourse)
2. Node Version Manager (NVM) for Windows, to help us install skills.
3. CoreUtils, which is a port of common GNU utils (like sed, ls, etc) for Windows. LLMs love using those.
4. Edikt for editting the JSON file.

Edikt is a young project. This guide pins version `v0.5.0`; review newer releases before updating it.

Let's install:

```powershell
& {
  function Update-Path {
    $machinePath = [Environment]::GetEnvironmentVariable("Path", "Machine")
    $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
    $env:Path = "$machinePath;$userPath"
  }

  # Install OpenCode, NVM for Windows, and Coreutils.
  winget install --id SST.opencode
  winget install --id CoreyButler.NVMforWindows
  winget install --id Microsoft.Coreutils

  # Download the pinned edikt release for lossless JSONC edits.
  $ediktVersion = "v0.5.0"
  $ediktDir = Join-Path $env:LOCALAPPDATA "edikt"
  $ediktPath = Join-Path $ediktDir "edikt.exe"
  $ediktZip = Join-Path $env:TEMP "edikt-$ediktVersion.zip"
  New-Item -ItemType Directory -Force -Path $ediktDir | Out-Null
  Invoke-WebRequest "https://github.com/jhheider/edikt/releases/download/$ediktVersion/edikt-windows-x86_64.zip" -OutFile $ediktZip
  Expand-Archive -Force -Path $ediktZip -DestinationPath $ediktDir
  Remove-Item $ediktZip
  Update-Path
  Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
  nvm install lts
  nvm use lts
  Start-Sleep -Seconds 1
  Update-Path
  Write-Host "opencode: $(opencode --version)"
  Write-Host "node: $(node --version)"
  Write-Host "npm: $(npm --version)"
}
```

## Providers

For API-key providers, run the following command and select the provider interactively:

```powershell
opencode auth login
```

For GitHub Copilot, start OpenCode and run `/connect`, then select GitHub Copilot. See the [OpenCode provider documentation](https://opencode.ai/docs/providers/) for the complete authentication flow.

## MCPs

In this section, we configure three MCP servers:

Let's go:

```powershell
& {
  $configPath = @(Join-Path $HOME ".config\opencode\opencode.jsonc"; Join-Path $HOME ".config\opencode\opencode.json") | ? { Test-Path $_ } | Select-Object -First 1
  $backupPath = "$configPath.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss_fff')"
  Copy-Item $configPath $backupPath
  Write-Host "Backup created at $backupPath"
  $ediktPath = Join-Path $env:LOCALAPPDATA "edikt\edikt.exe"
  $configDir = Split-Path $configPath
  $secretDir = Join-Path $configDir "secrets"
  $keyPath = Join-Path $secretDir "context7-api-key"
  New-Item -ItemType Directory -Force -Path $configDir, $secretDir | Out-Null
  if (-not (Test-Path -LiteralPath $keyPath) -or [string]::IsNullOrWhiteSpace((Get-Content -Raw -LiteralPath $keyPath))) {
    New-Item -ItemType File -Force -Path $keyPath | Out-Null
    Start-Process notepad.exe -ArgumentList "`"$keyPath`"" -Wait
    if ([string]::IsNullOrWhiteSpace((Get-Content -Raw -LiteralPath $keyPath))) { throw "The Context7 API key file is empty." }
  } else { Write-Host "Context7 API key already exists; leaving it unchanged." }
  $ediktScript = Join-Path $env:TEMP "opencode-$([guid]::NewGuid()).edk"
  $success = $false
  try {
    @'
.mcp.context7 = {type: "remote", url: "https://mcp.context7.com/mcp", headers: {CONTEXT7_API_KEY: "{file:secrets/context7-api-key}"}} |
.mcp.playwright = {type: "local", command: ["npx", "-y", "@playwright/mcp@latest"]} |
.mcp."aws-knowledge" = {type: "remote", url: "https://knowledge-mcp.global.api.aws"}
'@ | Set-Content -LiteralPath $ediktScript -Encoding utf8
    $output = & $ediktPath -t jsonc -f $ediktScript --in-place -- $configPath 2>&1
    if ($LASTEXITCODE) { throw "edikt failed: $(($output -join ' ') -replace '\s+', ' ')" }
    & opencode debug config *> $null
    if ($LASTEXITCODE) { throw "OpenCode rejected the updated configuration." }
    $success = $true
  } finally { Remove-Item $ediktScript -Force -ErrorAction SilentlyContinue; if (-not $success) { Copy-Item $backupPath $configPath -Force; Write-Warning "The update failed; the backup was restored." } }
  Write-Host "OpenCode configuration updated and validated at $configPath"
  Write-Host "Restart OpenCode to load the new configuration."
}
```

## Protect Configuration Files

The following script protects environment and application configuration files. Example files remain readable.

```powershell
& {
  $configPath = @(Join-Path $HOME ".config\opencode\opencode.jsonc"; Join-Path $HOME ".config\opencode\opencode.json") | ? { Test-Path $_ } | Select-Object -First 1
  $backupPath = "$configPath.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss_fff')"
  Copy-Item $configPath $backupPath
  Write-Host "Backup created at $backupPath"
  $ediktPath = Join-Path $env:LOCALAPPDATA "edikt\edikt.exe"
  $ediktScript = Join-Path $env:TEMP "opencode-$([guid]::NewGuid()).edk"
  $success = $false
  try {
    $expressions = @('.permission.read[".env*"] = "deny"'; '.permission.read["appsettings*.json"] = "deny"'; '.permission.read["*.example*"] = "allow"')
    ($expressions -join " |`n") | Set-Content -LiteralPath $ediktScript -Encoding utf8
    $output = & $ediktPath -t jsonc -f $ediktScript --in-place -- $configPath 2>&1
    if ($LASTEXITCODE) { throw "edikt failed: $(($output -join ' ') -replace '\s+', ' ')" }
    & opencode debug config *> $null
    if ($LASTEXITCODE) { throw "OpenCode rejected the updated configuration." }
    $success = $true
  } finally { Remove-Item $ediktScript -Force -ErrorAction SilentlyContinue; if (-not $success) { Copy-Item $backupPath $configPath -Force; Write-Warning "The update failed; the backup was restored." } }
  Write-Host "Configuration file permissions updated and validated at $configPath"
}
```

OpenCode allows shell commands by default. This optional script adds confirmation prompts for potentially destructive commands while leaving other commands at their defaults.

## Ask Before Commands

```powershell
& {
  $configPath = @(Join-Path $HOME ".config\opencode\opencode.jsonc"; Join-Path $HOME ".config\opencode\opencode.json") | ? { Test-Path $_ } | Select-Object -First 1
  $backupPath = "$configPath.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss_fff')"
  Copy-Item $configPath $backupPath
  Write-Host "Backup created at $backupPath"
  $ediktPath = Join-Path $env:LOCALAPPDATA "edikt\edikt.exe"
  $askCommands = @(
    "curl *localhost*"
    "curl *127.0.0.1*"
    "curl *0.0.0.0*"
    "curl * -X POST*"
    "curl * --request POST*"
    "curl * -X PUT*"
    "curl * --request PUT*"
    "curl * -X PATCH*"
    "curl * --request PATCH*"
    "curl * -X DELETE*"
    "curl * --request DELETE*"
    "curl * -d *"
    "curl * --data*"
    "git *"
    "terraform *"
    "tofu *"
    "ansible-playbook*"
    "aws *"
    "gcloud *"
    "az *"
    "doctl *"
    "kubectl *"
    "helm *"
    "docker *"
    "docker-compose *"
    "psql*"
    "mysql*"
    "mongosh*"
    "redis-cli*"
    "iptables*"
    "ufw*"
    "firewall-cmd*"
    "apt-get *"
    "apt *"
    "yum *"
    "dnf *"
    "brew *"
    "snap *"
    "rm *"
    "rm -rf*"
    "rmdir*"
    "mv *"
    "dd *"
    "mkfs*"
    "fdisk*"
    "parted*"
    "chmod*"
    "chown*"
    "sudo *"
    "systemctl *"
    "reboot*"
    "shutdown*"
    "kill*"
    "pkill*"
    "ssh *"
    "scp *"
    "npm *"
    "npx *"
    "yarn *"
    "pnpm *"
    "bun *"
    "powershell *"
    "pwsh *"
    "Remove-Item*"
    "Move-Item*"
    "Copy-Item*"
    "Rename-Item*"
    "Set-Content*"
    "Add-Content*"
    "Out-File*"
    "Clear-Content*"
    "New-Item*"
    "Invoke-WebRequest*"
    "Invoke-RestMethod*"
    "Start-Process*"
    "Stop-Process*"
    "Set-ExecutionPolicy*"
    "Stop-Computer*"
    "Restart-Computer*"
    "Stop-Service*"
    "Restart-Service*"
    "Remove-Service*"
    "winget *"
  )
  $allowCommands = @(
    "git status*"
    "git log*"
    "git diff*"
    "git show*"
    "git branch"
    "git branch -a*"
    "git tag"
    "git tag -l*"
    "terraform plan*"
    "tofu plan*"
  )
  $askOverrides = @(
    "terraform plan -destroy*"
    "tofu plan -destroy*"
  )
  $ediktScript = Join-Path $env:TEMP "opencode-$([guid]::NewGuid()).edk"
  $success = $false
  try {
    $expressions = @('.permission.external_directory = "ask"', '.permission.doom_loop = "ask"')
    $expressions += $askCommands | ForEach-Object { '.permission.bash["' + $_ + '"] = "ask"' }
    $expressions += $allowCommands | ForEach-Object { '.permission.bash["' + $_ + '"] = "allow"' }
    $expressions += $askOverrides | ForEach-Object { '.permission.bash["' + $_ + '"] = "ask"' }
    ($expressions -join " |`n") | Set-Content -LiteralPath $ediktScript -Encoding utf8
    $output = & $ediktPath -t jsonc -f $ediktScript --in-place -- $configPath 2>&1
    if ($LASTEXITCODE) { throw "edikt failed: $(($output -join ' ') -replace '\s+', ' ')" }
    & opencode debug config *> $null
    if ($LASTEXITCODE) { throw "OpenCode rejected the updated configuration." }
    $success = $true
  } finally { Remove-Item $ediktScript -Force -ErrorAction SilentlyContinue; if (-not $success) { Copy-Item $backupPath $configPath -Force; Write-Warning "The update failed; the backup was restored." } }
  Write-Host "Command permissions updated and validated at $configPath"
}
```

## Skills

Install the skills globally with the [`skills` CLI](https://skills.sh/docs):

```powershell
& {
  npx skills add https://github.com/anthropics/skills --skill skill-creator -g
  npx skills add https://github.com/mindrally/skills --skill htmx -g
  npx skills add https://github.com/vercel-labs/skills --skill find-skills -g
  npx skills add https://github.com/github/awesome-copilot --skill git-commit -g
}
```

These commands use the skill sources listed on [skills.sh](https://skills.sh/). Review a skill before installing it, because skills are executable instructions for an AI agent.
