Skip to content

Commit 6fc174b

Browse files
ADO Lens 0.8.0
Fewer tokens in every Claude Code session: about 1.6k instead of 5.5–7k. - Tool groups loaded on demand: a session starts with the core (`ado_status`, `ado_settings`, `ado_upgrade`, `ado_work_item`, `ado_query`, `ado_search`, `ado_trace` and the new `ado_tools`), and Claude loads a group with `ado_tools enable=write` (or `"write;boards"`) when the conversation needs it; the client is told about the new tools (MCP `tools/list_changed`). Groups: `write`, `boards`, `wiki`, `code`, `tests`, `recipes`, `pr`, `pipelines`. The mode says which groups may be loaded. Skills and prompts load the groups they use. - `adolens config --tool-loading auto|demand|all` (or *Tools at start* in the settings menu). `auto`, the default: on demand in Claude Code; every group of the mode at start in other clients (Claude Desktop), which may not take new tools during a conversation. `demand`: on demand everywhere. `all`: everything at start. - Tool sets renamed to groups: `work` → `write`, `trace` → `code`, `test` → `tests` (old names in the settings still work). - Shorter descriptions of `ado_update`, `ado_create` and `ado_pr_update`. - Pull request tags (labels): `ado_pr_update action=tag tags="R349; -old"`, tags when creating a PR, and tags shown in PR lists, details and merged PRs. - Product Owner mode: no pull request or pipeline tools and no developer prompts (`dev_*`); they stay in the dev and full modes.
1 parent f3c95e1 commit 6fc174b

12 files changed

Lines changed: 109 additions & 45 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
# Changelog
22

3-
## 0.8.0 — unreleased
3+
## 0.8.0 — 2026-10-18
44

5-
Fewer tokens in every session: about 1.3k instead of 5.5–7k.
5+
Fewer tokens in every Claude Code session: about 1.6k instead of 5.5–7k.
66

77
- Tool groups loaded on demand: a session starts with the core (`ado_status`, `ado_settings`, `ado_upgrade`,
88
`ado_work_item`, `ado_query`, `ado_search`, `ado_trace` and the new `ado_tools`), and Claude loads a group with
99
`ado_tools enable=write` (or `"write;boards"`) when the conversation needs it; the client is told about the new tools
1010
(MCP `tools/list_changed`). Groups: `write`, `boards`, `wiki`, `code`, `tests`, `recipes`, `pr`, `pipelines`. The mode
1111
says which groups may be loaded. Skills and prompts load the groups they use.
12-
- `adolens config --load-all-tools true` (or *Tools at start* in the settings menu): every group of the mode at start,
13-
for a client that does not take new tools during a conversation.
12+
- `adolens config --tool-loading auto|demand|all` (or *Tools at start* in the settings menu). `auto`, the default: on
13+
demand in Claude Code; every group of the mode at start in other clients (Claude Desktop), which may not take new
14+
tools during a conversation. `demand`: on demand everywhere. `all`: everything at start.
1415
- Tool sets renamed to groups: `work` → `write`, `trace` → `code`, `test` → `tests` (old names in the settings still work).
1516
- Shorter descriptions of `ado_update`, `ado_create` and `ado_pr_update`.
1617
- Pull request tags (labels): `ado_pr_update action=tag tags="R349; -old"`, tags when creating a PR, and tags shown in

‎README.md‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ adolens config --connection contoso --project Demo --team "Demo Team"
134134
adolens config --default-connection fabrikam
135135
adolens config --mode dev # po | dev | full
136136
adolens config --add-toolset pipelines # one more tool group on top of the mode
137-
adolens config --load-all-tools true # every group of the mode at start (more tokens)
137+
adolens config --tool-loading auto # auto | demand | all: when tool groups are loaded
138138
adolens config --confirm-above 3 # updates of more items are previewed first (0: always)
139139
adolens config --usage-log false
140140
adolens config --show-tokens true # end every answer with [~N tokens]
@@ -171,7 +171,7 @@ Settings live in `%LOCALAPPDATA%\ado-lens\adolens.json` (no secrets) — they ca
171171

172172
### Modes and tool groups
173173

