Found by Copilot reviewing the v2.2.0 milestone merge (#1993), verified against the code. Follow-on to #1957 / #1961.
#1961 fixed the rendering half of the duplicate-tool-name problem: rows are now keyed by source position, so filtering no longer orphans a child during reconciliation. But everything downstream of the key still identifies a tool by tool.name, so a tools/list that repeats a name is still not fully inspectable:
clients/web/src/components/groups/ToolControls/ToolControls.tsx:170 — selected={tool.name === selectedName}, so both duplicates highlight at once.
:172 — onClick is guarded by if (tool.name !== selectedName), so once either copy is selected, clicking the other is a no-op.
clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx:165-166 — tools.find((t) => t.name === selectedToolName) always resolves to the first match, so the detail panel can never show the second copy even if selection did change.
Net: with duplicate-tool-names-http.json loaded, the second get_weather / echo renders in the sidebar (that part now works) but cannot be opened. Two tools with the same name and different schemas are indistinguishable in the UI.
Expected
Each rendered row is independently selectable, and the detail panel shows that row's tool.
Suggested shape
Thread the same stable per-row identity ToolControls already computes for key through selection state — selectedToolKey rather than selectedToolName — in ToolControls, ToolsScreen's ui state, and the tools.find(...) lookups (:166 and :174).
onCallTool must keep sending the protocol name (selectedTool.name, ToolsScreen.tsx:260) — the wire identity is genuinely the duplicated name, and only the UI identity needs to be unique.
Repro
test-servers/configs/duplicate-tool-names-http.json, default (legacy) era. Click the trailing get_weather row after selecting the leading one.
Found by Copilot reviewing the v2.2.0 milestone merge (#1993), verified against the code. Follow-on to #1957 / #1961.
#1961 fixed the rendering half of the duplicate-tool-name problem: rows are now keyed by source position, so filtering no longer orphans a child during reconciliation. But everything downstream of the key still identifies a tool by
tool.name, so atools/listthat repeats a name is still not fully inspectable:clients/web/src/components/groups/ToolControls/ToolControls.tsx:170—selected={tool.name === selectedName}, so both duplicates highlight at once.:172—onClickis guarded byif (tool.name !== selectedName), so once either copy is selected, clicking the other is a no-op.clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx:165-166—tools.find((t) => t.name === selectedToolName)always resolves to the first match, so the detail panel can never show the second copy even if selection did change.Net: with
duplicate-tool-names-http.jsonloaded, the secondget_weather/echorenders in the sidebar (that part now works) but cannot be opened. Two tools with the same name and different schemas are indistinguishable in the UI.Expected
Each rendered row is independently selectable, and the detail panel shows that row's tool.
Suggested shape
Thread the same stable per-row identity
ToolControlsalready computes forkeythrough selection state —selectedToolKeyrather thanselectedToolName— inToolControls,ToolsScreen'suistate, and thetools.find(...)lookups (:166and:174).onCallToolmust keep sending the protocol name (selectedTool.name,ToolsScreen.tsx:260) — the wire identity is genuinely the duplicated name, and only the UI identity needs to be unique.Repro
test-servers/configs/duplicate-tool-names-http.json, default (legacy) era. Click the trailingget_weatherrow after selecting the leading one.