From 4ab89a7763efd123f347b9b1d4497a1348966b05 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Feb 2026 08:35:43 +0000 Subject: [PATCH 1/2] fix: resolve client icons for names with server suffix (e.g. "Claude Code (mcpmux)") The client icon lookup used exact matching on the full client_name, so clients like Claude Code that register as "Claude Code (mcpmux)" did not get the Claude icon. Extract resolveKnownClientKey() with prefix-aware matching into a dedicated utility and cover with 22 tests. https://claude.ai/code/session_012WNYFCB7MMMdy6zCSMAUdQ --- .../src/features/clients/ClientsPage.tsx | 12 +- apps/desktop/src/lib/clientIcons.ts | 45 ++++++++ tests/ts/lib/clientIcons.test.ts | 103 ++++++++++++++++++ 3 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 apps/desktop/src/lib/clientIcons.ts create mode 100644 tests/ts/lib/clientIcons.test.ts diff --git a/apps/desktop/src/features/clients/ClientsPage.tsx b/apps/desktop/src/features/clients/ClientsPage.tsx index 33eaf143..30b22139 100644 --- a/apps/desktop/src/features/clients/ClientsPage.tsx +++ b/apps/desktop/src/features/clients/ClientsPage.tsx @@ -4,6 +4,7 @@ import cursorIcon from '@/assets/client-icons/cursor.svg'; import vscodeIcon from '@/assets/client-icons/vscode.png'; import claudeIcon from '@/assets/client-icons/claude.svg'; import windsurfIcon from '@/assets/client-icons/windsurf.svg'; +import { resolveKnownClientKey } from '@/lib/clientIcons'; import { Laptop, Loader2, @@ -79,21 +80,18 @@ const CONNECTION_MODES = [ }, ]; -// Bundled icons for well-known AI clients (matched by client_name) -const KNOWN_CLIENT_ICONS: Record = { +// Bundled icons for well-known AI clients (resolved via icon key) +const CLIENT_ICON_ASSETS: Record = { cursor: cursorIcon, - 'vs code': vscodeIcon, vscode: vscodeIcon, - 'visual studio code': vscodeIcon, - 'claude desktop': claudeIcon, claude: claudeIcon, windsurf: windsurfIcon, - codeium: windsurfIcon, }; // Client icon component — uses bundled icon for known clients, falls back to logo_uri, then emoji function ClientIcon({ logo_uri, client_name }: { logo_uri?: string | null; client_name: string }) { - const iconUrl = KNOWN_CLIENT_ICONS[client_name.toLowerCase()] || logo_uri; + const knownKey = resolveKnownClientKey(client_name); + const iconUrl = (knownKey && CLIENT_ICON_ASSETS[knownKey]) || logo_uri; if (iconUrl) { return ( { + describe('exact matches', () => { + it('should resolve "cursor" to cursor', () => { + expect(resolveKnownClientKey('cursor')).toBe('cursor'); + }); + + it('should resolve "Cursor" (case-insensitive) to cursor', () => { + expect(resolveKnownClientKey('Cursor')).toBe('cursor'); + }); + + it('should resolve "claude" to claude', () => { + expect(resolveKnownClientKey('claude')).toBe('claude'); + }); + + it('should resolve "Claude Desktop" to claude', () => { + expect(resolveKnownClientKey('Claude Desktop')).toBe('claude'); + }); + + it('should resolve "vs code" to vscode', () => { + expect(resolveKnownClientKey('vs code')).toBe('vscode'); + }); + + it('should resolve "vscode" to vscode', () => { + expect(resolveKnownClientKey('vscode')).toBe('vscode'); + }); + + it('should resolve "Visual Studio Code" to vscode', () => { + expect(resolveKnownClientKey('Visual Studio Code')).toBe('vscode'); + }); + + it('should resolve "windsurf" to windsurf', () => { + expect(resolveKnownClientKey('windsurf')).toBe('windsurf'); + }); + + it('should resolve "codeium" to windsurf', () => { + expect(resolveKnownClientKey('codeium')).toBe('windsurf'); + }); + }); + + describe('prefix matches with parenthesised suffix', () => { + it('should resolve "Claude Code (mcpmux)" to claude', () => { + expect(resolveKnownClientKey('Claude Code (mcpmux)')).toBe('claude'); + }); + + it('should resolve "Claude Desktop (some-server)" to claude', () => { + expect(resolveKnownClientKey('Claude Desktop (some-server)')).toBe('claude'); + }); + + it('should resolve "Cursor (my-project)" to cursor', () => { + expect(resolveKnownClientKey('Cursor (my-project)')).toBe('cursor'); + }); + + it('should resolve "VS Code (workspace)" to vscode', () => { + expect(resolveKnownClientKey('VS Code (workspace)')).toBe('vscode'); + }); + + it('should resolve "Windsurf (test)" to windsurf', () => { + expect(resolveKnownClientKey('Windsurf (test)')).toBe('windsurf'); + }); + }); + + describe('prefix matches with space-separated suffix', () => { + it('should resolve "Claude Code v2" to claude', () => { + expect(resolveKnownClientKey('Claude Code v2')).toBe('claude'); + }); + + it('should resolve "Cursor beta" to cursor', () => { + expect(resolveKnownClientKey('Cursor beta')).toBe('cursor'); + }); + }); + + describe('non-matching names', () => { + it('should return null for unknown client names', () => { + expect(resolveKnownClientKey('unknown-client')).toBeNull(); + }); + + it('should return null for empty string', () => { + expect(resolveKnownClientKey('')).toBeNull(); + }); + + it('should not match partial names without word boundary', () => { + // "claudeXYZ" should NOT match "claude" — no word boundary + expect(resolveKnownClientKey('claudeXYZ')).toBeNull(); + }); + + it('should not match "cursorify" (no word boundary)', () => { + expect(resolveKnownClientKey('cursorify')).toBeNull(); + }); + }); + + describe('whitespace handling', () => { + it('should trim leading/trailing whitespace', () => { + expect(resolveKnownClientKey(' Cursor ')).toBe('cursor'); + }); + + it('should handle whitespace with suffix', () => { + expect(resolveKnownClientKey(' Claude Code (mcpmux) ')).toBe('claude'); + }); + }); +}); From 250794bb085c1b7091c0b8bb83dbc24caad2e63d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Feb 2026 08:43:31 +0000 Subject: [PATCH 2/2] chore: update Cargo.lock version references https://claude.ai/code/session_012WNYFCB7MMMdy6zCSMAUdQ --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d970146b..35065644 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2548,7 +2548,7 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "mcpmux" -version = "0.0.7" +version = "0.0.10" dependencies = [ "anyhow", "async-trait", @@ -2585,7 +2585,7 @@ dependencies = [ [[package]] name = "mcpmux-core" -version = "0.0.7" +version = "0.0.10" dependencies = [ "anyhow", "async-trait", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "mcpmux-gateway" -version = "0.0.7" +version = "0.0.10" dependencies = [ "anyhow", "async-stream", @@ -2648,7 +2648,7 @@ dependencies = [ [[package]] name = "mcpmux-mcp" -version = "0.0.7" +version = "0.0.10" dependencies = [ "anyhow", "async-trait", @@ -2667,7 +2667,7 @@ dependencies = [ [[package]] name = "mcpmux-storage" -version = "0.0.7" +version = "0.0.10" dependencies = [ "anyhow", "async-trait",