174-
Every tool definition costs tokens in every session, so a session starts with a small core (about 1.3k tokens) and
174+
Every tool definition costs tokens in every session, so a session starts with a small core (about 1.5k tokens) and
175175
Claude loads a group with `ado_tools enable=<group>` when the conversation needs it — a question about the sprint
176176
loads `boards`, a new story loads `write`. The skills and prompts load what they use. A mode only chooses which groups
177177
may be loaded; it is never a permission (the PAT decides).
@@ -188,9 +188,13 @@ may be loaded; it is never a permission (the PAT decides).
188188
| `pr` | `ado_pr`, `ado_pr_update` | – | ✓ | ✓ |
189189
| `pipelines` | `ado_pipeline`, `ado_pipeline_run` | – | ✓ | ✓ |
190190

191-
`adolens config --add-toolset <group>` allows one more group on top of the mode. A client that does not take new tools
192-
during a conversation can start with every group of the mode: `adolens config --load-all-tools true` (or *Tools at
193-
start* in the settings menu) — about 6k tokens per session instead of 1.3k.
191+
`adolens config --add-toolset <group>` allows one more group on top of the mode. When groups are loaded is
192+
`adolens config --tool-loading` (or *Tools at start* in the settings menu):
193+
194+
- `auto` (default): on demand in Claude Code, which takes new tools during a conversation; every group of the mode
195+
at start in other clients, Claude Desktop among them (about 6–7k tokens), so nothing is missing there;
196+
- `demand`: on demand in every client (about 1.3k tokens at start) — for a client known to take new tools;
197+
- `all`: every group of the mode at start everywhere.
194198

195199
## Tool map
196200

‎README.pl.md‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ adolens config --connection contoso --project Demo --team "Demo Team"
137137
adolens config --default-connection fabrikam
138138
adolens config --mode dev # po | dev | full
139139
adolens config --add-toolset pipelines # dodatkowa grupa narzędzi ponad tryb
140-
adolens config --load-all-tools true # wszystkie grupy trybu od startu (więcej tokenów)
140+
adolens config --tool-loading auto # auto | demand | all: kiedy ładować grupy narzędzi
141141
adolens config --confirm-above 3 # zmiany większej liczby elementów najpierw jako podgląd (0: zawsze)
142142
adolens config --usage-log false
143143
adolens config --show-tokens true # każda odpowiedź kończy się [~N tokens]
@@ -172,7 +172,7 @@ Ustawienia siedzą w `%LOCALAPPDATA%\ado-lens\adolens.json` (bez sekretów) —
172172

173173
### Tryby i grupy narzędzi
174174

175-
Każda definicja narzędzia kosztuje tokeny w każdej sesji, więc sesja startuje z małym rdzeniem (ok. 1,3 tys. tokenów),
175+
Każda definicja narzędzia kosztuje tokeny w każdej sesji, więc sesja startuje z małym rdzeniem (ok. 1,5 tys. tokenów),
176176
a Claude dociąga grupę przez `ado_tools enable=<grupa>`, gdy rozmowa jej potrzebuje — pytanie o sprint ładuje `boards`,
177177
nowa historyjka `write`. Skille i prompty same ładują to, czego używają. Tryb wybiera tylko, które grupy wolno załadować;
178178
nigdy nie jest uprawnieniem (o tym decyduje PAT).
@@ -189,9 +189,13 @@ nigdy nie jest uprawnieniem (o tym decyduje PAT).
189189
| `pr` | `ado_pr`, `ado_pr_update` | – | ✓ | ✓ |
190190
| `pipelines` | `ado_pipeline`, `ado_pipeline_run` | – | ✓ | ✓ |
191191

192-
`adolens config --add-toolset <grupa>` dopuszcza jeszcze jedną grupę ponad tryb. Klient, który nie przyjmuje nowych
193-
narzędzi w trakcie rozmowy, może startować ze wszystkimi grupami trybu: `adolens config --load-all-tools true` (albo
194-
*Narzędzia na start* w menu ustawień) — ok. 6 tys. tokenów na sesję zamiast 1,3 tys.
192+
`adolens config --add-toolset <grupa>` dopuszcza jeszcze jedną grupę ponad tryb. Kiedy ładować grupy, ustawia
193+
`adolens config --tool-loading` (albo *Narzędzia na start* w menu ustawień):
194+
195+
- `auto` (domyślnie): na żądanie w Claude Code, który przyjmuje nowe narzędzia w trakcie rozmowy; w innych klientach,
196+
także w Claude Desktop, wszystkie grupy trybu od startu (ok. 6–7 tys. tokenów), żeby niczego tam nie brakowało;
197+
- `demand`: na żądanie w każdym kliencie (ok. 1,3 tys. tokenów na start) — dla klienta, o którym wiadomo, że przyjmuje nowe narzędzia;
198+
- `all`: wszystkie grupy trybu od startu wszędzie.
195199

