diff --git a/CHANGELOG.md b/CHANGELOG.md index 0333269..4a0cf2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,21 @@ that may never merge. They are not releases and are not listed here. ## Unreleased +## 1.1.1 - 2026-10-22 + +- The update check now runs in published builds. 0.3.0 through 1.1.0 were + built with it off, so they never said a newer version existed. At a + terminal, at most once a day, `mapbox` now prints a notice on stderr when + one does; `MAPBOX_NO_UPDATE_CHECK=1` or + `mapbox config set update-check off` turns it off. Nothing reaches stdout, + and piped or CI runs never check. +- `install.sh`/`install.ps1`: the MCP question no longer waits several + seconds before it appears. The installer asks whenever it found a coding + agent or VS Code, and only after a yes does it look for Claude Code, Codex, VS Code + and Cursor and check what each already has. A reinstall therefore asks + again even when every server is registered; a yes then reports each one + as already there. + ## 1.1.0 - 2026-10-22 - `mapbox workflow` (beta): install and run workflows, which are named, diff --git a/Cargo.lock b/Cargo.lock index e977738..c19a4a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -816,7 +816,7 @@ checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" [[package]] name = "mapbox-cli" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index e32b781..0654710 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mapbox-cli" -version = "1.1.0" +version = "1.1.1" edition = "2021" description = "A command-line interface for Mapbox APIs, with commands generated at build time from OpenAPI specs." repository = "https://github.com/mapbox/mapbox-cli" diff --git a/README.md b/README.md index 1bdd008..0bd6d97 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,11 @@ taking the newest, with or without the leading `v`, so the version `mapbox --version` prints can be pasted straight in: ```sh -curl -fsSL https://cli.mapbox.com/install.sh | MAPBOX_CLI_VERSION=1.1.0 sh +curl -fsSL https://cli.mapbox.com/install.sh | MAPBOX_CLI_VERSION=1.1.1 sh ``` ```powershell -$env:MAPBOX_CLI_VERSION = '1.1.0'; irm https://cli.mapbox.com/install.ps1 | iex +$env:MAPBOX_CLI_VERSION = '1.1.1'; irm https://cli.mapbox.com/install.ps1 | iex ``` Both scripts put the install directory on your `PATH`: `install.sh` adds a @@ -105,9 +105,11 @@ or run the script somewhere with no terminal to ask on (CI, a container), and it does neither, printing the two commands to run by hand instead. It then asks, separately, whether to add the [Mapbox MCP servers](#mcp-servers) -to each coding agent it finds that can take one (Claude Code, Codex, VS Code, -Cursor), the same thing `mapbox mcp install --global` does. It asks only when -there is something left to add, with the same rules: no terminal means no. +to your coding agents, the same thing `mapbox mcp install --global` does: a +yes adds them to whichever of Claude Code, Codex, VS Code and Cursor are +installed, and skips any that already have them. It asks whenever it found a +coding agent above or VS Code is installed, with the same rules: no terminal +means no. Set `MAPBOX_CLI_NO_AGENT_SETUP=1` to skip both questions. On macOS and Linux, `install.sh` also offers to install the @@ -141,7 +143,7 @@ Six targets are published: `aarch64-apple-darwin`, `x86_64-apple-darwin`, then extract: ```sh -version=v1.1.0 +version=v1.1.1 file=mapbox-${version}-aarch64-apple-darwin.tar.gz curl -fsSLO "https://cli.mapbox.com/${version}/${file}" @@ -156,7 +158,7 @@ On Windows the archive is a `.zip`, and PowerShell can read the manifest directly: ```powershell -$version = 'v1.1.0' +$version = 'v1.1.1' $target = 'x86_64-pc-windows-msvc' $manifest = Invoke-RestMethod "https://cli.mapbox.com/$version/manifest.json" diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 3974070..2399c51 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -751,6 +751,7 @@ Windows 11 on Arm has and Windows 10 on Arm does not. ) } + $agentsFound = $false if ($AgentSetupAllowed) { # A child writing to stderr is a terminating NativeCommandError in # Windows PowerShell under $ErrorActionPreference = 'Stop' (set @@ -765,6 +766,7 @@ Windows 11 on Arm has and Windows 10 on Arm does not. try { $checkOut = (& $destination generate-skills --global --dry-run -o json 2>$null) -join '' if ($LASTEXITCODE -eq 0) { + $agentsFound = $true $agentNames = Get-AgentName $checkOut Write-Ok "Coding agents found: $agentNames." $doAgentSetup = $false @@ -838,85 +840,96 @@ Windows 11 on Arm has and Windows 10 on Arm does not. # # A separate question from the skill above: this registers a hosted # server in the coding agent's own config, next to servers the user - # added, rather than writing a directory this CLI owns. `mapbox mcp - # install` never replaces an entry that is already there (src/mcp.rs), - # so on a reinstall it is only offered when something is left to - # register. Same opt-out and same no-console rule as the skill. - if ($AgentSetupAllowed) { + # added, rather than writing a directory this CLI owns. Asked only + # when the skill step above found a coding agent, or VS Code is + # there, with the same opt-out and no-console rule. VS Code takes an + # MCP server but no skill, so the skill step can't find it; + # ~/.vscode is where it keeps extensions on every OS. + # + # Nothing here checks which clients are installed or what they + # already have, for the reason install.sh gives: that check runs each + # client's own CLI and took seconds with nothing on screen. `mapbox + # mcp install` tries each client itself once someone says yes, so a + # reinstall asks again, a yes reports what was already there, and a + # no is reported as skipped. + if ($AgentSetupAllowed -and ($agentsFound -or (Test-Path -LiteralPath (Join-Path $HOME '.vscode') -PathType Container))) { $previousErrorAction = $ErrorActionPreference $ErrorActionPreference = 'Continue' try { - # Fails with mcp_client_not_found when no supported client is - # installed, which is most machines: nothing to offer, so - # nothing is said. - $mcpCheck = (& $destination mcp install --global --dry-run -o json 2>$null) -join '' - if ($LASTEXITCODE -eq 0) { - $clientLabels = @{ - 'claude-code' = 'Claude Code'; 'codex' = 'Codex' - 'vscode' = 'VS Code'; 'cursor' = 'Cursor' + $clientLabels = @{ + 'claude-code' = 'Claude Code'; 'codex' = 'Codex' + 'vscode' = 'VS Code'; 'cursor' = 'Cursor' + } + $doMcpSetup = $false + if (-not [Console]::IsInputRedirected) { + Write-AskTitle 'Add the Mapbox MCP servers to your coding agents?' + Write-AskNote @('This runs ', 'mapbox mcp install --global', ', now or any time later.') + $answer = Read-AskAnswer 'Add them now?' + $Layout.Indent = ' ' + if ($answer -match '^(?i:y|yes)$') { $doMcpSetup = $true } + } + if ($doMcpSetup) { + # Each client's own CLI does the registering, and + # Codex's can stop to print a sign-in URL, so + # stderr - where mapbox forwards what they print - + # is left on the console, live. No spinner: a + # second writer to the console is not worth it + # here without a terminal to test it on. + Write-Part $Layout.Indent + Write-Part '...' 'Cyan' + Write-Host ' Adding the Mapbox MCP servers' + # stderr is passed through as it arrives and kept + # too, for the error code a failure carries. + $mcpErr = '' + $mcpOut = (& $destination mcp install --global -o json 2>&1 | ForEach-Object { + if ($_ -is [System.Management.Automation.ErrorRecord]) { + $line = $_.ToString() + [Console]::Error.WriteLine($line) + $mcpErr += $line + } else { $_ } + }) -join '' + $mcpOk = $LASTEXITCODE -eq 0 + # One line per server and outcome, clients joined, + # so a server added just now reads differently from + # one that was already there. + $serverLabels = @{ 'mapbox' = 'Mapbox MCP'; 'mapbox-devkit' = 'Mapbox DevKit MCP' } + $rows = @() + try { $rows = @(($mcpOut | ConvertFrom-Json).results) } catch { $rows = @() } + $groups = [ordered]@{} + foreach ($row in $rows) { + $what = if ($serverLabels.ContainsKey($row.server)) { $serverLabels[$row.server] } else { $row.server } + $who = if ($clientLabels.ContainsKey($row.client)) { $clientLabels[$row.client] } else { $row.client } + $key = "$($row.status)|$what" + if (-not $groups.Contains($key)) { $groups[$key] = @() } + $groups[$key] += $who } - $mcpClients = @(($mcpCheck | ConvertFrom-Json).results | - Where-Object { $_.status -eq 'would_install' } | - ForEach-Object { $_.client } | Select-Object -Unique | - ForEach-Object { if ($clientLabels.ContainsKey($_)) { $clientLabels[$_] } else { $_ } }) -join ', ' - if ($mcpClients) { - $doMcpSetup = $false - if (-not [Console]::IsInputRedirected) { - Write-AskTitle "Add the Mapbox MCP servers to ${mcpClients}?" - Write-AskNote @('This runs ', 'mapbox mcp install --global', ', now or any time later.') - $answer = Read-AskAnswer 'Add them now?' - $Layout.Indent = ' ' - if ($answer -match '^(?i:y|yes)$') { $doMcpSetup = $true } + foreach ($key in $groups.Keys) { + $status, $what = $key -split '\|', 2 + $who = $groups[$key] -join ', ' + switch ($status) { + 'installed' { Write-Ok "Added $what to $who" } + 'already_installed' { Write-Skip "$what was already in $who" } + 'installed_login_incomplete' { Write-Warn "Added $what to $who, but sign-in did not finish" } + 'client_not_found' { Write-Warn "Skipped $what for ${who}: CLI not on PATH" } + 'config_unreadable' { Write-Warn "Skipped $what for ${who}: config could not be read" } + default { Write-Warn "Could not add $what to $who" } } - if ($doMcpSetup) { - # Each client's own CLI does the registering, and - # Codex's can stop to print a sign-in URL, so - # stderr - where mapbox forwards what they print - - # is left on the console, live. No spinner: a - # second writer to the console is not worth it - # here without a terminal to test it on. - Write-Part $Layout.Indent - Write-Part '...' 'Cyan' - Write-Host ' Adding the Mapbox MCP servers' - $mcpOut = (& $destination mcp install --global -o json) -join '' - $mcpOk = $LASTEXITCODE -eq 0 - # One line per server and outcome, clients joined, - # so a server added just now reads differently from - # one that was already there. - $serverLabels = @{ 'mapbox' = 'Mapbox MCP'; 'mapbox-devkit' = 'Mapbox DevKit MCP' } - $rows = @() - try { $rows = @(($mcpOut | ConvertFrom-Json).results) } catch { $rows = @() } - $groups = [ordered]@{} - foreach ($row in $rows) { - $what = if ($serverLabels.ContainsKey($row.server)) { $serverLabels[$row.server] } else { $row.server } - $who = if ($clientLabels.ContainsKey($row.client)) { $clientLabels[$row.client] } else { $row.client } - $key = "$($row.status)|$what" - if (-not $groups.Contains($key)) { $groups[$key] = @() } - $groups[$key] += $who - } - foreach ($key in $groups.Keys) { - $status, $what = $key -split '\|', 2 - $who = $groups[$key] -join ', ' - switch ($status) { - 'installed' { Write-Ok "Added $what to $who" } - 'already_installed' { Write-Skip "$what was already in $who" } - 'installed_login_incomplete' { Write-Warn "Added $what to $who, but sign-in did not finish" } - 'client_not_found' { Write-Warn "Skipped $what for ${who}: CLI not on PATH" } - 'config_unreadable' { Write-Warn "Skipped $what for ${who}: config could not be read" } - default { Write-Warn "Could not add $what to $who" } - } - } - if (-not $mcpOk) { - if ($rows.Count -eq 0) { Write-Warn 'Could not add the Mapbox MCP servers.' } - Write-Detail "Run 'mapbox mcp install --global' to see why and try again." - } + } + if (-not $mcpOk) { + if ($mcpErr -match '"code"\s*:\s*"mcp_client_not_found"') { + # The agent found above takes skills but not + # an MCP server this CLI can register. + Write-Skip 'No Claude Code, Codex, VS Code or Cursor here to add them to.' } else { - Write-Skip "Mapbox MCP servers not added to $mcpClients. Run this any time:" - Write-Detail 'mapbox mcp install --global' + if ($rows.Count -eq 0) { Write-Warn 'Could not add the Mapbox MCP servers.' } + Write-Detail "Run 'mapbox mcp install --global' to see why and try again." } - $Layout.Indent = ' ' } + } else { + Write-Skip 'Skipped the Mapbox MCP servers. Run this any time:' + Write-Detail 'mapbox mcp install --global' } + $Layout.Indent = ' ' } finally { $ErrorActionPreference = $previousErrorAction } diff --git a/scripts/install.sh b/scripts/install.sh index a458d9b..94766f9 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -943,8 +943,10 @@ agent_names_from() { # agent_names_from : comma-joined agent labels paste -sd, - | sed 's/,/, /g' } +agents_found=no if agent_setup_allowed; then if agent_check=$(log_capture "${INSTALL_DIR}/mapbox" generate-skills --global --dry-run -o json); then + agents_found=yes agent_names="$(agent_names_from "$agent_check")" step_ok "Coding agents found: ${agent_names}." do_agent_setup=no @@ -1010,10 +1012,19 @@ fi # # A separate question from the skill above: this registers a hosted server in # the coding agent's own config, next to servers the user added, rather than -# writing a directory this CLI owns. `mapbox mcp install` never replaces an -# entry that is already there (src/mcp.rs), so on a reinstall it is only -# offered when something is left to register. Same opt-out and same -# no-terminal rule as the skill. +# writing a directory this CLI owns. Asked only when the skill step above +# found a coding agent, or VS Code is there, with the same opt-out and +# no-terminal rule. VS Code takes an MCP server but no skill, so the skill +# step can't find it; ~/.vscode is where it keeps extensions on every OS. +# +# Nothing here checks which clients are installed or what they already have. +# That check runs each client's own CLI, and Claude Code's takes over a second +# per call, so it held the screen still for several seconds between the two +# questions, even for anyone who then answered no. `mapbox mcp install` tries +# each client itself once someone says yes: it skips one that isn't there and +# never replaces an entry that is (src/mcp.rs). So a reinstall asks again even +# when everything is registered, a yes reports each server as already there, +# and a no is reported as skipped, since nothing knows whether it was missing. # "status|server|client" per row of `mapbox mcp install -o json`, with the # labels src/mcp.rs prints in place of its ids. @@ -1046,66 +1057,63 @@ mcp_grouped() { # mcp_grouped } END { for (i = 1; i <= n; i++) print order[i] "|" who[order[i]] }' } -# The clients a dry run would register anything with, comma-joined. -mcp_clients_from() { # mcp_clients_from - mcp_rows "$1" | awk -F'|' '$1 == "would_install" && !seen[$3]++ { print $3 }' | - paste -sd, - | sed 's/,/, /g' -} - -if agent_setup_allowed; then - # Fails with mcp_client_not_found when no supported client is installed, - # which is most machines: nothing to offer, so nothing is said. - if mcp_check=$(log_capture "${INSTALL_DIR}/mapbox" mcp install --global --dry-run -o json); then - mcp_clients="$(mcp_clients_from "$mcp_check")" - if [ -n "$mcp_clients" ]; then - do_mcp_setup=no - if have_tty; then - ask_title "Add the Mapbox MCP servers to ${mcp_clients}?" - ask_note "This runs $(ask_cmd 'mapbox mcp install --global'), now or any time later." - answer="$(ask " Add them now? ${DIM}[y/N]${RESET} ")" || answer='' - is_yes "$answer" && do_mcp_setup=yes - STEP_INDENT=' ' - fi - if [ "$do_mcp_setup" = yes ]; then - # Each client's own CLI does the registering, and Codex's can - # stop to print a sign-in URL, so its output is shown live. - if run_live "Adding the Mapbox MCP servers" "${WORK_DIR}/mcp-install.log" "${WORK_DIR}/mcp-install.json" \ - "${INSTALL_DIR}/mapbox" mcp install --global -o json; then - mcp_ok=yes - else - mcp_ok=no - fi - mcp_grouped "$(cat "${WORK_DIR}/mcp-install.json" 2>/dev/null)" >"${WORK_DIR}/mcp-rows" - # One line per server and outcome, so a server added just now - # reads differently from one that was already there. - mcp_trouble=no - while IFS='|' read -r status what who; do - case "$status" in - installed | already_installed) ;; - *) mcp_trouble=yes ;; - esac - case "$status" in - installed) step_ok "Added ${what} to ${who}" ;; - already_installed) step_skip "${what} was already in ${who}" ;; - installed_login_incomplete) step_warn "Added ${what} to ${who}, but sign-in did not finish" ;; - client_not_found) step_warn "Skipped ${what} for ${who}: CLI not on PATH" ;; - config_unreadable) step_warn "Skipped ${what} for ${who}: config could not be read" ;; - *) step_warn "Could not add ${what} to ${who}" ;; - esac - done <"${WORK_DIR}/mcp-rows" - if [ "$mcp_ok" = no ]; then - # A failure no row above accounts for still gets a line of - # its own, rather than a list of successes. - [ "$mcp_trouble" = yes ] || step_warn "Not every Mapbox MCP server could be added." - step_detail "Run 'mapbox mcp install --global' to see why and try again." - fi +if agent_setup_allowed && { [ "$agents_found" = yes ] || [ -d "${HOME}/.vscode" ]; }; then + do_mcp_setup=no + if have_tty; then + ask_title "Add the Mapbox MCP servers to your coding agents?" + ask_note "This runs $(ask_cmd 'mapbox mcp install --global'), now or any time later." + answer="$(ask " Add them now? ${DIM}[y/N]${RESET} ")" || answer='' + is_yes "$answer" && do_mcp_setup=yes + STEP_INDENT=' ' + fi + if [ "$do_mcp_setup" = yes ]; then + # Each client's own CLI does the registering, and Codex's can + # stop to print a sign-in URL, so its output is shown live. + if run_live "Adding the Mapbox MCP servers" "${WORK_DIR}/mcp-install.log" "${WORK_DIR}/mcp-install.json" \ + "${INSTALL_DIR}/mapbox" mcp install --global -o json; then + mcp_ok=yes + else + mcp_ok=no + fi + mcp_grouped "$(cat "${WORK_DIR}/mcp-install.json" 2>/dev/null)" >"${WORK_DIR}/mcp-rows" + # One line per server and outcome, so a server added just now + # reads differently from one that was already there. + mcp_trouble=no + while IFS='|' read -r status what who; do + case "$status" in + installed | already_installed) ;; + *) mcp_trouble=yes ;; + esac + case "$status" in + installed) step_ok "Added ${what} to ${who}" ;; + already_installed) step_skip "${what} was already in ${who}" ;; + installed_login_incomplete) step_warn "Added ${what} to ${who}, but sign-in did not finish" ;; + client_not_found) step_warn "Skipped ${what} for ${who}: CLI not on PATH" ;; + config_unreadable) step_warn "Skipped ${what} for ${who}: config could not be read" ;; + *) step_warn "Could not add ${what} to ${who}" ;; + esac + done <"${WORK_DIR}/mcp-rows" + if [ "$mcp_ok" = no ]; then + if grep -Eqs '"code"[[:space:]]*:[[:space:]]*"mcp_client_not_found"' "${WORK_DIR}/mcp-install.log" "${WORK_DIR}/mcp-install.json"; then + # The agent found above takes skills but not an MCP + # server this CLI can register (Goose, Amp, ...). + step_skip "No Claude Code, Codex, VS Code or Cursor here to add them to." else - step_skip "Mapbox MCP servers not added to ${mcp_clients}. Run this any time:" - step_detail "${DIM}mapbox mcp install --global${RESET}" + # A failure no row above accounts for still gets a line + # of its own, rather than a list of successes. + if [ ! -s "${WORK_DIR}/mcp-rows" ]; then + step_warn "Could not add the Mapbox MCP servers." + elif [ "$mcp_trouble" = no ]; then + step_warn "Not every Mapbox MCP server could be added." + fi + step_detail "Run 'mapbox mcp install --global' to see why and try again." fi - STEP_INDENT=' ' fi + else + step_skip "Skipped the Mapbox MCP servers. Run this any time:" + step_detail "${DIM}mapbox mcp install --global${RESET}" fi + STEP_INDENT=' ' fi # --- The Tilesets CLI ------------------------------------------------------ diff --git a/scripts/test-install.ps1 b/scripts/test-install.ps1 index 6fc77e9..bb76d68 100644 --- a/scripts/test-install.ps1 +++ b/scripts/test-install.ps1 @@ -200,8 +200,9 @@ esac # variables so the same binary covers every branch: MAPBOX_TEST_INSTALL_MODE # (ok | already_installed) and MAPBOX_TEST_UPDATE_MODE (clean | dirty, read # only once install reports already_installed) - the same two knobs -# test-install.sh's agent-channel fixture uses. MAPBOX_TEST_MCP_MODE (none | -# fresh) drives `mcp`; only its dry run is reachable from here, see below. +# test-install.sh's agent-channel fixture uses. `mcp` only marks that it ran, +# in the file MAPBOX_TEST_MCP_RAN names: install.ps1 should run it only after +# a yes at a console, which no case here can give. function New-AgentFakeBinary([string]$Path, [string]$VersionText) { $ErrorActionPreference = 'Continue' if ($OnWindows) { @@ -252,12 +253,10 @@ class Program { } } if (a0 == "mcp") { - if ((Environment.GetEnvironmentVariable("MAPBOX_TEST_MCP_MODE") ?? "none") == "none") { - Console.Error.WriteLine("{\"code\":\"mcp_client_not_found\"}"); - return 1; - } - Console.WriteLine("{\"results\":[{\"client\":\"claude-code\",\"server\":\"mapbox\",\"status\":\"would_install\"},{\"client\":\"claude-code\",\"server\":\"mapbox-devkit\",\"status\":\"would_install\"},{\"client\":\"vscode\",\"server\":\"mapbox\",\"status\":\"would_install\"}]}"); - return 0; + string marker = Environment.GetEnvironmentVariable("MAPBOX_TEST_MCP_RAN"); + if (!string.IsNullOrEmpty(marker)) { System.IO.File.WriteAllText(marker, ""); } + Console.Error.WriteLine("{\"code\":\"mcp_client_not_found\"}"); + return 1; } Console.WriteLine("$VersionText"); return 0; @@ -282,11 +281,9 @@ case "`${1:-}" in echo '{"written":true}' ;; mcp) - if [ "`${MAPBOX_TEST_MCP_MODE:-none}" = none ]; then - echo '{"code":"mcp_client_not_found"}' >&2 - exit 1 - fi - echo '{"results":[{"client":"claude-code","server":"mapbox","status":"would_install"},{"client":"claude-code","server":"mapbox-devkit","status":"would_install"},{"client":"vscode","server":"mapbox","status":"would_install"}]}' + [ -z "`${MAPBOX_TEST_MCP_RAN:-}" ] || : >"`$MAPBOX_TEST_MCP_RAN" + echo '{"code":"mcp_client_not_found"}' >&2 + exit 1 ;; agent-skills) sub="`$2" @@ -556,7 +553,7 @@ function New-CaseEnv([string]$Name) { Clear-Env 'MAPBOX_CLI_NO_AGENT_SETUP' Clear-Env 'MAPBOX_TEST_INSTALL_MODE' Clear-Env 'MAPBOX_TEST_UPDATE_MODE' - Clear-Env 'MAPBOX_TEST_MCP_MODE' + $env:MAPBOX_TEST_MCP_RAN = Join-Path $script:CaseDir 'mcp-ran' # What install.ps1 reads to decide where it is running. Set explicitly so # the same case means the same thing on Windows and on the machine this is # written on. @@ -1070,23 +1067,29 @@ try { Start-Case 'MCP, no console: not asked, not added' New-CaseEnv 'mcp-no-console' $env:MAPBOX_CLI_BASE_URL = "$($open.BaseUrl)/agent-channel" - $env:MAPBOX_TEST_MCP_MODE = 'fresh' Invoke-Installer Expect-Status 0 'exits 0' Expect-NoOut 'Add the Mapbox MCP servers' 'does not ask' - Expect-Out 'Mapbox MCP servers not added to Claude Code, VS Code.' 'names each client once' + Expect-Out 'Skipped the Mapbox MCP servers.' 'says so' + Expect-Equal 'False' "$(Test-Path -LiteralPath $env:MAPBOX_TEST_MCP_RAN)" 'and never runs mapbox mcp, which is the slow part' Expect-Out 'mapbox mcp install --global' 'gives the manual command' Start-Case 'MAPBOX_CLI_NO_AGENT_SETUP skips the MCP question too' New-CaseEnv 'mcp-opt-out' $env:MAPBOX_CLI_BASE_URL = "$($open.BaseUrl)/agent-channel" - $env:MAPBOX_TEST_MCP_MODE = 'fresh' $env:MAPBOX_CLI_NO_AGENT_SETUP = '1' Invoke-Installer Expect-Status 0 'exits 0' # Not just 'MCP': -like ignores case, and this case's own directory, # printed in the PATH note above, is named mcp-opt-out. Expect-NoOut 'Mapbox MCP servers' 'says nothing about it' + + Start-Case 'MCP, no coding agent found: nothing said' + New-CaseEnv 'mcp-no-agent' + Invoke-Installer + Expect-Status 0 'exits 0' + Expect-NoOut 'Mapbox MCP servers' 'says nothing when there is nothing to add them to' + Expect-Equal 'False' "$(Test-Path -LiteralPath $env:MAPBOX_TEST_MCP_RAN)" 'and never runs mapbox mcp' } finally { if ($open) { Stop-ChannelServer $open } if ($gated) { Stop-ChannelServer $gated } diff --git a/scripts/test-install.sh b/scripts/test-install.sh index 800d811..f3a5c2e 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -226,6 +226,7 @@ case "\${1:-}" in exit 1 ;; mcp) + : >"\${HOME}/.mcp-ran" echo '{"code":"mcp_client_not_found"}' >&2 exit 1 ;; @@ -284,8 +285,7 @@ make_channel "${CHANNEL}/v0.1.0-dev.abc1234" v0.1.0-dev.abc1234 # and MAPBOX_TEST_UPDATE_MODE (clean | dirty, read only once install # reports already_installed). Defaults match the plain first-install case. # MAPBOX_TEST_MCP_MODE (none | fresh | registered | fail) drives `mcp`; -# none, no MCP client found, is the default so cases about the skill see -# only the skill's question. +# none is a machine with no client mapbox can register a server with. AGENT_CHANNEL="${ROOT}/agent-channel" mkdir -p "${AGENT_CHANNEL}/latest/build" cat >"${AGENT_CHANNEL}/latest/build/mapbox" <<'EOF' @@ -327,20 +327,14 @@ case "${1:-}" in esac ;; mcp) + : >"${HOME}/.mcp-ran" mcp_mode="${MAPBOX_TEST_MCP_MODE:-none}" if [ "$mcp_mode" = none ]; then echo '{"code":"mcp_client_not_found"}' >&2 exit 1 fi - is_dry=no - for a in "$@"; do [ "$a" = "--dry-run" ] && is_dry=yes; done - if [ "$is_dry" = yes ]; then - if [ "$mcp_mode" = registered ]; then - status=already_installed - else - status=would_install - fi - echo '{"results":[{"client":"claude-code","server":"mapbox","status":"'"$status"'"},{"client":"claude-code","server":"mapbox-devkit","status":"'"$status"'"},{"client":"vscode","server":"mapbox","status":"'"$status"'"}]}' + if [ "$mcp_mode" = registered ]; then + echo '{"results":[{"client":"claude-code","server":"mapbox","status":"already_installed"},{"client":"claude-code","server":"mapbox-devkit","status":"already_installed"},{"client":"vscode","server":"mapbox","status":"already_installed"}]}' elif [ "$mcp_mode" = fail ]; then echo '{"results":[{"client":"claude-code","server":"mapbox","status":"installed"},{"client":"vscode","error":"--add-mcp rejected {\\"name\\":\\"mapbox\\"}","server":"mapbox","status":"failed"}]}' exit 1 @@ -675,6 +669,9 @@ new_case_env() { # case-name export MAPBOX_CLI_BASE_URL MAPBOX_INSTALL_DIR PATH } +# Whether the installer ran `mapbox mcp` at all: the fake marks it in HOME. +mcp_ran() { if [ -f "${HOME}/.mcp-ran" ]; then echo yes; else echo no; fi; } + shim() { # shim-name [name-on-path] cp "${SHIMS}/$1" "${CASE_SHIMS}/${2:-$1}" } @@ -1322,6 +1319,7 @@ export MAPBOX_INSTALL_TILESETS=no run_piped && status=0 || status=$? expect_status 0 "$status" 'exits 0' expect_no_out 'coding agent' 'says nothing about a step that has nothing to do' +expect_no_out 'MCP' 'nor about the MCP servers' start 'MAPBOX_CLI_NO_AGENT_SETUP opts out even though an agent was detected' new_case_env agent-opt-out @@ -1349,7 +1347,7 @@ start 'a terminal, answered no' new_case_env agent-tty-no export MAPBOX_CLI_BASE_URL="file://${AGENT_CHANNEL}" export MAPBOX_INSTALL_TILESETS=no -run_interactive n && status=0 || status=$? +run_interactive "$(printf 'n\nn')" && status=0 || status=$? expect_status 0 "$status" 'a declined setup is not an install failure' expect_out 'Coding agents found: claude.' 'names the agent' expect_out 'Set up the mapbox CLI skill' 'asks' @@ -1365,7 +1363,7 @@ new_case_env agent-tty-yes-fresh export MAPBOX_CLI_BASE_URL="file://${AGENT_CHANNEL}" export MAPBOX_INSTALL_TILESETS=no export MAPBOX_TEST_INSTALL_MODE=ok -run_interactive y && status=0 || status=$? +run_interactive "$(printf 'y\nn')" && status=0 || status=$? expect_status 0 "$status" 'exits 0' expect_out 'Wrote the mapbox CLI skill for: claude.' 'reports the skill written' expect_out 'Installed the Mapbox Agent Skills library for: claude.' 'reports the library installed' @@ -1376,7 +1374,7 @@ export MAPBOX_CLI_BASE_URL="file://${AGENT_CHANNEL}" export MAPBOX_INSTALL_TILESETS=no export MAPBOX_TEST_INSTALL_MODE=already_installed export MAPBOX_TEST_UPDATE_MODE=clean -run_interactive y && status=0 || status=$? +run_interactive "$(printf 'y\nn')" && status=0 || status=$? expect_status 0 "$status" 'exits 0' expect_out 'Updated the Mapbox Agent Skills library for: claude.' 'updates in place' expect_no_out 'have local changes and were left alone' 'nothing was skipped' @@ -1387,7 +1385,7 @@ export MAPBOX_CLI_BASE_URL="file://${AGENT_CHANNEL}" export MAPBOX_INSTALL_TILESETS=no export MAPBOX_TEST_INSTALL_MODE=already_installed export MAPBOX_TEST_UPDATE_MODE=dirty -run_interactive y && status=0 || status=$? +run_interactive "$(printf 'y\nn')" && status=0 || status=$? expect_status 0 "$status" 'exits 0' expect_out 'Some Mapbox Agent Skills have local changes and were left alone.' \ 'reports the local edit rather than discarding it' @@ -1408,8 +1406,9 @@ export MAPBOX_TEST_MCP_MODE=fresh run_piped && status=0 || status=$? expect_status 0 "$status" 'exits 0 rather than blocking (124 would be a hang)' expect_no_out 'Add the Mapbox MCP servers' 'does not ask' -expect_out 'Mapbox MCP servers not added to Claude Code, VS Code.' 'names each client once' -expect_says "$(grep -c '^ - Mapbox MCP servers not added' "$OUT")" 1 'top level when no question was asked' +expect_says "$(mcp_ran)" no 'and never runs mapbox mcp, which is the slow part' +expect_out 'Skipped the Mapbox MCP servers.' 'says so' +expect_says "$(grep -c '^ - Skipped the Mapbox MCP servers' "$OUT")" 1 'top level when no question was asked' expect_out 'mapbox mcp install --global' 'gives the manual command' start 'MCP, a terminal, answered yes' @@ -1419,7 +1418,7 @@ export MAPBOX_INSTALL_TILESETS=no export MAPBOX_TEST_MCP_MODE=fresh run_interactive "$(printf 'n\ny')" && status=0 || status=$? expect_status 0 "$status" 'exits 0' -expect_out 'Add the Mapbox MCP servers to Claude Code, VS Code?' 'asks, separately from the skill' +expect_out 'Add the Mapbox MCP servers to your coding agents?' 'asks, separately from the skill' expect_out 'This runs mapbox mcp install --global' 'names the command it runs' expect_out 'This runs mapbox mcp install --global, now or any time later.' 'says it can be run later instead' expect_out 'Add them now? [y/N]' 'asks about now' @@ -1462,17 +1461,50 @@ export MAPBOX_INSTALL_TILESETS=no export MAPBOX_TEST_MCP_MODE=fresh run_interactive "$(printf 'n\nn')" && status=0 || status=$? expect_status 0 "$status" 'a declined setup is not an install failure' -expect_out 'Mapbox MCP servers not added to Claude Code, VS Code.' 'says so' +expect_out 'Skipped the Mapbox MCP servers.' 'says so' expect_no_out 'Added the Mapbox MCP servers' 'adds nothing' +expect_says "$(mcp_ran)" no 'and never runs mapbox mcp' -start 'MCP, everything already registered: not asked again' +start 'MCP, everything already registered: asked, and a yes says so' new_case_env mcp-registered export MAPBOX_CLI_BASE_URL="file://${AGENT_CHANNEL}" export MAPBOX_INSTALL_TILESETS=no export MAPBOX_TEST_MCP_MODE=registered -run_interactive n && status=0 || status=$? +run_interactive "$(printf 'n\ny')" && status=0 || status=$? +expect_status 0 "$status" 'exits 0' +expect_out 'Add the Mapbox MCP servers to your coding agents?' 'asks: only a yes runs the check' +expect_out 'Mapbox MCP was already in Claude Code, VS Code' 'reports what was already there' +expect_no_out 'Added Mapbox' 'claims to add nothing' + +start 'MCP, only VS Code, which takes no skill: still offered' +new_case_env mcp-vscode-only +export MAPBOX_INSTALL_TILESETS=no +mkdir -p "${HOME}/.vscode" +run_piped && status=0 || status=$? +expect_status 0 "$status" 'exits 0' +expect_no_out 'Coding agents found' 'the skill step found nothing' +expect_out 'Skipped the Mapbox MCP servers.' 'but the MCP step still runs' +expect_says "$(mcp_ran)" no 'without running mapbox mcp' + +start 'MCP, only VS Code, opted out: nothing said' +new_case_env mcp-vscode-opt-out +export MAPBOX_INSTALL_TILESETS=no +export MAPBOX_CLI_NO_AGENT_SETUP=1 +mkdir -p "${HOME}/.vscode" +run_piped && status=0 || status=$? +expect_status 0 "$status" 'exits 0' +expect_no_out 'MCP' 'the opt-out covers it too' + +start 'MCP, an agent found but no client mapbox can register with: a yes says so' +new_case_env mcp-no-client +export MAPBOX_CLI_BASE_URL="file://${AGENT_CHANNEL}" +export MAPBOX_INSTALL_TILESETS=no +export MAPBOX_TEST_MCP_MODE=none +run_interactive "$(printf 'n\ny')" && status=0 || status=$? expect_status 0 "$status" 'exits 0' -expect_no_out 'MCP' 'says nothing when there is nothing to add' +expect_out 'No Claude Code, Codex, VS Code or Cursor here to add them to.' 'says there was nothing to add them to' +expect_no_out 'Could not add' 'not as a failure' +expect_no_out 'to see why and try again' 'with nothing to retry' start 'MCP, a failed registration is reported, not fatal' new_case_env mcp-fail