feat(mcp): search + keyboard navigation for installed MCP servers#2639
feat(mcp): search + keyboard navigation for installed MCP servers#2639aashir-athar wants to merge 2 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a controlled McpServerSearch input and wires it into McpServersTab; InstalledServerList gains an optional ChangesMCP Server Search and Filtering
Sequence DiagramsequenceDiagram
participant User
participant McpServerSearch
participant McpServersTab
participant InstalledServerList
User->>McpServerSearch: type in search field
McpServerSearch->>McpServersTab: onChange(filterText)
McpServersTab->>McpServersTab: setState(searchFilter)
McpServersTab->>InstalledServerList: pass filter prop
InstalledServerList->>InstalledServerList: filter servers by display_name/qualified_name/description
InstalledServerList->>User: render filtered list + match count
User->>InstalledServerList: press ArrowUp/ArrowDown
InstalledServerList->>User: move focus between visible servers
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/components/channels/mcp/InstalledServerList.tsx`:
- Around line 143-146: InstalledServerList renders a status dot using raw enum
values in title={status}; update the component (InstalledServerList) to use the
i18n hook (useT from app/src/lib/i18n/I18nContext) and supply a localized label
instead of the raw enum. Add or reuse a mapping (e.g., STATUS_LABELS or compute
with t(`status.${status}`)) to translate each status value, and pass the
translated string to the span title and any other user-facing status text while
keeping STATUS_DOT for CSS classes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 49377314-0775-4bde-9897-403dfd58363e
📒 Files selected for processing (19)
app/src/components/channels/mcp/InstalledServerList.test.tsxapp/src/components/channels/mcp/InstalledServerList.tsxapp/src/components/channels/mcp/McpServerSearch.test.tsxapp/src/components/channels/mcp/McpServerSearch.tsxapp/src/components/channels/mcp/McpServersTab.tsxapp/src/lib/i18n/chunks/ar-1.tsapp/src/lib/i18n/chunks/bn-1.tsapp/src/lib/i18n/chunks/de-1.tsapp/src/lib/i18n/chunks/en-1.tsapp/src/lib/i18n/chunks/es-1.tsapp/src/lib/i18n/chunks/fr-1.tsapp/src/lib/i18n/chunks/hi-1.tsapp/src/lib/i18n/chunks/id-1.tsapp/src/lib/i18n/chunks/it-1.tsapp/src/lib/i18n/chunks/ko-1.tsapp/src/lib/i18n/chunks/pt-1.tsapp/src/lib/i18n/chunks/ru-1.tsapp/src/lib/i18n/chunks/zh-CN-1.tsapp/src/lib/i18n/en.ts
…prettier on PR files
Summary
Channels → ChannelConfigPanel → McpServersTab. As users install more MCP servers, scanning the left-pane list becomes a real chore — this PR adds a controlled search input, live filtering, an accessible result counter, and arrow-key navigation across the list.McpServerSearch.tsx: controlled<input type="search">wrapped in arole="search"landmark, with a clear button that appears when the value is non-empty. Intentionally no global keyboard shortcut to avoid colliding with the app-wideCommandProviderinApp.tsx.InstalledServerList.tsx: optionalfilterprop case-insensitively matches againstdisplay_name,qualified_name, anddescription. New "X of Y servers" indicator announced viarole="status" aria-live="polite". New "No servers match "{query}"" empty state. ArrowUp/ArrowDown move focus across the visible server buttons (clamped at the edges; only the filtered set is traversed).mcp.installed.search.*namespace, mirrored across all 13 locale chunks (English values used as the untranslated placeholder per the repo's standard pattern; counted in each locale's "untranslated" total exactly like the existing keys).McpServerSearch, 11 for the new behaviour inInstalledServerList).Problem
The MCP feature surface ships today via
pages/Channels.tsx→ChannelConfigPanel.tsx→McpServersTab.tsx. The left pane is a flat scrolling list of installed servers (InstalledServerList). With three or four installs it's fine. The moment a user installs the eighth or fifteenth — which is the actual usage shape with the Smithery catalog — finding a specific server requires either scrolling or page-search via the browser/CEF shortcut.There's also no keyboard-only path for moving through the list. Each server is a
<button>so Tab moves between them, but power users with even a small inventory want ArrowUp/ArrowDown.These are the two paper cuts this PR addresses. They're contained, complementary, and they scale with the platform's growth — every Smithery server added makes them more valuable.
Solution
New:
McpServerSearch.tsxControlled component with two props (
value,onChange). Renders:<div role="search" aria-label={t('mcp.installed.search.landmarkAria')}>landmark so assistive tech can jump straight to it.<input type="search">witharia-labeland placeholder, both translated.<button>that only appears whenvalue.length > 0, witharia-labeland a decorative<svg aria-hidden="true">X icon.Intentional non-features:
CommandProviderinApp.tsx; binding another global shortcut here would silently override or fight with it. Users click or Tab into the field.Extended:
InstalledServerList.tsxNew optional
filter?: stringprop. When set, the list:display_name + qualified_name + descriptionand matches case-insensitively (substring).filteris non-empty: a<p role="status" aria-live="polite">showing"{shown} of {total} servers"so screen readers announce the new total as the user types.servers.length === 0regardless of filter.<ul>of server buttons.Each server button gets a stable
data-server-id={server.server_id}attribute and anonKeyDownhandler:listRef.current.querySelectorAll('button[data-server-id]'), callfocus(), andpreventDefault. Clamps at the edges (no wrap, simpler mental model; matches WAI-ARIA listbox guidance for explicit single-selection lists).fireEvent.keyDownreturnstruefor an unrelated key.Wiring in
McpServersTab.tsxA new
useState<string>('')in the tab owns the filter. The<McpServerSearch>renders directly above<InstalledServerList>in the left pane, but only whenservers.length > 0— there's nothing to search when nothing is installed, and the search box would visually compete with the existing "Browse catalog" empty-state CTA.The filter is intentionally not persisted — it's a transient scan helper, not a saved view. Reloading the tab or revisiting the page returns to an unfiltered list.
i18n
6 new keys under
mcp.installed.search.*added toapp/src/lib/i18n/en.tsAND toapp/src/lib/i18n/chunks/en-1.ts(the runtime chunk that holds themcp.installed.*namespace). Per the project's parity rule enforced byscripts/i18n-coverage.ts, the same six keys are also added to all 12 non-English locale chunks (ar-1.ts…zh-CN-1.ts) with English values as untranslated placeholders. They show up in each locale's "untranslated" count exactly like every other not-yet-translated key.Verified locally:
Submission Checklist
McpServerSearch(landmark, input attributes, clear button visibility / firing, controlled value, decorative-iconaria-hidden). 11 cover the new behaviour onInstalledServerList(filter by name / qualified_name / description / undefined-description; whitespace trimming; no-match state; "X of Y" with correctrole="status"+aria-live="polite"attributes; count hidden when filter empty or whitespace-only; original empty state precedence; ArrowDown/Up focus moves; clamping at edges; unrelated keys not intercepted; arrow nav restricted to visible items).InstalledServerList.tsxand every new line inMcpServerSearch.tsxis exercised by the new tests. Local Vitest: 99/99 passing, including all pre-existing MCP component tests (no regression).## Related— N/A: enhancement to existing MCP feature surface.Closes #NNNin## Related— no specific issue; this is an organic UX improvement on the MCP surface.Impact
McpServersTabis only rendered inChannelConfigPanelwhich only shows on desktop. No iOS / web impact.useMemoguards the filtered list so the filter only re-runs whenserversortrimmedFilterchange.filteris an optional prop with default''. Any existing caller ofInstalledServerListthat doesn't pass it gets exactly the previous behaviour byte-for-byte (verified via the existing test suite passing unchanged).Related
mcp.installed.search.*keys across the 12 non-English locales (they currently fall back to the English values; flagged in each locale'suntranslatedcount).git_*). Needs the tool inventory pre-loaded across all connected servers; out of scope for this PR (would change the data model).AI Authored PR Metadata
Linear Issue
Commit & Branch
feat/mcp-servers-search13d70bda(will be referenced from this PR)Validation Run
All four key gates passed locally:
pnpm --filter openhuman-app compile— clean (tsc --noEmit, no output = success).pnpm --filter openhuman-app lint— clean (exit 0).pnpm vitest run src/components/channels/mcp/— 99/99 passing including the 18 new tests and the pre-existingSkills.mcp-coming-soon.test.tsx(the latter confirms no regression to the intentionally-pinned Skills-page placeholder).pnpm i18n:check— exit 0, every locale at1:1197/1197parity, 0 missing keys, 0 extra keys, 0 drift.Validation Blocked
command:pnpm --filter openhuman-app format:check(which chainscargo fmt --checkvia the app'sformat:checkscript) and the husky pre-push hook (which runspnpm format→cargo fmt).error:'cargo' is not recognized as an internal or external command, operable program or batch file.— no Rust toolchain installed on the dev machine.impact:Usedgit push --no-verifyper CLAUDE.md's allowance for unrelated pre-existing breakage. This PR touches zero Rust files, socargo fmt --checkis not actually applicable to this change set — CI's Linux runner will run it as a no-op for the changed files. Prettier on the changed TS files runs in CI on a clean Linux checkout where line endings are LF; verified locally that the new files were written with LF.Behavior Changes
Parity Contract
filterprop defaults to''. When omitted (every existing call site),InstalledServerListrenders identically to the previous version — verified by all pre-existing tests still passing unchanged.'disconnected'for missing status entries is unchanged.Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Localization