196200
## Mapa narzędzi
197201

‎src/AdoLens/AdoLens.csproj‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<AssemblyTitle>ADO Lens for Azure DevOps</AssemblyTitle>
2424
<Description>Azure DevOps (work items, boards, wiki, Git and TFVC, pipelines) served to AI assistants over MCP</Description>
2525
<Copyright>Copyright © 2026 WillowDev</Copyright>
26-
<Version>0.8.0-beta.1</Version>
26+
<Version>0.8.0</Version>
2727
</PropertyGroup>
2828

2929
<ItemGroup>

‎src/AdoLens/Cli.cs‎

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ connections list the connections
2828
config show or change settings
2929
--connection <name> --project <name> --team <name> (defaults of a connection)
3030
--default-connection <name> --mode po|dev|full --usage-log true|false
31-
--show-tokens true|false --load-all-tools true|false --add-toolset <set> --remove-toolset <set> --confirm-above <n>
31+
--show-tokens true|false --tool-loading auto|demand|all --add-toolset <set> --remove-toolset <set> --confirm-above <n>
3232
--language pl|en|auto (settings console and installer texts)
3333
--download-dir <folder>|default (where save=true puts files; default Downloads\ADO Lens)
3434
--connection <c> --label-languages "en-US, pl"|all (languages review and label require)
@@ -323,9 +323,9 @@ public int Config()
323323
settings.ShowTokens = bool.TryParse(st, out var b) ? b : throw new ArgumentException("--show-tokens takes true or false");
324324
changed = true;
325325
}
326-
if (cl.Opt("load-all-tools") is { } la)
326+
if (cl.Opt("tool-loading") is { } tl)
327327
{
328-
settings.LoadAllTools = bool.TryParse(la, out var b) ? b : throw new ArgumentException("--load-all-tools takes true or false");
328+
settings.ToolLoading = Settings.NormalizeToolLoading(tl);
329329
changed = true;
330330
}
331331
if (cl.Opt("confirm-above") is { } ca)
@@ -342,7 +342,12 @@ public int Config()
342342
if (changed) settings.Save(SettingsPath);
343343
Console.WriteLine($"settings: {SettingsPath}{(changed ? " (saved)" : "")}");
344344
Console.WriteLine($"mode: {settings.Mode} (tool groups: {string.Join(", ", Toolsets.Active(settings).OrderBy(t => Array.IndexOf(Toolsets.All, t)))}; "
345-
+ (settings.LoadAllTools ? "all loaded at start)" : "core at start, the rest when Claude needs them)"));
345+
+ settings.ToolLoading switch
346+
{
347+
"all" => "all loaded at start)",
348+
"demand" => "core at start, the rest when Claude needs them)",
349+
_ => "auto: on demand in Claude Code, all at start in other clients)",
350+
});
346351
Console.WriteLine($"usage log: {(settings.UsageLog ? "on" : "off")} token footer: {(settings.ShowTokens ? "on" : "off")} preview updates of more than {settings.ConfirmBulkAbove} items default connection: {settings.DefaultConnection ?? "-"}");
347352
return Connections();
348353
}
@@ -749,8 +754,7 @@ async Task<int> SetupMenuAsync()
749754
Console.WriteLine(Ui.T(" 6. Language: Polish / English", " 6. Język: polski / angielski"));
750755
Console.WriteLine(Ui.T(" 7. Required label languages (checks of D365 changes)", " 7. Wymagane języki etykiet (kontrola zmian D365)"));
751756
Console.WriteLine(Ui.T($" 8. Download folder: {Downloads.BaseDir(settings)}", $" 8. Folder pobierania: {Downloads.BaseDir(settings)}"));
752-
Console.WriteLine(Ui.T($" 9. Tools at start: {(settings.LoadAllTools ? "all of the mode" : "core, the rest on demand")}",
753-
$" 9. Narzędzia na start: {(settings.LoadAllTools ? "wszystkie z trybu" : "podstawowe, reszta na żądanie")}"));
757+
Console.WriteLine(Ui.T($" 9. Tools at start: {settings.ToolLoading}", $" 9. Narzędzia na start: {settings.ToolLoading}"));
754758
var choice = Prompt(Ui.T("Choice (Enter = done): ", "Wybór (Enter = koniec): "));
755759
switch (choice?.Trim())
756760
{
@@ -791,12 +795,14 @@ async Task<int> SetupMenuAsync()
791795
Ui.Polish = settings.Language == "pl" || settings.Language is null && Ui.Polish;
792796
break;
793797
case "9":
794-
Console.WriteLine(Ui.T(" 1. Core only - Claude loads the other groups when the conversation needs them (fewer tokens)",
795-
" 1. Tylko podstawowe - Claude dociąga pozostałe grupy, gdy rozmowa ich wymaga (mniej tokenów)"));
796-
Console.WriteLine(Ui.T(" 2. All tools of the mode at start (for a client that does not take new tools during a conversation)",
797-
" 2. Wszystkie narzędzia trybu od startu (dla klienta, który nie przyjmuje nowych narzędzi w trakcie rozmowy)"));
798-
var t = Prompt(Ui.T($" choice [{(settings.LoadAllTools ? 2 : 1)}]: ", $" wybór [{(settings.LoadAllTools ? 2 : 1)}]: "));
799-
settings.LoadAllTools = t?.Trim() switch { "1" => false, "2" => true, _ => settings.LoadAllTools };
798+
Console.WriteLine(Ui.T(" 1. auto - on demand in Claude Code, all at start in other clients (Claude Desktop)",
799+
" 1. auto - na żądanie w Claude Code, wszystkie od startu w innych klientach (Claude Desktop)"));
800+
Console.WriteLine(Ui.T(" 2. demand - core at start, Claude loads the other groups when the conversation needs them (fewest tokens)",
801+
" 2. demand - podstawowe na start, Claude dociąga pozostałe grupy, gdy rozmowa ich wymaga (najmniej tokenów)"));
802+
Console.WriteLine(Ui.T(" 3. all - every tool of the mode at start", " 3. all - wszystkie narzędzia trybu od startu"));
803+
var cur = settings.ToolLoading switch { "demand" => "2", "all" => "3", _ => "1" };
804+
var t = Prompt(Ui.T($" choice [{cur}]: ", $" wybór [{cur}]: "));
805+
settings.ToolLoading = (string.IsNullOrWhiteSpace(t) ? cur : t.Trim()) switch { "2" => "demand", "3" => "all", _ => "auto" };
800806
settings.Save(SettingsPath);
801807
Console.WriteLine(Ui.T(" new Claude sessions use it", " obowiązuje w nowych sesjach Claude'a"));
802808
break;

‎src/AdoLens/CommandLine.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public sealed partial class CommandLine
2323
["pr"] = ["connection=", "project=", "repo=", "list=", "detail=", "merged=", "since=", "top="],
2424
["pipeline"] = ["connection=", "project=", "definition=", "branch=", "top=", "detail=", "artifacts", "artifact=", "part=", "save", "to=", "deployments", "stage="],
2525
["changes"] = ["connection=", "repo=", "file=", "detail="],
26-
["config"] = ["env=", "remove-env=", "connection=", "default-connection=", "project=", "team=", "mode=", "usage-log=", "add-toolset=", "remove-toolset=", "confirm-above=", "show-tokens=", "load-all-tools=", "language=", "label-languages=", "download-dir="],
26+
["config"] = ["env=", "remove-env=", "connection=", "default-connection=", "project=", "team=", "mode=", "usage-log=", "add-toolset=", "remove-toolset=", "confirm-above=", "show-tokens=", "tool-loading=", "language=", "label-languages=", "download-dir="],
2727
["item"] = ["connection=", "include=", "detail="],
2828
["query"] = ["connection=", "project=", "wiql=", "query=", "top="],
2929
["create"] = ["connection=", "project=", "type=", "title=", "description=", "criteria=", "assign=", "parent=", "iteration=", "area=", "tags=", "state=", "field=", "template="],

‎src/AdoLens/McpTools.cs‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ public static class McpTools
5656
ado_tools enable=<group> (several: "write;boards"). ado_tools without arguments lists the groups.
5757
""";
5858

59-
/// <summary>Instructions of the groups loaded at start (with groups, the core only) and how to load the others.</summary>
59+
/// <summary>
60+
/// Instructions and how to load groups. They are sent before the server knows the client, so only demand keeps them
61+
/// to the core lines (the others come with each loaded group); auto and all carry the lines of every group of the mode.
62+
/// </summary>
6063
public static string Instructions(Settings settings)
6164
{
62-
var loaded = Toolsets.Loaded(settings);
63-
return Intro + (settings.LoadAllTools ? "" : Groups) + string.Concat(Lines.Where(l => loaded.Contains(l.Key)).Select(l => l.Value));
65+
var groups = settings.ToolLoading == "demand" ? Toolsets.Loaded(settings) : Toolsets.Active(settings);
66+
return Intro + (settings.ToolLoading == "all" ? "" : Groups) + string.Concat(Lines.Where(l => groups.Contains(l.Key)).Select(l => l.Value));
6467
}
6568

6669
/// <summary>The writing and usage hints of a group, returned when the group is loaded during a conversation.</summary>

‎src/AdoLens/Program.cs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ async Task<int> RunServerAsync()
162162
})
163163
.WithStdioServerTransport()
164164
.WithActiveTools(lens.Settings)
165+
.WithRequestFilters(f => f.AddListToolsFilter(next => (context, ct) =>
166+
{
167+
ToolGroups.ForClient(lens.Settings, context.Server);
168+
return next(context, ct);
169+
}))
165170
.WithPrompts(Prompts.TypesFor(lens.Settings));
166171
await builder.Build().RunAsync();
167172
return 0;

‎src/AdoLens/Settings.cs‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,21 @@ public sealed partial class Settings
4949
/// <summary>Language of the settings console and installer texts: "pl", "en", or null for the language of Windows.</summary>
5050
public string? Language { get; set; }
5151

52+
public const string ToolLoadings = "auto, demand, all";
53+
5254
/// <summary>
53-
/// Start every session with all tool groups of the mode instead of the core only (for clients that do not take new
54-
/// tools during a conversation; costs more tokens per session).
55+
/// demand: a session starts with the core and Claude loads groups when it needs them; all: every group of the mode
56+
/// from the start (more tokens); auto: demand for clients known to take new tools during a conversation, all for others.
5557
/// </summary>
56-
public bool LoadAllTools { get; set; }
58+
public string ToolLoading { get; set; } = "auto";
59+
60+
public static string NormalizeToolLoading(string value) => value.Trim().ToLowerInvariant() switch
61+
{
62+
"auto" or "" => "auto",
63+
"demand" or "on-demand" or "core" => "demand",
64+
"all" => "all",
65+
_ => throw new ArgumentException($"unknown tool loading '{value}' (known: {ToolLoadings})"),
66+
};
5767

5868
/// <summary>Where files are saved (save=true); null: Downloads\ADO Lens.</summary>
5969
public string? DownloadDir { get; set; }

‎src/AdoLens/ToolGroups.cs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@ namespace AdoLens;
1010
/// </summary>
1111
public static class ToolGroups
1212
{
13+
/// <summary>Clients known to take new tools during a conversation (MCP tools/list_changed), by their clientInfo name.</summary>
14+
static readonly string[] TakeNewTools = ["claude-code"];
15+
16+
static int _clientChecked;
17+
18+
public static bool TakesNewTools(string? clientName) =>
19+
clientName is { } n && TakeNewTools.Any(c => n.Contains(c, StringComparison.OrdinalIgnoreCase));
20+
21+
/// <summary>
22+
/// ToolLoading=auto: before the first tool list, a client that is not known to take new tools gets every group of
23+
/// the mode, so nothing is missing there; the others keep the core and load groups on demand.
24+
/// </summary>
25+
public static void ForClient(Settings settings, McpServer server)
26+
{
27+
if (Interlocked.Exchange(ref _clientChecked, 1) == 1) return;
28+
if (settings.ToolLoading != "auto" || TakesNewTools(server.ClientInfo?.Name)) return;
29+
if (server.ServerOptions.ToolCollection is not { } tools) return;
30+
using (tools.DeferChangedEvents())
31+
foreach (var method in Toolsets.Methods(Toolsets.Active(settings)))
32+
tools.TryAdd(McpServerTool.Create(method, options: new McpServerToolCreateOptions { Services = server.Services }));
33+
}
34+
1335
public static string Load(Settings settings, McpServer server, string? enable)
1436
{
1537
var tools = server.ServerOptions.ToolCollection ?? throw new InvalidOperationException("this server has no tool collection");

0 commit comments

Comments
 (0)