|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -uo pipefail |
| 3 | + |
| 4 | +# ============================================================================= |
| 5 | +# Step 10 — Developer Tools |
| 6 | +# Installs GitHub MCP server. More developer tools may be added here. |
| 7 | +# Run after completing Steps 1-9. Run this in your terminal. |
| 8 | +# ============================================================================= |
| 9 | + |
| 10 | +RED='\033[0;31m' |
| 11 | +GREEN='\033[0;32m' |
| 12 | +YELLOW='\033[1;33m' |
| 13 | +BLUE='\033[0;34m' |
| 14 | +NC='\033[0m' |
| 15 | + |
| 16 | +ERRORS=0 |
| 17 | + |
| 18 | +info() { echo -e "${BLUE}[INFO]${NC} $1"; } |
| 19 | +success() { echo -e "${GREEN}[OK]${NC} $1"; } |
| 20 | +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } |
| 21 | +fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; } |
| 22 | +soft_fail() { echo -e "${RED}[FAIL]${NC} $1 (non-critical, continuing...)"; ERRORS=$((ERRORS + 1)); } |
| 23 | + |
| 24 | +# Track what was installed this run |
| 25 | +INSTALLED_GITHUB=false |
| 26 | + |
| 27 | +# ----------------------------------------------------------------------------- |
| 28 | +# Detect OS |
| 29 | +# ----------------------------------------------------------------------------- |
| 30 | +detect_os() { |
| 31 | + case "$(uname -s)" in |
| 32 | + Darwin) OS="mac" ;; |
| 33 | + Linux) OS="linux" ;; |
| 34 | + MINGW*|MSYS*|CYGWIN*) fail "Windows is not supported. This script is for macOS and Linux only." ;; |
| 35 | + *) fail "Unsupported OS: $(uname -s). This script supports macOS and Linux only." ;; |
| 36 | + esac |
| 37 | + info "Detected OS: $OS" |
| 38 | +} |
| 39 | + |
| 40 | +# ----------------------------------------------------------------------------- |
| 41 | +# Verify prerequisites |
| 42 | +# ----------------------------------------------------------------------------- |
| 43 | +verify_prerequisites() { |
| 44 | + if ! command -v node &>/dev/null; then |
| 45 | + fail "Node.js not found. Run Step 1 first." |
| 46 | + fi |
| 47 | + if ! command -v claude &>/dev/null; then |
| 48 | + fail "Claude Code not found. Run Step 1 first." |
| 49 | + fi |
| 50 | + success "Prerequisites verified" |
| 51 | +} |
| 52 | + |
| 53 | +# ----------------------------------------------------------------------------- |
| 54 | +# Interactive menu — let the user pick which tools to install |
| 55 | +# ----------------------------------------------------------------------------- |
| 56 | +choose_tools() { |
| 57 | + # Detect non-interactive mode (stdin is a pipe, not a terminal) |
| 58 | + if [ ! -t 0 ]; then |
| 59 | + info "Non-interactive mode detected (running via curl pipe)" |
| 60 | + CHOICES="" |
| 61 | + |
| 62 | + # Auto-detect already-installed tools |
| 63 | + if claude mcp list 2>/dev/null | grep -q "github" 2>/dev/null; then |
| 64 | + CHOICES="$CHOICES 1" |
| 65 | + INSTALLED_GITHUB=true |
| 66 | + fi |
| 67 | + |
| 68 | + if [ -n "$CHOICES" ]; then |
| 69 | + info "Found already-installed tools — verifying configuration" |
| 70 | + return |
| 71 | + else |
| 72 | + echo "" |
| 73 | + echo -e "${YELLOW} Step 10 requires interactive input for API credentials.${NC}" |
| 74 | + echo -e "${YELLOW} Run it directly in your terminal:${NC}" |
| 75 | + echo "" |
| 76 | + echo " bash <(curl -fsSL https://raw.githubusercontent.com/lorecraft-io/cli-maxxing/main/step-10/step-10-install.sh)" |
| 77 | + echo "" |
| 78 | + print_summary |
| 79 | + exit 0 |
| 80 | + fi |
| 81 | + fi |
| 82 | + |
| 83 | + echo "" |
| 84 | + echo -e "${BLUE} Which developer tools do you use?${NC}" |
| 85 | + echo -e "${BLUE} (enter numbers separated by spaces)${NC}" |
| 86 | + echo "" |
| 87 | + echo " 1) GitHub — repos, issues, PRs, code search (requires Personal Access Token)" |
| 88 | + echo "" |
| 89 | + echo -e "${YELLOW} This step is for developers. If you don't use GitHub with Claude,${NC}" |
| 90 | + echo -e "${YELLOW} you can skip it — all earlier steps work without it.${NC}" |
| 91 | + echo "" |
| 92 | + read -rp " Enter your choices (e.g. \"1\"): " CHOICES |
| 93 | + echo "" |
| 94 | + |
| 95 | + if [ -z "$CHOICES" ]; then |
| 96 | + warn "No tools selected. Nothing to install." |
| 97 | + print_summary |
| 98 | + exit 0 |
| 99 | + fi |
| 100 | +} |
| 101 | + |
| 102 | +# ----------------------------------------------------------------------------- |
| 103 | +# Install GitHub MCP |
| 104 | +# ----------------------------------------------------------------------------- |
| 105 | +install_github() { |
| 106 | + info "Installing GitHub MCP server..." |
| 107 | + |
| 108 | + if claude mcp list 2>/dev/null | grep -q "github"; then |
| 109 | + success "GitHub MCP already installed" |
| 110 | + INSTALLED_GITHUB=true |
| 111 | + return |
| 112 | + fi |
| 113 | + |
| 114 | + echo "" |
| 115 | + echo -e "${BLUE} GitHub MCP gives Claude read/write access to your repos,${NC}" |
| 116 | + echo -e "${BLUE} issues, pull requests, and code search via the GitHub API.${NC}" |
| 117 | + echo "" |
| 118 | + echo -e "${BLUE} You need a Personal Access Token (classic PAT). Create one at:${NC}" |
| 119 | + echo -e "${BLUE} https://github.com/settings/tokens/new${NC}" |
| 120 | + echo "" |
| 121 | + echo " Suggested settings:" |
| 122 | + echo " - Token name: claude-github-mcp" |
| 123 | + echo " - Expiration: No expiration" |
| 124 | + echo " - Scopes: repo, read:org, gist" |
| 125 | + echo "" |
| 126 | + echo -e "${YELLOW} Use a classic token (not fine-grained) for full repo access.${NC}" |
| 127 | + echo -e "${YELLOW} Check only: repo (top checkbox), read:org (under admin:org), gist.${NC}" |
| 128 | + echo "" |
| 129 | + |
| 130 | + read -sp " GitHub Personal Access Token (ghp_...): " GITHUB_TOKEN |
| 131 | + echo " [saved]" |
| 132 | + echo "" |
| 133 | + |
| 134 | + if [ -z "$GITHUB_TOKEN" ]; then |
| 135 | + warn "No GitHub token provided. Skipping GitHub setup." |
| 136 | + warn "Re-run Step 10 when you have a token ready." |
| 137 | + return |
| 138 | + fi |
| 139 | + |
| 140 | + if [[ ! "$GITHUB_TOKEN" =~ ^gh[ps]_ ]]; then |
| 141 | + warn "Token doesn't look like a GitHub PAT (expected ghp_ or ghs_ prefix)." |
| 142 | + warn "Proceeding anyway — registration will fail if the token is invalid." |
| 143 | + echo "" |
| 144 | + fi |
| 145 | + |
| 146 | + # Register with the token injected via env var into the MCP server process. |
| 147 | + # Credentials live in Claude's MCP config only — not written to disk here. |
| 148 | + claude mcp add --scope user github -- npx -y @modelcontextprotocol/server-github 2>/dev/null |
| 149 | + |
| 150 | + # Inject the token directly into the config entry (claude mcp add --scope user |
| 151 | + # does not support -e flags in all CLI versions, so we patch the env block). |
| 152 | + python3 - "$GITHUB_TOKEN" <<'PYEOF' |
| 153 | +import sys, json, os |
| 154 | +
|
| 155 | +token = sys.argv[1] |
| 156 | +config_path = os.path.expanduser("~/.claude.json") |
| 157 | +
|
| 158 | +with open(config_path) as f: |
| 159 | + config = json.load(f) |
| 160 | +
|
| 161 | +mcpServers = config.get("mcpServers", {}) |
| 162 | +if "github" in mcpServers: |
| 163 | + mcpServers["github"].setdefault("env", {}) |
| 164 | + mcpServers["github"]["env"]["GITHUB_PERSONAL_ACCESS_TOKEN"] = token |
| 165 | + config["mcpServers"] = mcpServers |
| 166 | + with open(config_path, "w") as f: |
| 167 | + json.dump(config, f, indent=2) |
| 168 | + print("Token injected into GitHub MCP config.") |
| 169 | +else: |
| 170 | + print("WARNING: github entry not found in MCP config — token not injected.") |
| 171 | +PYEOF |
| 172 | + |
| 173 | + if claude mcp list 2>/dev/null | grep -q "github"; then |
| 174 | + success "GitHub MCP installed" |
| 175 | + INSTALLED_GITHUB=true |
| 176 | + else |
| 177 | + soft_fail "GitHub MCP installation could not be verified" |
| 178 | + fi |
| 179 | +} |
| 180 | + |
| 181 | +# ----------------------------------------------------------------------------- |
| 182 | +# Self-test — check each installed tool is registered |
| 183 | +# ----------------------------------------------------------------------------- |
| 184 | +run_self_test() { |
| 185 | + echo "" |
| 186 | + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 187 | + echo -e "${BLUE} Running Self-Test${NC}" |
| 188 | + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 189 | + echo "" |
| 190 | + |
| 191 | + TEST_PASS=0 |
| 192 | + TEST_FAIL=0 |
| 193 | + TEST_SKIP=0 |
| 194 | + |
| 195 | + check_registered() { |
| 196 | + local label="$1" |
| 197 | + local needle="$2" |
| 198 | + if claude mcp list 2>/dev/null | grep -q "$needle"; then |
| 199 | + success "TEST: $label MCP registered" |
| 200 | + TEST_PASS=$((TEST_PASS + 1)) |
| 201 | + else |
| 202 | + soft_fail "TEST: $label MCP not registered" |
| 203 | + TEST_FAIL=$((TEST_FAIL + 1)) |
| 204 | + fi |
| 205 | + } |
| 206 | + |
| 207 | + if $INSTALLED_GITHUB; then check_registered "GitHub" "github"; else info "TEST: GitHub — skipped"; TEST_SKIP=$((TEST_SKIP + 1)); fi |
| 208 | + |
| 209 | + echo "" |
| 210 | + if [ "$TEST_FAIL" -eq 0 ]; then |
| 211 | + echo -e " ${GREEN}All $TEST_PASS tests passed.${NC} ($TEST_SKIP skipped)" |
| 212 | + else |
| 213 | + echo -e " ${GREEN}$TEST_PASS passed${NC}, ${RED}$TEST_FAIL failed${NC}, $TEST_SKIP skipped." |
| 214 | + echo -e " ${YELLOW}Scroll up to see what went wrong.${NC}" |
| 215 | + fi |
| 216 | + echo "" |
| 217 | + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 218 | +} |
| 219 | + |
| 220 | +# ----------------------------------------------------------------------------- |
| 221 | +# Summary |
| 222 | +# ----------------------------------------------------------------------------- |
| 223 | +print_summary() { |
| 224 | + echo "" |
| 225 | + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 226 | + echo -e "${GREEN} Step 10 Complete — Developer Tools${NC}" |
| 227 | + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 228 | + echo "" |
| 229 | + |
| 230 | + INSTALLED_COUNT=0 |
| 231 | + |
| 232 | + if $INSTALLED_GITHUB; then echo " GitHub — repos, issues, PRs, code search"; INSTALLED_COUNT=$((INSTALLED_COUNT + 1)); fi |
| 233 | + |
| 234 | + if [ "$INSTALLED_COUNT" -eq 0 ]; then |
| 235 | + echo " No tools were installed." |
| 236 | + else |
| 237 | + echo "" |
| 238 | + echo " $INSTALLED_COUNT tool(s) installed and ready in Claude Code." |
| 239 | + echo "" |
| 240 | + echo " What you can do now:" |
| 241 | + |
| 242 | + if $INSTALLED_GITHUB; then |
| 243 | + echo " - Ask Claude to list open PRs or issues on any of your repos" |
| 244 | + echo " - Ask Claude to search code across your GitHub organizations" |
| 245 | + echo " - Ask Claude to create issues, review diffs, or push commits" |
| 246 | + fi |
| 247 | + fi |
| 248 | + |
| 249 | + echo "" |
| 250 | + if [ "$ERRORS" -gt 0 ]; then |
| 251 | + echo -e " ${YELLOW}Warnings: $ERRORS issue(s) detected.${NC}" |
| 252 | + echo -e " ${YELLOW}Scroll up to see details.${NC}" |
| 253 | + echo "" |
| 254 | + fi |
| 255 | + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 256 | + echo "" |
| 257 | + echo " Continue to the Final Step to install your status line." |
| 258 | + echo "" |
| 259 | +} |
| 260 | + |
| 261 | +# ----------------------------------------------------------------------------- |
| 262 | +# Main |
| 263 | +# ----------------------------------------------------------------------------- |
| 264 | +main() { |
| 265 | + echo "" |
| 266 | + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 267 | + echo -e "${BLUE} Step 10 — Developer Tools${NC}" |
| 268 | + echo -e "${BLUE} GitHub and other dev-facing MCP tools • macOS + Linux${NC}" |
| 269 | + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
| 270 | + echo "" |
| 271 | + |
| 272 | + detect_os |
| 273 | + verify_prerequisites |
| 274 | + choose_tools |
| 275 | + |
| 276 | + # Process each selection in the canonical order |
| 277 | + for CHOICE in $CHOICES; do |
| 278 | + case "$CHOICE" in |
| 279 | + 1) if ! $INSTALLED_GITHUB; then install_github; else success "GitHub already configured"; fi ;; |
| 280 | + *) warn "Unknown choice: $CHOICE (skipping)" ;; |
| 281 | + esac |
| 282 | + done |
| 283 | + |
| 284 | + run_self_test |
| 285 | + print_summary |
| 286 | +} |
| 287 | + |
| 288 | +main "$@" |
0 commit comments