Skip to content

Instantly share code, notes, and snippets.

@ardzz
Created May 28, 2026 17:30
Show Gist options
  • Select an option

  • Save ardzz/3a2e1247bcb1b18aa95d9a32455e8f56 to your computer and use it in GitHub Desktop.

Select an option

Save ardzz/3a2e1247bcb1b18aa95d9a32455e8f56 to your computer and use it in GitHub Desktop.
Reset opencode MCP OAuth state — fixes {"detail":"Unknown client_id"} and similar refresh errors. Backup-safe, idempotent, supports --list/--dry-run.
#!/usr/bin/env bash
# opencode-mcp-reset — Reset OAuth state for a single MCP server in opencode.
#
# Use this when opencode shows: {"detail":"Unknown client_id"} or any OAuth
# refresh error. It removes the broken entry from opencode's mcp-auth.json so
# opencode triggers a fresh OAuth flow next time you use that MCP.
#
# Usage:
# opencode-mcp-reset # reset 'consensus' (default)
# opencode-mcp-reset <mcp-name> # reset a specific MCP
# opencode-mcp-reset --list # show stored MCP auth entries
# opencode-mcp-reset --dry-run NAME # preview only, no changes
# opencode-mcp-reset --help
#
# After running: restart opencode and invoke the MCP — a browser-based OAuth
# flow will start and a fresh client_id will be registered.
set -euo pipefail
# ─── Colors (only if stdout is a TTY) ──────────────────────────────────────
if [[ -t 1 ]]; then
R=$'\033[31m'; G=$'\033[32m'; Y=$'\033[33m'; B=$'\033[34m'; D=$'\033[2m'; N=$'\033[0m'
else
R=""; G=""; Y=""; B=""; D=""; N=""
fi
die() { echo "${R}error:${N} $*" >&2; exit 1; }
info() { echo "${B}::${N} $*"; }
ok() { echo "${G}✓${N} $*"; }
warn() { echo "${Y}!${N} $*"; }
# ─── Locate mcp-auth.json across platforms ─────────────────────────────────
find_auth_file() {
local candidates=(
"${XDG_DATA_HOME:-$HOME/.local/share}/opencode/mcp-auth.json"
"$HOME/Library/Application Support/opencode/mcp-auth.json" # macOS
"${APPDATA:-$HOME/AppData/Roaming}/opencode/mcp-auth.json" # Windows (Git Bash/WSL)
)
for f in "${candidates[@]}"; do
[[ -f "$f" ]] && { echo "$f"; return 0; }
done
return 1
}
# ─── Argument parsing ──────────────────────────────────────────────────────
DRY_RUN=0
LIST=0
MCP=""
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'
exit 0 ;;
-l|--list) LIST=1; shift ;;
-n|--dry-run) DRY_RUN=1; shift ;;
-*) die "unknown flag: $1" ;;
*) MCP="$1"; shift ;;
esac
done
[[ -z "$MCP" && $LIST -eq 0 ]] && MCP="consensus"
# ─── Tool checks ───────────────────────────────────────────────────────────
command -v python3 >/dev/null 2>&1 \
|| die "python3 is required (used for safe JSON editing)"
AUTH_FILE="$(find_auth_file)" \
|| die "mcp-auth.json not found in any known opencode data dir.
Checked:
\$XDG_DATA_HOME/opencode/mcp-auth.json
~/Library/Application Support/opencode/mcp-auth.json
\$APPDATA/opencode/mcp-auth.json
Is opencode installed and have you used MCP at least once?"
info "auth file: ${D}${AUTH_FILE}${N}"
# ─── --list mode ───────────────────────────────────────────────────────────
if [[ $LIST -eq 1 ]]; then
python3 - "$AUTH_FILE" <<'PY'
import json, sys, datetime
with open(sys.argv[1]) as f: d = json.load(f)
if not d:
print(" (empty — no MCPs have completed OAuth yet)"); sys.exit()
for name, cfg in d.items():
tok = cfg.get("tokens", {}) if isinstance(cfg, dict) else {}
exp = tok.get("expiresAt")
when = ""
if isinstance(exp, (int, float)):
# opencode stores seconds (sometimes float)
ts = exp if exp < 1e12 else exp / 1000
try:
when = " (expires " + datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M") + ")"
except Exception:
pass
print(f" - {name}{when}")
PY
exit 0
fi
# ─── Reset mode ────────────────────────────────────────────────────────────
EXISTS=$(python3 -c "
import json, sys
with open('$AUTH_FILE') as f: d = json.load(f)
print('yes' if '$MCP' in d else 'no')
")
if [[ "$EXISTS" != "yes" ]]; then
warn "MCP '${MCP}' tidak ada di ${AUTH_FILE}. Tidak ada yang dihapus."
info "Gunakan ${D}--list${N} untuk melihat entry yang tersedia."
exit 0
fi
if [[ $DRY_RUN -eq 1 ]]; then
warn "DRY-RUN: entry '${MCP}' AKAN dihapus. Tidak ada perubahan dilakukan."
exit 0
fi
# Backup with timestamp
BACKUP="${AUTH_FILE}.bak.$(date +%s)"
cp "$AUTH_FILE" "$BACKUP"
ok "backup: ${D}${BACKUP}${N}"
# Remove the entry (atomic via temp file → rename)
TMP="$(mktemp "${AUTH_FILE}.tmp.XXXXXX")"
python3 - "$AUTH_FILE" "$MCP" "$TMP" <<'PY'
import json, sys
src, mcp, dst = sys.argv[1:4]
with open(src) as f: d = json.load(f)
d.pop(mcp, None)
with open(dst, "w") as f: json.dump(d, f, indent=2)
PY
mv "$TMP" "$AUTH_FILE"
ok "entry '${MCP}' dihapus dari mcp-auth.json"
# ─── Next steps ────────────────────────────────────────────────────────────
cat <<EOF
${G}Selesai.${N} Langkah selanjutnya:
1. Restart opencode (tutup & buka lagi sesinya).
2. Panggil tool dari MCP '${MCP}' — opencode akan menampilkan URL OAuth.
3. Buka URL itu di browser, login, beri izin.
4. Selesai — clientId baru akan tersimpan otomatis di mcp-auth.json.
Restore backup jika perlu:
${D}cp "${BACKUP}" "${AUTH_FILE}"${N}
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment