From 3b995f6658402bd1e18c821ccca4758f59fb5cb5 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Mon, 17 Aug 2026 00:23:26 -0400 Subject: [PATCH] docs(spec): describe tool selection by row key, not name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `v2_ux_interfaces.md` and `v2_storage.md` still prescribed `selectedName` / `selectedToolName` and `tools.find(t => t.name === …)` — the exact shape that made a duplicated tool name uninspectable. Point them at `toolRowKey` / `findToolByRowKey` and note the split between UI identity (the row key, carried by `onSelectTool`) and wire identity (the protocol name, carried by `onCallTool`). Follow-up to #2001 review feedback. Signed-off-by: cliffhall --- specification/v2_storage.md | 4 +++- specification/v2_ux_interfaces.md | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/specification/v2_storage.md b/specification/v2_storage.md index 33e8687a..3ff99e71 100644 --- a/specification/v2_storage.md +++ b/specification/v2_storage.md @@ -304,7 +304,9 @@ interface LogsDisplayActions { ```typescript interface ExecutionFormState { // Tools - selectedToolName: string | null; + // The selected row's `toolRowKey` (`index:name`), not the tool's name — a + // `tools/list` may repeat a name (#2001). + selectedToolKey: string | null; toolFormValues: Record; lastToolResult: ToolResult | null; diff --git a/specification/v2_ux_interfaces.md b/specification/v2_ux_interfaces.md index f1e3a158..c97c03e8 100644 --- a/specification/v2_ux_interfaces.md +++ b/specification/v2_ux_interfaces.md @@ -646,7 +646,8 @@ will close out as part of that work. - **Purpose**: Searchable tool list sidebar with list-changed indicator. - **Current props**: `tools: ToolListItemProps[]`, `listChanged`, `onRefreshList`, `onSelectTool`. - **MCP schema touch points**: `Tool`, `ListToolsResult`, `ToolListChangedNotification`. -- **Target props**: `tools: Tool[]`, `selectedName?`, `listChanged`, `onRefreshList`, `onSelectTool`. +- **Target props**: `tools: Tool[]`, `selectedKey?`, `listChanged`, `onRefreshList`, `onSelectTool`. +- **Row identity**: a `tools/list` may repeat a name, so rows are keyed — for React reconciliation *and* for selection — by `toolRowKey(name, sourceIndex)` (`utils/toolUtils.ts`), not by `tool.name`. `selectedKey` and `onSelectTool` both carry that key (#1957/#2001). - **Callbacks → core hook**: `useManagedTools`. - **Internal refactors**: Consume schema `Tool[]` directly. @@ -716,14 +717,14 @@ themselves — every callback passed down must originate in a core hook. - **MCP schema touch points**: `Tool`, `ListToolsResult`, `CallToolRequest.params`, `CallToolResult`, `ToolListChangedNotification`. - **Target props**: - `tools: Tool[]` — raw MCP `Tool[]` from `ListToolsResult.tools`. - - `selectedToolName?: string` — selection is screen-local; detail derived from `tools`. + - `selectedToolKey?: string` — selection is screen-local; detail derived from `tools`. This is the row's `toolRowKey`, not its name: duplicate names would otherwise select every copy at once and resolve only the first (#2001). - `callState?: { status: 'idle' | 'pending' | 'ok' | 'error'; request?: CallToolRequest['params']; result?: CallToolResult; error?: string }`. - `listChanged: boolean` — driven by `notifications/tools/list_changed`. - - `onRefreshList: () => void`, `onSelectTool: (name: string) => void`, `onCallTool: (name: string, args: Record) => void`. + - `onRefreshList: () => void`, `onSelectTool: (key: string) => void`, `onCallTool: (name: string, args: Record) => void` — note `onSelectTool` carries the row key (UI identity) while `onCallTool` carries the protocol name (wire identity). - **Callbacks → core hook**: `useManagedTools` provides `tools`, `listChanged`, `refreshTools`, `callTool`, `lastCallResult`; backed by `useInspectorClient` for the underlying `client.request`. - **Internal refactors**: - Stop flattening `Tool` into `ToolListItemProps` at the screen boundary — pass `Tool` down and let `ToolListItem` project fields. - - Derive the selected tool from `tools.find(t => t.name === selectedToolName)` instead of receiving a pre-built `ToolDetailPanelProps`. + - Derive the selected tool from `findToolByRowKey(tools, selectedToolKey)` instead of receiving a pre-built `ToolDetailPanelProps`. - Replace the separate `result` prop with a unified `callState` so pending/error UI can be rendered by `ToolResultPanel`. ### PromptsScreen