diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2910f4d..72b2b57 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.5.1 — 2026-10-15
+
+- An icon: the program, the installer, Settings → Apps, the Start menu and the Claude Desktop extension.
+- Start menu folder "ADO Lens": settings (`adolens setup`), status, skills for Claude Desktop, uninstall.
+- The installer puts the program folder on the user's PATH: `adolens` works in any new terminal.
+- Uninstall asks whether to remove the stored PATs and the settings and cache too (default: keep them); it also takes
+ the program off the PATH. `adolens logout --all` removes every stored PAT.
+
## 0.5.0 — 2026-10-15
Tests, templates, and two more ways to install.
diff --git a/README.md b/README.md
index c6bb1cd..9a8fb44 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+
+
# ADO Lens for Azure DevOps — an MCP server for Claude
[](https://github.com/willowdev-code/ado-lens/actions/workflows/ci.yml)
@@ -47,8 +49,12 @@ The install package is not part of the repository (`dist\` is excluded by `.giti
administrator rights. It asks for the Azure DevOps link and the PAT (checked before anything is installed), who uses
it (Product Owner / developer / everything), installs the skills and registers ADO Lens in Claude; an update skips the
questions. Until the program is signed, Windows SmartScreen may show "Windows protected your PC" for a file downloaded
-in a browser: *More info → Run anyway* (or use the one-line install above, which avoids it). Uninstall it from
-*Settings → Apps*.
+in a browser: *More info → Run anyway* (or use the one-line install above, which avoids it).
+
+After installing, the Start menu has an **ADO Lens** folder: *settings* (mode, environments, another connection —
+`adolens setup` in a console), *status* (is everything working) and the skills for Claude Desktop; `adolens` works in
+any new terminal. Uninstall from *Settings → Apps → ADO Lens* (or the Start menu): it removes the program, its Claude
+registrations and skills, and asks whether to remove the stored PATs and settings too (by default they stay).
**Claude Desktop only (extension).** `ado-lens-X.Y.Z.mcpb` from **Releases**: open it with Claude Desktop (or drag it
into *Settings → Extensions*) and fill in the project link and the PAT, which Claude Desktop keeps in the system's secure
@@ -103,7 +109,7 @@ mentions it once per session; say "update ADO Lens" and `ado_upgrade` downloads
installer in its own window. From a terminal: `adolens upgrade` shows what is new, `adolens upgrade --install` installs
it (`--pre` includes test releases). Settings, PATs and metadata are kept; Claude may stay open.
-Uninstall: `uninstall.ps1` in the installation folder (removes the Claude registrations and skills of this installation,
+Uninstall (ZIP and one-line installs): `uninstall.ps1` in the installation folder (removes the Claude registrations and skills of this installation,
asks about the stored PATs and the local data).
### The PAT
diff --git a/README.pl.md b/README.pl.md
index 2f7137a..e00ef9e 100644
--- a/README.pl.md
+++ b/README.pl.md
@@ -1,3 +1,5 @@
+
+
# ADO Lens for Azure DevOps — serwer MCP dla Claude'a
[](https://github.com/willowdev-code/ado-lens/actions/workflows/ci.yml)
@@ -48,7 +50,11 @@ administratora. Pyta o link do Azure DevOps i PAT (sprawdzane, zanim cokolwiek s
Owner / programista / wszystko), instaluje skille i rejestruje ADO Lens w Claudzie; aktualizacja pomija pytania. Dopóki
program nie jest podpisany, Windows SmartScreen może przy pliku pobranym przeglądarką pokazać „System Windows ochronił
ten komputer”: *Więcej informacji → Uruchom mimo to* (albo użyj instalacji jedną linijką powyżej, która tego unika).
-Odinstalowanie: *Ustawienia → Aplikacje*.
+
+Po instalacji w menu Start jest folder **ADO Lens**: *ustawienia* (tryb, środowiska, kolejne połączenie — `adolens setup`
+w konsoli), *status* (czy wszystko działa) i skille dla Claude Desktop; `adolens` działa w każdym nowym terminalu.
+Odinstalowanie: *Ustawienia → Aplikacje → ADO Lens* (albo z menu Start) — usuwa program, jego rejestracje w Claudzie
+i skille, i pyta, czy usunąć też zapisane PAT-y i ustawienia (domyślnie zostają).
**Tylko Claude Desktop (rozszerzenie).** `ado-lens-X.Y.Z.mcpb` z **Releases**: otwórz go w Claude Desktop (albo
przeciągnij do *Settings → Extensions*) i wpisz link do projektu oraz PAT, który Claude Desktop trzyma w bezpiecznym
@@ -105,7 +111,7 @@ w sesji; wystarczy powiedzieć „zaktualizuj ADO Lens”, a `ado_upgrade` pobie
instalator w osobnym oknie. Z terminala: `adolens upgrade` pokazuje, co nowego, `adolens upgrade --install` instaluje
(`--pre` uwzględnia wydania testowe). Ustawienia, PAT-y i metadane zostają; Claude może być otwarty.
-Deinstalacja: `uninstall.ps1` w folderze instalacji (usuwa rejestracje i skille tej instalacji w Claude, pyta o zapisane
+Deinstalacja (instalacja z ZIP-a i jedną linijką): `uninstall.ps1` w folderze instalacji (usuwa rejestracje i skille tej instalacji w Claude, pyta o zapisane
PAT-y i dane lokalne).
### PAT
diff --git a/pack.ps1 b/pack.ps1
index 9a5471f..7335e7b 100644
--- a/pack.ps1
+++ b/pack.ps1
@@ -92,6 +92,7 @@ if (-not $NoZip) {
[IO.File]::WriteAllText((Join-Path $mcpbStage 'manifest.json'), $manifest, (New-Object Text.UTF8Encoding $false))
Copy-Item (Join-Path $stage 'bin') $mcpbStage -Recurse
Copy-Item (Join-Path $stage 'LICENSE') $mcpbStage
+ Copy-Item (Join-Path $PSScriptRoot 'packaging\icon\icon.png') $mcpbStage
$mcpb = Join-Path $OutDir "ado-lens-$version.mcpb"
New-Zip $mcpbStage $mcpb $false
Write-Checksum $mcpb 'mcpb'
diff --git a/packaging/icon/adolens.ico b/packaging/icon/adolens.ico
new file mode 100644
index 0000000..37bf0eb
Binary files /dev/null and b/packaging/icon/adolens.ico differ
diff --git a/packaging/icon/icon.png b/packaging/icon/icon.png
new file mode 100644
index 0000000..c3ba3d6
Binary files /dev/null and b/packaging/icon/icon.png differ
diff --git a/packaging/icon/make-icon.ps1 b/packaging/icon/make-icon.ps1
new file mode 100644
index 0000000..610ae91
--- /dev/null
+++ b/packaging/icon/make-icon.ps1
@@ -0,0 +1,91 @@
+<#
+.SYNOPSIS
+ Draws the ADO Lens icon: a lens over three work item bars on an Azure blue tile.
+ Writes adolens.ico (16-256 px, PNG frames) and icon.png (512 px) next to this script.
+#>
+$ErrorActionPreference = 'Stop'
+Add-Type -AssemblyName System.Drawing
+
+function New-RoundedRect([float] $x, [float] $y, [float] $w, [float] $h, [float] $r) {
+ $p = New-Object System.Drawing.Drawing2D.GraphicsPath
+ $d = 2 * $r
+ $p.AddArc($x, $y, $d, $d, 180, 90)
+ $p.AddArc($x + $w - $d, $y, $d, $d, 270, 90)
+ $p.AddArc($x + $w - $d, $y + $h - $d, $d, $d, 0, 90)
+ $p.AddArc($x, $y + $h - $d, $d, $d, 90, 90)
+ $p.CloseFigure()
+ return $p
+}
+
+function New-IconBitmap([int] $size) {
+ $bmp = New-Object System.Drawing.Bitmap $size, $size, ([System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
+ $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
+ $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
+ $g.Clear([System.Drawing.Color]::Transparent)
+ $s = [float] $size
+
+ # Tile: Azure blue, lighter at the top.
+ $margin = [Math]::Max(0.5, $s * 0.03)
+ $tile = New-RoundedRect $margin $margin ($s - 2 * $margin) ($s - 2 * $margin) ($s * 0.2)
+ $grad = New-Object System.Drawing.Drawing2D.LinearGradientBrush (New-Object System.Drawing.PointF 0, 0), (New-Object System.Drawing.PointF 0, $s),
+ ([System.Drawing.Color]::FromArgb(255, 0, 137, 230)), ([System.Drawing.Color]::FromArgb(255, 0, 90, 170))
+ $g.FillPath($grad, $tile)
+
+ $white = [System.Drawing.Color]::White
+ # Lens: a ring, centred a little up and left, with a handle to the bottom right.
+ $cx = $s * 0.43; $cy = $s * 0.43; $r = $s * 0.24
+ $ring = [Math]::Max(1.5, $s * 0.075)
+ if ($size -ge 32) {
+ # Three work item bars inside the lens (left out at small sizes, where they would blur).
+ $bar = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(235, 255, 255, 255))
+ $bh = $s * 0.045
+ foreach ($i in 0..2) {
+ $w = $r * (1.05 - 0.25 * $i)
+ $y = $cy - $r * 0.42 + $i * $r * 0.42 - $bh / 2
+ $path = New-RoundedRect ($cx - $r * 0.55) $y $w $bh ($bh / 2)
+ $g.FillPath($bar, $path)
+ }
+ }
+ $pen = New-Object System.Drawing.Pen $white, $ring
+ $g.DrawEllipse($pen, $cx - $r, $cy - $r, 2 * $r, 2 * $r)
+ $handle = New-Object System.Drawing.Pen $white, ([Math]::Max(2, $s * 0.11))
+ $handle.StartCap = [System.Drawing.Drawing2D.LineCap]::Round
+ $handle.EndCap = [System.Drawing.Drawing2D.LineCap]::Round
+ $a = 0.7071 * ($r + $ring / 2)
+ $g.DrawLine($handle, $cx + $a, $cy + $a, $s * 0.76, $s * 0.76)
+ $g.Dispose()
+ return $bmp
+}
+
+$dir = $PSScriptRoot
+$sizes = 16, 20, 24, 32, 40, 48, 64, 128, 256
+$frames = foreach ($n in $sizes) {
+ $bmp = New-IconBitmap $n
+ $ms = New-Object System.IO.MemoryStream
+ $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
+ $bmp.Dispose()
+ , $ms.ToArray()
+}
+
+# ICO: header, one directory entry per size, then the PNG frames.
+$out = New-Object System.IO.MemoryStream
+$w = New-Object System.IO.BinaryWriter $out
+$w.Write([UInt16]0); $w.Write([UInt16]1); $w.Write([UInt16]$sizes.Count)
+$offset = 6 + 16 * $sizes.Count
+for ($i = 0; $i -lt $sizes.Count; $i++) {
+ $n = $sizes[$i]
+ $dim = if ($n -ge 256) { 0 } else { $n }
+ $w.Write([byte]$dim); $w.Write([byte]$dim); $w.Write([byte]0); $w.Write([byte]0)
+ $w.Write([UInt16]1); $w.Write([UInt16]32)
+ $w.Write([UInt32]$frames[$i].Length); $w.Write([UInt32]$offset)
+ $offset += $frames[$i].Length
+}
+foreach ($f in $frames) { $w.Write($f) }
+$w.Flush()
+[IO.File]::WriteAllBytes((Join-Path $dir 'adolens.ico'), $out.ToArray())
+
+$big = New-IconBitmap 512
+$big.Save((Join-Path $dir 'icon.png'), [System.Drawing.Imaging.ImageFormat]::Png)
+$big.Dispose()
+Write-Host "written adolens.ico and icon.png in $dir"
diff --git a/packaging/inno/ado-lens.iss b/packaging/inno/ado-lens.iss
index 3b0cb7a..e7039f8 100644
--- a/packaging/inno/ado-lens.iss
+++ b/packaging/inno/ado-lens.iss
@@ -26,7 +26,11 @@ AppSupportURL=https://github.com/willowdev-code/ado-lens/issues
DefaultDirName={localappdata}\Programs\ADO Lens
DisableDirPage=yes
DisableProgramGroupPage=yes
+DefaultGroupName=ADO Lens
DisableReadyPage=yes
+SetupIconFile={#SourcePath}..\icon\adolens.ico
+; The program folder goes on the user's PATH, so "adolens" works in any new terminal.
+ChangesEnvironment=yes
PrivilegesRequired=lowest
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
@@ -52,6 +56,19 @@ Source: "{#DesktopSkills}\*.zip"; DestDir: "{app}\skills-for-claude-desktop"; Fl
; Claude Code reads skills from %USERPROFILE%\.claude\skills (only when Claude Code is there); removed on uninstall.
Source: "{#Source}\skills\*"; DestDir: "{%USERPROFILE}\.claude\skills"; Flags: recursesubdirs ignoreversion; Check: HasClaudeCode
+[Icons]
+; Start menu: settings and status open in a console that stays open (adolens commands work there too).
+Name: "{group}\{cm:IconSettings}"; Filename: "{cmd}"; Parameters: "/k """"{app}\bin\adolens.exe"" setup"""; WorkingDir: "{app}"; IconFilename: "{app}\bin\adolens.exe"
+Name: "{group}\{cm:IconStatus}"; Filename: "{cmd}"; Parameters: "/k """"{app}\bin\adolens.exe"" status"""; WorkingDir: "{app}"; IconFilename: "{app}\bin\adolens.exe"
+Name: "{group}\{cm:IconSkills}"; Filename: "{app}\skills-for-claude-desktop"
+Name: "{group}\{cm:UninstallProgram,ADO Lens}"; Filename: "{uninstallexe}"; IconFilename: "{app}\bin\adolens.exe"
+
+[Registry]
+Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Check: NeedsAddPath(ExpandConstant('{app}\bin'))
+
+[UninstallDelete]
+Type: files; Name: "{app}\bin\*.old"
+
[UninstallRun]
Filename: "{app}\bin\adolens.exe"; Parameters: "unregister"; Flags: runhidden waituntilterminated; RunOnceId: "unregister"
@@ -60,6 +77,14 @@ Filename: "{app}\skills-for-claude-desktop"; Description: "{cm:OpenSkills}"; Fla
Filename: "https://github.com/willowdev-code/ado-lens#readme"; Description: "{cm:OpenReadme}"; Flags: postinstall shellexec skipifsilent unchecked
[CustomMessages]
+en.IconSettings=ADO Lens - settings
+en.IconStatus=ADO Lens - status
+en.IconSkills=ADO Lens - skills for Claude Desktop
+en.RemoveData=Also remove the stored personal access tokens (Windows Credential Manager) and the settings and cache of ADO Lens?%n%nChoose No to keep them for a later installation.
+pl.IconSettings=ADO Lens - ustawienia
+pl.IconStatus=ADO Lens - status
+pl.IconSkills=ADO Lens - skille dla Claude Desktop
+pl.RemoveData=Usunąć też zapisane tokeny dostępu (Menedżer poświadczeń Windows) oraz ustawienia i pamięć podręczną ADO Lens?%n%nWybierz Nie, aby zachować je na potrzeby ponownej instalacji.
en.ConnTitle=Azure DevOps
en.ConnSub=Where your work items are
en.ConnText=Paste a link to your Azure DevOps project from the browser (for example https://dev.azure.com/contoso/Sales), or the organization name.
@@ -119,6 +144,64 @@ var
function SetEnvironmentVariable(lpName: String; lpValue: String): Boolean;
external 'SetEnvironmentVariableW@kernel32.dll stdcall';
+{ Whether a folder is missing from the user's PATH (compared without case and trailing backslash). }
+function NeedsAddPath(Dir: String): Boolean;
+var
+ Path: String;
+begin
+ if not RegQueryStringValue(HKCU, 'Environment', 'Path', Path) then
+ begin
+ Result := True;
+ Exit;
+ end;
+ Result := Pos(';' + Lowercase(RemoveBackslashUnlessRoot(Dir)) + ';', ';' + Lowercase(Path) + ';') = 0;
+end;
+
+procedure RemoveFromPath(Dir: String);
+var
+ Path, Item, Kept: String;
+ P: Integer;
+begin
+ if not RegQueryStringValue(HKCU, 'Environment', 'Path', Path) then Exit;
+ Kept := '';
+ Path := Path + ';';
+ while Path <> '' do
+ begin
+ P := Pos(';', Path);
+ Item := Copy(Path, 1, P - 1);
+ Delete(Path, 1, P);
+ if (Item <> '') and (Lowercase(RemoveBackslashUnlessRoot(Item)) <> Lowercase(RemoveBackslashUnlessRoot(Dir))) then
+ begin
+ if Kept <> '' then Kept := Kept + ';';
+ Kept := Kept + Item;
+ end;
+ end;
+ RegWriteExpandStringValue(HKCU, 'Environment', 'Path', Kept);
+end;
+
+var
+ RemoveUserData: Boolean;
+
+{ Before the files go: ask whether to remove the stored PATs and the local data (default: keep them), and sign out
+ with the program while it is still there. After: take the program folder off the PATH and delete the data. }
+procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
+var
+ Code: Integer;
+begin
+ if CurUninstallStep = usUninstall then
+ begin
+ RemoveUserData := not UninstallSilent and
+ (MsgBox(CustomMessage('RemoveData'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES);
+ if RemoveUserData then
+ Exec(ExpandConstant('{app}\bin\adolens.exe'), 'logout --all', '', SW_HIDE, ewWaitUntilTerminated, Code);
+ end
+ else if CurUninstallStep = usPostUninstall then
+ begin
+ RemoveFromPath(ExpandConstant('{app}\bin'));
+ if RemoveUserData then DelTree(ExpandConstant('{localappdata}\ado-lens'), True, True, True);
+ end;
+end;
+
function HasClaudeCode: Boolean;
begin
Result := DirExists(ExpandConstant('{%USERPROFILE}\.claude'));
diff --git a/packaging/mcpb/manifest.json b/packaging/mcpb/manifest.json
index 5c53897..64930b9 100644
--- a/packaging/mcpb/manifest.json
+++ b/packaging/mcpb/manifest.json
@@ -17,6 +17,7 @@
"documentation": "https://github.com/willowdev-code/ado-lens#readme",
"support": "https://github.com/willowdev-code/ado-lens/issues",
"license": "MIT",
+ "icon": "icon.png",
"keywords": ["azure devops", "work items", "sprint", "wiki", "tfvc", "git", "pull requests"],
"server": {
"type": "binary",
diff --git a/src/AdoLens/AdoLens.csproj b/src/AdoLens/AdoLens.csproj
index f579b8d..d08b4fa 100644
--- a/src/AdoLens/AdoLens.csproj
+++ b/src/AdoLens/AdoLens.csproj
@@ -12,6 +12,8 @@
true
true
en
+
+ ..\..\packaging\icon\adolens.ico
@@ -21,7 +23,7 @@
ADO Lens for Azure DevOps
Azure DevOps (work items, boards, wiki, Git and TFVC, pipelines) served to AI assistants over MCP
Copyright © 2026 WillowDev
- 0.5.0
+ 0.5.1
diff --git a/src/AdoLens/Cli.cs b/src/AdoLens/Cli.cs
index e371266..8a819ab 100644
--- a/src/AdoLens/Cli.cs
+++ b/src/AdoLens/Cli.cs
@@ -22,7 +22,7 @@ public static void PrintUsage() => Console.WriteLine("""
login add or update a connection: organization, default project and PAT
--org --project --team --connection
--pat-stdin (read the PAT from standard input) --default (make it the default connection)
- logout remove the stored PAT of a connection --connection
+ logout remove the stored PAT of a connection --connection | --all
connections list the connections
config show or change settings
--connection --project --team (defaults of a connection)
@@ -184,6 +184,13 @@ public async Task LoginAsync()
public int Logout()
{
var settings = Settings.Load(SettingsPath);
+ if (cl.Flag("all"))
+ {
+ // Uninstall: every stored PAT of the connections in the settings.
+ foreach (var conn in settings.Connections.Keys)
+ Console.WriteLine(CredentialStore.Delete(conn) ? $"removed {CredentialStore.TargetFor(conn)}" : $"no stored PAT for '{conn}'");
+ return 0;
+ }
var name = cl.Opt("connection") ?? settings.DefaultConnection;
if (string.IsNullOrWhiteSpace(name)) { Console.WriteLine("no connection configured - nothing to remove"); return 0; }
Console.WriteLine(CredentialStore.Delete(name)
diff --git a/src/AdoLens/CommandLine.cs b/src/AdoLens/CommandLine.cs
index 1212b13..1c015f7 100644
--- a/src/AdoLens/CommandLine.cs
+++ b/src/AdoLens/CommandLine.cs
@@ -17,7 +17,7 @@ public sealed partial class CommandLine
["login"] = ["connection=", "org=", "project=", "team=", "pat-stdin", "pat-env=", "default"],
["tests"] = ["connection=", "team=", "detail="],
["test-create"] = ["connection=", "file=", "plan=", "suite="],
- ["logout"] = ["connection="],
+ ["logout"] = ["connection=", "all"],
["connections"] = [],
["trace"] = ["connection=", "detail="],
["pr"] = ["connection=", "project=", "repo=", "list=", "detail="],