mirror of
https://github.com/chainbase-labs/Agentkey.git
synced 2026-09-20 14:20:23 +08:00
83363014c4
## Summary - Drive both `npx skills add -a` and `@agentkey/mcp --auth-login --only` from a single detected-agent list. MCP registration now follows the same per-host auto-detection that skill install already does, expanding MCP auto-registration from 3 clients to **16**. - Fix the longstanding `claude-code` marker bug: it included Claude Desktop's config dir, causing skills CLI to target a nonexistent Claude Code on Desktop-only machines. `claude-desktop` is now its own id (MCP-only) — passed to `--auth-login --only` but never to `skills add`. - Detect Claude Desktop via `/Applications/Claude.app` / `%LOCALAPPDATA%\AnthropicClaude` so "installed but never launched" still registers (Linux still requires the config dir). - New `scripts/dev-smoke.sh` — sandboxed 4-phase regression suite, 42 assertions, ~10s, never touches real `$HOME`. Run before any PR touching install/uninstall scripts. ## Depends on [chainbase-labs/AgentKey-Server#9](https://github.com/chainbase-labs/AgentKey-Server/pull/9) — adds `--only <ids>` to `@agentkey/mcp --auth-login`. Older CLI versions silently ignore the flag, so this PR is forward-compatible either way. ## Uninstaller (the bigger gap before this) The previous uninstaller only cleaned 3 config paths and only knew the `mcpServers.<name>` JSON shape. With 13 new agents using 4 different schema dialects, that left AgentKey configured everywhere after uninstall. - Expanded MCP cleanup to **14 JSON paths + codex TOML**, covering all 16 auto-registered agents - Schema-agnostic JSON scrub: walks the tree and drops dict keys whose name EXACTLY matches our server names. Handles `mcpServers.<name>` / `mcp.<name>` / `amp.mcpServers.<name>` / `projects.X.mcpServers.<name>` in one pass - Codex TOML splice via awk / PowerShell (no parser dep) — drops `[mcp_servers.agentkey]` + legacy quoted block, preserves sibling sections - `droid mcp remove` + `openclaw mcp unset` for CLI-registered agents - **Exact-match** server names (not substring) so user keys like `agentkey-helper` are preserved (regression test included in dev-smoke) ## Bugs fixed during review | Where | Bug | |---|---| | install.sh:487 | Unbound `$TARGETS` variable (renamed during refactor) — `set -u` would have made this fatal | | install.ps1 | `$SkillTargets.Count` used where `$AllTargets.Count` was meant — diverged from install.sh behavior | | install.sh | `--only claude-desktop` ran `skills add -a` with no filter, defeating the user's `--only` intent. Now correctly skips the skill step | | install.sh helpers | Leaked-scope loop vars (`_ids`, `_id`) — declared `local -a` | ## Test plan - [x] `scripts/dev-smoke.sh` — 42 passing / 0 failing (Phase 1 unit tests + Phase 2 installer + Phase 3 writer schemas + Phase 4 uninstaller w/ false-positive guard) - [x] `bash -n` clean for install.sh + uninstall.sh - [x] `--list-agents` correctly lists `claude-desktop` as a separate id - [x] `--only claude-desktop --skip-mcp --yes` walks the new "MCP-only, skip skill" branch - [x] Auto-detect path tested via `bash -x` trace under `set -u` (no unbound-variable explosion) - [x] Uninstaller decoy fixtures: `agentkey-helper`, `other-svr`, `[mcp_servers.other]`, `[unrelated_section]` all preserved after scrub - [ ] Windows: `install.ps1` / `uninstall.ps1` syntax-checked but not runtime-tested (no Windows box handy — happy to test if reviewer has one) --------- Co-authored-by: Bruce <bruce@checkabc.me>
426 lines
20 KiB
PowerShell
426 lines
20 KiB
PowerShell
#Requires -Version 5.1
|
||
<#
|
||
.SYNOPSIS
|
||
AgentKey installer for Windows
|
||
.DESCRIPTION
|
||
Usage:
|
||
irm https://agentkey.app/install.ps1 | iex
|
||
& ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -Yes
|
||
& ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -Only "claude-code,cursor"
|
||
& ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -NoTelemetry
|
||
|
||
Behavior mirrors install.sh: checks Node >= 18 (installs via winget/scoop/choco),
|
||
auto-detects which AI agents are installed and runs `npx skills add` for them,
|
||
then `npx @agentkey/cli --auth-login` for device auth. The auth step always
|
||
tries to open a local browser AND prints the URL so headless / SSH users can
|
||
copy it elsewhere. MCP config is written automatically for Claude Code /
|
||
Claude Desktop / Cursor.
|
||
#>
|
||
|
||
[CmdletBinding()]
|
||
param(
|
||
[switch]$Yes,
|
||
[switch]$Interactive,
|
||
[string]$Only,
|
||
[switch]$AllAgents,
|
||
[switch]$ListAgents,
|
||
[switch]$SkipSkill,
|
||
[switch]$SkipMcp,
|
||
[switch]$NoTelemetry,
|
||
[switch]$Help
|
||
)
|
||
|
||
$ErrorActionPreference = 'Stop'
|
||
$SkillRepo = 'chainbase-labs/agentkey'
|
||
$CliPackage = '@agentkey/cli'
|
||
$NodeMinMajor = 18
|
||
|
||
# ── Agent markers (mirror of install.sh) ──────────────────────────────────
|
||
# Subset of vercel-labs/skills' 45 supported agent IDs that have reliable
|
||
# Windows-side markers. Sync source:
|
||
# https://github.com/vercel-labs/skills (Supported Agents table).
|
||
#
|
||
# IMPORTANT: ids here MUST match the `--only` ids accepted by both
|
||
# `npx skills add -a` and `npx -y @agentkey/cli --auth-login --only`.
|
||
# `claude-desktop` is the documented exception (no skill install path, but
|
||
# MCP config is writable) — it's listed below and used only for MCP --only.
|
||
$AgentMarkers = @(
|
||
@{ Id = 'claude-code'; Markers = @("path:$env:USERPROFILE\.claude.json", 'cmd:claude') }
|
||
@{ Id = 'claude-desktop'; Markers = @("path:$env:LOCALAPPDATA\AnthropicClaude", "path:$env:APPDATA\Claude\claude_desktop_config.json", "path:$env:APPDATA\Claude") }
|
||
@{ Id = 'cursor'; Markers = @("path:$env:USERPROFILE\.cursor", 'cmd:cursor', "path:$env:LOCALAPPDATA\Programs\cursor") }
|
||
@{ Id = 'codex'; Markers = @("path:$env:USERPROFILE\.codex", 'cmd:codex') }
|
||
@{ Id = 'gemini-cli'; Markers = @("path:$env:USERPROFILE\.gemini", 'cmd:gemini') }
|
||
@{ Id = 'opencode'; Markers = @("path:$env:APPDATA\opencode", "path:$env:USERPROFILE\.opencode", 'cmd:opencode') }
|
||
@{ Id = 'openclaw'; Markers = @("path:$env:USERPROFILE\.openclaw", 'cmd:openclaw') }
|
||
@{ Id = 'qwen-code'; Markers = @("path:$env:USERPROFILE\.qwen", 'cmd:qwen') }
|
||
@{ Id = 'iflow-cli'; Markers = @("path:$env:USERPROFILE\.iflow", 'cmd:iflow') }
|
||
@{ Id = 'windsurf'; Markers = @("path:$env:USERPROFILE\.codeium\windsurf", "path:$env:USERPROFILE\.windsurf", 'cmd:windsurf') }
|
||
@{ Id = 'warp'; Markers = @("path:$env:USERPROFILE\.warp") }
|
||
@{ Id = 'amp'; Markers = @("path:$env:APPDATA\amp", 'cmd:amp') }
|
||
@{ Id = 'crush'; Markers = @("path:$env:APPDATA\crush", 'cmd:crush') }
|
||
@{ Id = 'goose'; Markers = @("path:$env:APPDATA\goose", 'cmd:goose') }
|
||
@{ Id = 'droid'; Markers = @('cmd:droid') }
|
||
@{ Id = 'kode'; Markers = @('cmd:kode') }
|
||
@{ Id = 'kilo'; Markers = @('cmd:kilo') }
|
||
@{ Id = 'kimi-cli'; Markers = @("path:$env:USERPROFILE\.kimi", 'cmd:kimi') }
|
||
@{ Id = 'kiro-cli'; Markers = @("path:$env:USERPROFILE\.kiro", 'cmd:kiro') }
|
||
)
|
||
|
||
# Agent ids that are MCP-only (no skill install path). Never passed to
|
||
# `npx skills add -a`, only to `--auth-login --only`.
|
||
$McpOnlyAgents = @('claude-desktop')
|
||
|
||
# Agent ids whose MCP registration the installer can drive automatically.
|
||
# Mirror of MCP_AUTO_AGENTS in install.sh and AGENT_REGISTRY in
|
||
# AgentKey-Server/cli/src/lib/mcp-clients.ts. Keep these three in sync.
|
||
$McpAutoAgents = @(
|
||
'claude-code', 'claude-desktop', 'cursor', 'codex', 'gemini-cli',
|
||
'opencode', 'qwen-code', 'iflow-cli', 'kimi-cli', 'kiro-cli',
|
||
'windsurf', 'warp', 'amp', 'crush', 'droid', 'openclaw'
|
||
)
|
||
|
||
# ── UI helpers ────────────────────────────────────────────────────────────
|
||
function Write-Banner {
|
||
Write-Host ''
|
||
Write-Host ' █████ ██████ ███████ ███ ██ ████████ ██ ██ ███████ ██ ██' -ForegroundColor Cyan
|
||
Write-Host ' ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ' -ForegroundColor Cyan
|
||
Write-Host ' ███████ ██ ███ █████ ██ ██ ██ ██ █████ █████ ████ ' -ForegroundColor Cyan
|
||
Write-Host ' ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ' -ForegroundColor Cyan
|
||
Write-Host ' ██ ██ ██████ ███████ ██ ████ ██ ██ ██ ███████ ██ ' -ForegroundColor Cyan
|
||
Write-Host ''
|
||
Write-Host ' One command. Full internet access for your AI agent.' -ForegroundColor White
|
||
Write-Host ' https://agentkey.app' -ForegroundColor DarkGray
|
||
Write-Host ''
|
||
}
|
||
|
||
function Write-Step ($text) { Write-Host ''; Write-Host " $text" -ForegroundColor White }
|
||
function Write-Info ($text) { Write-Host " › $text" -ForegroundColor Gray }
|
||
function Write-Ok ($text) { Write-Host " ✓ $text" -ForegroundColor Green }
|
||
function Write-Warn2($text) { Write-Host " ! $text" -ForegroundColor Yellow }
|
||
function Write-Err ($text) { Write-Host " ✗ $text" -ForegroundColor Red }
|
||
function Write-Muted($text) { Write-Host " $text" -ForegroundColor DarkGray }
|
||
|
||
function Die ($text) { Write-Err $text; exit 1 }
|
||
|
||
# ── Helpers: agent detection ──────────────────────────────────────────────
|
||
function Test-AgentMarker {
|
||
param([string]$Marker)
|
||
if ($Marker.StartsWith('cmd:')) {
|
||
return [bool](Get-Command $Marker.Substring(4) -ErrorAction SilentlyContinue)
|
||
}
|
||
if ($Marker.StartsWith('path:')) {
|
||
return Test-Path -LiteralPath $Marker.Substring(5)
|
||
}
|
||
return $false
|
||
}
|
||
|
||
function Get-DetectedAgents {
|
||
$hits = New-Object System.Collections.Generic.List[string]
|
||
foreach ($entry in $AgentMarkers) {
|
||
foreach ($m in $entry.Markers) {
|
||
if (Test-AgentMarker $m) { $hits.Add($entry.Id) | Out-Null; break }
|
||
}
|
||
}
|
||
return @($hits | Sort-Object -Unique)
|
||
}
|
||
|
||
# ── Help ──────────────────────────────────────────────────────────────────
|
||
if ($Help) {
|
||
@'
|
||
AgentKey installer for Windows
|
||
|
||
Usage:
|
||
irm https://agentkey.app/install.ps1 | iex
|
||
& ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -Yes
|
||
|
||
Parameters:
|
||
-Yes Non-interactive: install skill to every detected agent, no prompts
|
||
-Interactive Force interactive mode (fails if console input is redirected)
|
||
-Only <a,b,c> Only install skill for these agents (e.g. "claude-code,cursor")
|
||
-AllAgents Skip auto-detection; let 'skills' CLI install for every detected agent
|
||
-ListAgents Print the agents we'd auto-select on this machine and exit
|
||
-SkipSkill Skip the skill install step (only run MCP auth)
|
||
-SkipMcp Skip the MCP auth step (only install the skill)
|
||
-NoTelemetry Disable anonymous usage telemetry (writes
|
||
%USERPROFILE%\.config\agentkey\telemetry-disabled so
|
||
the skill stays opted-out across runs)
|
||
-Help Show this help
|
||
|
||
Behavior:
|
||
The installer auto-detects which AI agents are on this machine and
|
||
pre-selects them for skill installation. The auth step always tries
|
||
to open a browser and also prints the URL — so SSH / WinRM / Docker
|
||
/ OpenClaw users can copy the URL to any device with a browser.
|
||
'@
|
||
exit 0
|
||
}
|
||
|
||
if ($ListAgents) {
|
||
$detected = Get-DetectedAgents
|
||
if ($detected.Count -gt 0) { $detected -join "`n" | Write-Output }
|
||
else { Write-Host 'no agents detected on this host' -ForegroundColor Yellow }
|
||
exit 0
|
||
}
|
||
|
||
Write-Banner
|
||
|
||
# ── 1. Preflight ──────────────────────────────────────────────────────────
|
||
Write-Step '1. Preflight'
|
||
|
||
# Platform guard
|
||
if (-not $IsWindows -and $PSVersionTable.PSVersion.Major -ge 6) {
|
||
Die 'This script targets Windows. On macOS/Linux use install.sh instead.'
|
||
}
|
||
Write-Ok 'Platform: windows'
|
||
|
||
# Resolve interactive mode. PowerShell's `iex` runs in the current session, so
|
||
# Read-Host works natively even under `irm | iex`. The only thing we need to
|
||
# guard is truly redirected input (scheduled tasks, CI with redirected stdin).
|
||
$InputRedirected = $false
|
||
try { $InputRedirected = [Console]::IsInputRedirected } catch { $InputRedirected = $false }
|
||
|
||
$Mode = $null
|
||
if ($Yes) { $Mode = 'noninteractive' }
|
||
elseif ($Interactive) {
|
||
if ($InputRedirected) { Die '-Interactive requested but console input is redirected.' }
|
||
$Mode = 'interactive'
|
||
}
|
||
elseif ($InputRedirected) {
|
||
$Mode = 'noninteractive'
|
||
Write-Warn2 'No interactive console detected — falling back to -Yes'
|
||
}
|
||
else {
|
||
$Mode = 'interactive'
|
||
}
|
||
Write-Ok "Mode: $Mode"
|
||
|
||
# Resolve telemetry intent: -NoTelemetry overrides everything; existing
|
||
# %USERPROFILE%\.config\agentkey\telemetry-disabled file means already-opted-out.
|
||
$TelemetryOptOutFile = Join-Path $env:USERPROFILE '.config\agentkey\telemetry-disabled'
|
||
if ($NoTelemetry) {
|
||
New-Item -ItemType Directory -Path (Split-Path $TelemetryOptOutFile) -Force | Out-Null
|
||
New-Item -ItemType File -Path $TelemetryOptOutFile -Force | Out-Null
|
||
Write-Ok 'Telemetry: disabled (-NoTelemetry)'
|
||
} elseif (Test-Path -LiteralPath $TelemetryOptOutFile) {
|
||
Write-Ok "Telemetry: disabled ($TelemetryOptOutFile exists)"
|
||
} else {
|
||
Write-Info 'Telemetry: anonymous usage stats enabled (re-run with -NoTelemetry to opt out)'
|
||
}
|
||
|
||
# Node check
|
||
function Get-NodeMajor {
|
||
try {
|
||
$v = (& node --version) 2>$null
|
||
if ($v -match '^v(\d+)\.') { return [int]$Matches[1] }
|
||
} catch {}
|
||
return 0
|
||
}
|
||
|
||
function Install-Node {
|
||
Write-Info "Installing Node.js LTS ..."
|
||
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
||
winget install -e --id OpenJS.NodeJS.LTS --silent --accept-source-agreements --accept-package-agreements | Out-Null
|
||
} elseif (Get-Command scoop -ErrorAction SilentlyContinue) {
|
||
scoop install nodejs-lts | Out-Null
|
||
} elseif (Get-Command choco -ErrorAction SilentlyContinue) {
|
||
choco install nodejs-lts -y | Out-Null
|
||
} else {
|
||
Die 'No package manager found (winget/scoop/choco). Install Node.js LTS manually: https://nodejs.org/'
|
||
}
|
||
# Refresh PATH so this session sees the newly installed node
|
||
$env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
|
||
[System.Environment]::GetEnvironmentVariable('Path', 'User')
|
||
Write-Ok 'Node.js installed'
|
||
}
|
||
|
||
$nodeMajor = Get-NodeMajor
|
||
if ($nodeMajor -ge $NodeMinMajor) {
|
||
Write-Ok "Node.js: v$nodeMajor.x"
|
||
} else {
|
||
if ($nodeMajor -gt 0) { Write-Warn2 "Node.js v$nodeMajor found but v$NodeMinMajor+ is required" }
|
||
if ($Mode -eq 'interactive') {
|
||
Write-Host ''
|
||
Write-Host " Node.js v$NodeMinMajor+ is required but not found." -ForegroundColor White
|
||
$reply = Read-Host ' Install it now? [Y/n]'
|
||
if ($reply -match '^(n|no)$') { Die 'Node.js required. Aborting.' }
|
||
}
|
||
Install-Node
|
||
}
|
||
|
||
if (-not (Get-Command npx -ErrorAction SilentlyContinue)) {
|
||
Die 'npx not found after Node install — please reopen your terminal or reinstall Node.js.'
|
||
}
|
||
|
||
# Resolve target agent list — shared between the skill step and the MCP step.
|
||
# $AllTargets — every detected agent, including MCP-only ones (claude-desktop)
|
||
# $SkillTargets — $AllTargets minus MCP-only ids (those would fail `skills add`)
|
||
# $McpTargets — $AllTargets filtered to ids the MCP CLI knows how to write
|
||
$AllTargets = @()
|
||
if ($Only) {
|
||
$AllTargets = @($Only -split ',' | Where-Object { $_ -ne '' })
|
||
Write-Info "Targeting agents from -Only: $($AllTargets -join ', ')"
|
||
} elseif ($AllAgents) {
|
||
Write-Info "Installing for every agent the 'skills' CLI detects (-AllAgents)"
|
||
} else {
|
||
$AllTargets = @(Get-DetectedAgents)
|
||
if ($AllTargets.Count -gt 0) {
|
||
Write-Ok "Detected agents on this host: $($AllTargets -join ', ')"
|
||
Write-Muted '(override with -Only <ids>, or use -AllAgents)'
|
||
} else {
|
||
Write-Info "No agents auto-detected — letting 'skills' CLI scan."
|
||
}
|
||
}
|
||
|
||
$SkillTargets = @($AllTargets | Where-Object { $_ -notin $McpOnlyAgents })
|
||
$McpTargets = @($AllTargets | Where-Object { $_ -in $McpAutoAgents })
|
||
|
||
# ── 2. Install the AgentKey skill ─────────────────────────────────────────
|
||
if ($SkipSkill) {
|
||
Write-Step '2. Install the AgentKey skill'
|
||
Write-Muted 'Skipped (-SkipSkill)'
|
||
} elseif ($AllTargets.Count -gt 0 -and $SkillTargets.Count -eq 0) {
|
||
# User explicitly selected only MCP-only ids (e.g. `-Only claude-desktop`).
|
||
# There's nothing for `skills add` to do — skip the step entirely rather
|
||
# than fall through to "install for every detected agent."
|
||
Write-Step '2. Install the AgentKey skill'
|
||
Write-Muted "Skipped — selected targets ($($AllTargets -join ',')) are MCP-only (no skill install path)."
|
||
} else {
|
||
Write-Step '2. Install the AgentKey skill'
|
||
|
||
$skillsArgs = @('-y', 'skills', 'add', $SkillRepo, '-g')
|
||
if ($SkillTargets.Count -gt 0) {
|
||
$skillsArgs += '-a'
|
||
$skillsArgs += $SkillTargets
|
||
}
|
||
# Always pass -y in noninteractive mode AND when we already resolved
|
||
# an explicit target list — there's nothing left to ask the user.
|
||
if ($Mode -eq 'noninteractive' -or $AllTargets.Count -gt 0) {
|
||
$skillsArgs += '-y'
|
||
}
|
||
|
||
& npx @skillsArgs
|
||
if ($LASTEXITCODE -ne 0) { Die "Failed to install skill via 'skills' CLI" }
|
||
# The skills CLI sometimes prints "Installation failed" and still
|
||
# exits 0 (e.g. network error during git clone). Verify the skill
|
||
# actually landed on disk before declaring success. Paths must mirror
|
||
# the `path:` markers in $AgentMarkers: most agents live under
|
||
# %USERPROFILE%\.<agent>, but amp / crush / goose / opencode live
|
||
# under %APPDATA%\<agent>.
|
||
$userHome = [Environment]::GetFolderPath('UserProfile')
|
||
$candidatePaths = @(
|
||
(Join-Path $userHome '.agents\skills\agentkey'),
|
||
(Join-Path $userHome '.claude\skills\agentkey'),
|
||
(Join-Path $userHome '.cursor\skills\agentkey'),
|
||
(Join-Path $userHome '.codex\skills\agentkey'),
|
||
(Join-Path $userHome '.gemini\skills\agentkey'),
|
||
(Join-Path $userHome '.opencode\skills\agentkey'),
|
||
(Join-Path $userHome '.openclaw\skills\agentkey'),
|
||
(Join-Path $userHome '.qwen\skills\agentkey'),
|
||
(Join-Path $userHome '.iflow\skills\agentkey'),
|
||
(Join-Path $userHome '.windsurf\skills\agentkey'),
|
||
(Join-Path $userHome '.warp\skills\agentkey'),
|
||
(Join-Path $userHome '.kimi\skills\agentkey'),
|
||
(Join-Path $userHome '.kiro\skills\agentkey'),
|
||
# APPDATA-rooted agents (parity with install.sh's $HOME/.config/<agent>)
|
||
(Join-Path $env:APPDATA 'amp\skills\agentkey'),
|
||
(Join-Path $env:APPDATA 'crush\skills\agentkey'),
|
||
(Join-Path $env:APPDATA 'goose\skills\agentkey'),
|
||
(Join-Path $env:APPDATA 'opencode\skills\agentkey')
|
||
)
|
||
$agentkeyFound = $false
|
||
foreach ($abs in $candidatePaths) {
|
||
if (Test-Path (Join-Path $abs 'SKILL.md')) {
|
||
$agentkeyFound = $true
|
||
break
|
||
}
|
||
}
|
||
if (-not $agentkeyFound) {
|
||
Die "Skill install reported success but no agentkey SKILL.md was created — likely a network or git clone failure. Retry: npx -y skills add $SkillRepo -g -y"
|
||
}
|
||
Write-Ok 'Skill installed'
|
||
}
|
||
|
||
# ── 3. MCP authentication ────────────────────────────────────────────────
|
||
# Always run auth-login. The CLI itself decides whether the existing token
|
||
# can be reused or a fresh device-code flow is needed — the installer no
|
||
# longer second-guesses by sniffing config files (which produced false
|
||
# positives across the stdio → HTTP schema change).
|
||
if ($SkipMcp) {
|
||
Write-Step '3. Register the MCP server'
|
||
Write-Muted 'Skipped (-SkipMcp)'
|
||
} elseif ($AllTargets.Count -gt 0 -and $McpTargets.Count -eq 0) {
|
||
# User selected ONLY MCP-incompatible agents (goose / kode / kilo via
|
||
# -Only). Running auth-login without --only would silently register MCP
|
||
# in every detected agent, overriding the user's explicit scope. Skip
|
||
# rather than over-register. See PR #41 B1.
|
||
Write-Step '3. Register the MCP server'
|
||
Write-Muted "Skipped — selected agents ($($AllTargets -join ',')) need manual MCP setup (see SKILL.md Fallback section)."
|
||
} else {
|
||
# Pin MCP registration to the same agent list the skill step targeted.
|
||
# When McpTargets is empty (auto-detect found nothing), let
|
||
# `@agentkey/cli` do its own detection — same fallback we use for skill
|
||
# install. Older CLI versions silently ignore --only, so this is
|
||
# forward-compatible.
|
||
$authArgs = @('--auth-login')
|
||
if ($McpTargets.Count -gt 0) {
|
||
$authArgs += '--only'
|
||
$authArgs += ($McpTargets -join ',')
|
||
}
|
||
|
||
Write-Step '3. Register the MCP server'
|
||
Write-Info 'Opening your browser for AgentKey device authentication ...'
|
||
if ($McpTargets.Count -gt 0) {
|
||
Write-Muted "Will register MCP in: $($McpTargets -join ', ')"
|
||
} else {
|
||
Write-Muted "If a browser doesn't open (SSH / WinRM / Docker / headless), the auth URL is also printed below — open it on any device to finish."
|
||
}
|
||
Write-Host ''
|
||
|
||
# Telemetry context for install_completed. Opt-out is honored at the
|
||
# SOURCE: when AGENTKEY_TELEMETRY=0, no other context env vars are
|
||
# exported — hostname-derived fingerprint, agent lists, and installer
|
||
# flags are never computed nor passed to the child `npx @agentkey/cli`
|
||
# process. The server treats AGENTKEY_TELEMETRY=0 as a hard skip.
|
||
if ($NoTelemetry -or (Test-Path -LiteralPath $TelemetryOptOutFile)) {
|
||
$env:AGENTKEY_TELEMETRY = '0'
|
||
} else {
|
||
$env:AGENTKEY_TELEMETRY = '1'
|
||
|
||
$_hn = [System.Net.Dns]::GetHostName()
|
||
$_user = $env:USERNAME
|
||
$_input = "$_hn|windows|$_user"
|
||
$_bytes = [System.Text.Encoding]::UTF8.GetBytes($_input)
|
||
$_sha = [System.Security.Cryptography.SHA256]::Create()
|
||
$_hash = ($_sha.ComputeHash($_bytes) | ForEach-Object { $_.ToString('x2') }) -join ''
|
||
$DeviceFingerprint = $_hash.Substring(0, 16)
|
||
|
||
$DetectedAgents = Get-DetectedAgents
|
||
|
||
$env:AGENTKEY_INSTALL_SOURCE = 'one_liner'
|
||
$env:AGENTKEY_DETECTED_AGENTS = ($DetectedAgents -join ',')
|
||
$env:AGENTKEY_SELECTED_AGENTS = ($AllTargets -join ',')
|
||
$env:AGENTKEY_INSTALLER_FLAGS = ($PSBoundParameters.Keys | ForEach-Object { "-$_" }) -join ','
|
||
$env:AGENTKEY_DEVICE_FINGERPRINT = $DeviceFingerprint
|
||
}
|
||
|
||
& npx -y $CliPackage @authArgs
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Err 'MCP auth failed.'
|
||
Write-Muted "Retry manually: npx -y $CliPackage $($authArgs -join ' ')"
|
||
exit 1
|
||
}
|
||
Write-Ok 'MCP server registered'
|
||
}
|
||
|
||
# ── 4. Summary ───────────────────────────────────────────────────────────
|
||
Write-Step '✨ Installation complete'
|
||
Write-Host ''
|
||
Write-Host ' Next steps' -ForegroundColor White
|
||
Write-Muted '1. Restart your agent (Claude Code / Cursor / etc.)'
|
||
Write-Muted '2. Ask it something that needs the internet:'
|
||
Write-Host ' "What has Musk been tweeting about lately?"' -ForegroundColor Cyan
|
||
Write-Host ''
|
||
Write-Host ' Docs https://agentkey.app/docs' -ForegroundColor White
|
||
Write-Host ' Uninstall irm https://agentkey.app/uninstall.ps1 | iex' -ForegroundColor White
|
||
Write-Host ''
|