Skip to content

Commit 9380dad

Browse files
committed
feat: redesign README, update screenshots, and improve E2E capture
- Rewrite README with compelling marketing copy and focused screenshots - Add new focused screenshots: client-detail, client-permissions, featureset-detail, server-expanded, space-switcher - Update existing screenshots with content-only captures (no sidebar) - Improve OAuth consent modal with space connection mode selector - Add server feature toggle command for E2E testing - Enhance screenshot capture E2E with CSS zoom/scale for focused crops - Add FUNDING.yml for GitHub sponsors Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent d9b6b19 commit 9380dad

22 files changed

Lines changed: 919 additions & 141 deletions

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: mcpmux
2+
ko_fi: mcpmux

README.md

Lines changed: 46 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,45 @@
77

88
**[Website](https://mcpmux.com)** · **[Download](https://mcpmux.com/download)** · **[Discover Servers](https://mcpmux.com)** · **[Features](https://mcpmux.com/features)**
99

10-
![McpMux Dashboard](docs/screenshots/dashboard.png)
10+
![McpMux Dashboard](docs/screenshots/space-switcher.png)
1111

1212
---
1313

1414
## The Problem
1515

16-
Every AI client has its own MCP config file. Same servers, same credentials — duplicated everywhere.
16+
You use Cursor, Claude Code, VS Code, and Windsurf. They all support MCP. But every client has its own config file — and none of them talk to each other.
1717

1818
```
1919
Cursor → config.json → github, slack, db + API keys
20-
Claude Desktop → config.json → github, slack, db + API keys (again)
21-
VS Code → settings.json → github, slack, db + API keys (again)
22-
Windsurf → config.json → github, slack, db + API keys (again)
20+
Claude Code → config.json → github, slack, db + API keys (copy-paste)
21+
VS Code → settings.json → github, slack, db + API keys (copy-paste)
22+
Windsurf → config.json → github, slack, db + API keys (copy-paste)
2323
```
2424

25-
Add a server? **Update every client.** Rotate an API key? **Update every client.** New project? **Reconfigure everything.**
25+
Add a server? **Edit four files.** Rotate an API key? **Edit four files.** New teammate, new machine, new project? **Start from scratch.**
2626

27-
And all those credentials? Sitting in **plain-text JSON files** on disk.
27+
And those API keys? Sitting in **plain-text JSON files** anyone can read.
2828

2929
## The Fix
3030

31-
McpMux is a desktop app that runs a local gateway. Configure your servers once, point all clients to one URL.
31+
McpMux is a desktop app that runs a local MCP gateway. You configure servers once — every AI client connects to the same URL.
3232

3333
```
3434
Cursor ─┐
35-
Claude Desktop ─┤──→ McpMux (localhost) ──→ all your MCP servers
35+
Claude Code ─┤──→ McpMux (localhost:45818) ──→ all your MCP servers
3636
VS Code ─┤ encrypted credentials
3737
Windsurf ─┘ one config, one place
3838
```
3939

40-
Add a server in McpMux and every client has it instantly. No files to edit.
40+
Add a server in McpMux and it appears in Cursor, Claude, VS Code, and Windsurf instantly. No files to edit. No credentials to copy.
4141

4242
---
4343

4444
## How It Works
4545

46-
**1.** Install servers from the built-in registry (or add manually)
46+
**1.** Install servers from the built-in registry or add your own
4747

48-
**2.** Paste one config into your AI clients:
48+
**2.** Paste one config into each AI client (the last config you'll ever need):
4949

5050
```json
5151
{
@@ -58,114 +58,77 @@ Add a server in McpMux and every client has it instantly. No files to edit.
5858
}
5959
```
6060

61-
**3.** Done. All tools from all servers are available in every client.
61+
**3.** Done. Every tool from every server is available in every client, right now.
6262

63-
McpMux routes calls to the right server, refreshes OAuth tokens automatically, and keeps credentials encrypted in your OS keychain. You don't think about it.
63+
McpMux routes calls to the right server, refreshes OAuth tokens automatically, and keeps credentials encrypted in your OS keychain — you never think about it again.
6464

6565
---
6666

6767
## Features
6868

69-
### Server Management
69+
### All Your Servers, One Place
7070

71-
Add, remove, and monitor all your MCP servers in one place. See connection status, auth type, and transport at a glance.
71+
No more duplicating server configs across Cursor, Claude, VS Code, and Windsurf. Install a server in McpMux and it's immediately available everywhere. Expand any server to inspect its tools, prompts, and resources. See live connection status. OAuth tokens refresh automatically in the background.
7272

73-
![My Servers](docs/screenshots/servers.png)
73+
![Server Management — Azure, AWS, Notion, Docker, Slack all connected](docs/screenshots/servers.png)
7474

75-
### Server Registry
75+
### 100+ Servers, One Click
7676

77-
Browse and one-click install from a curated registry of MCP servers. Filter by category, auth type, or hosting. Cached for offline use. You can also browse the full registry at [mcpmux.com](https://mcpmux.com).
77+
Stop hunting for MCP server repos and hand-writing transport configs. Browse a curated registry of 100+ servers — GitHub, Slack, PostgreSQL, Docker, Notion, AWS, Azure, and more. Click install, enter your credentials, and the server is live across every AI client you use. You can also browse the full registry at [mcpmux.com](https://mcpmux.com).
7878

79-
![Discover Servers](docs/screenshots/discover.png)
79+
![Discover — browse 100+ servers and install with one click](docs/screenshots/discover.png)
8080

81-
### Spaces
81+
### Workspaces That Keep Things Separate
8282

83-
Isolated workspaces with their own servers and credentials. Switch between "Work" and "Personal" in one click — your AI clients follow automatically.
83+
Create isolated Spaces — each with their own servers, credentials, and permissions. A "Work" space for company databases and internal APIs. A "Personal" space for side projects. Switch in one click from the sidebar and every connected AI client follows automatically. No more accidentally querying your personal database from a work project.
8484

85-
![Workspaces](docs/screenshots/spaces.png)
85+
![Workspaces — switch context instantly from the sidebar](docs/screenshots/space-switcher.png)
8686

87-
### Feature Sets
87+
### Control What Each Client Can Do
8888

89-
Control exactly which tools, prompts, and resources are available. Create custom permission bundles like "Read Only" or "Dev Tools" and assign them to clients.
89+
Not every AI client should have the same power. Create Feature Sets — permission bundles that control exactly which tools, prompts, and resources a client can access. Build a "Read Only" set for cautious workflows, a "React Development" set with just GitHub and Filesystem, or a "Full Stack Dev" set with everything. Assign them per-client so each tool only goes where you want it.
9090

91-
![Feature Sets](docs/screenshots/featuresets.png)
91+
![Feature Sets — granular per-server tool selection](docs/screenshots/featureset-detail.png)
9292

93-
### Connected Clients
93+
### See and Manage Every Connected Client
9494

95-
See every AI client connected to your gateway. Manage OAuth registrations and control which space each client can access.
95+
Cursor, VS Code, Windsurf, Claude Code — see every AI client connected to your gateway in real time. Click any client to manage its workspace, grant or revoke feature sets, and see exactly which tools it can access. New clients authenticate via OAuth with a one-click approval flow.
9696

97-
![Connected Clients](docs/screenshots/clients.png)
98-
99-
### Settings
100-
101-
Auto-updates, launch at startup, system tray, and appearance preferences.
102-
103-
![Settings](docs/screenshots/settings.png)
97+
![Client Management — per-client permissions and effective features](docs/screenshots/client-detail.png)
10498

10599
---
106100

107101
## Security
108102

109-
MCP defaults to plain-text config files with raw API keys. McpMux replaces that with:
103+
MCP defaults to plain-text config files with raw API keys. McpMux replaces that with defense in depth:
110104

111-
- **OS Keychain** — secrets in platform-native secure storage, not on disk
112-
- **AES-256-GCM** — field-level database encryption
113-
- **OAuth 2.1 + PKCE** — standard auth with automatic token refresh
105+
- **OS Keychain** — secrets in platform-native secure storage (DPAPI on Windows, Keychain on macOS, libsecret on Linux), never in plain-text files
106+
- **AES-256-GCM** — field-level database encryption for all sensitive data
107+
- **OAuth 2.1 + PKCE** — standard auth flow with automatic token refresh
114108
- **Local-only gateway** — binds to `127.0.0.1`, nothing exposed to the network
115-
- **Per-client access keys**granular permissions per AI client
116-
- **Sanitized logs** — tokens never appear in log files
117-
- **Memory zeroization** — secrets wiped from memory after use
109+
- **Per-client access keys**each AI client authenticates independently with granular permissions
110+
- **Sanitized logs** — tokens and secrets never appear in log output
111+
- **Memory zeroization** — secrets wiped from memory after use via `zeroize`
118112

119-
All MCP traffic stays on your machine. Cloud sync (optional) only covers config metadata — never credentials or payloads.
113+
All MCP traffic stays on your machine. Cloud sync (optional, coming soon) only covers config metadata — never credentials or payloads.
120114

121115
---
122116

123117
## Getting Started
124118

125-
**1. [Download McpMux](https://mcpmux.com/download)** — Windows (MSI), macOS (DMG), Linux (DEB/RPM/AppImage)
119+
**1. [Download McpMux](https://mcpmux.com/download)** — Windows, macOS, Linux
126120

127-
**2. Add servers** — Discover tab to browse the registry, or [explore servers on mcpmux.com](https://mcpmux.com) and install with one click
121+
**2. Add servers**use the Discover tab to browse 100+ servers, or [explore servers on mcpmux.com](https://mcpmux.com) and install with one click
128122

129-
**3. Paste config**Copy the snippet from the Dashboard into your AI clients
123+
**3. Paste the config**copy the snippet from the Dashboard into your AI clients
130124

131-
That's the last config file you'll need to touch.
125+
That's the last config file you'll ever touch.
132126

133-
---
134-
135-
## Install on Linux
136-
137-
**Quick install** (detects your distro automatically):
138-
```bash
139-
curl -fsSL https://install.mcpmux.com | bash
140-
```
141-
142-
<details>
143-
<summary>Other install methods</summary>
144-
145-
**Debian / Ubuntu** (APT repository for automatic updates):
146-
```bash
147-
curl -fsSL https://install.mcpmux.com/apt | sudo bash
148-
```
149-
150-
**Fedora / RHEL** (from GitHub Releases):
151-
```bash
152-
# Download the latest .rpm
153-
sudo dnf install https://github.com/mcpmux/mcp-mux/releases/latest/download/mcpmux-0.0.12-1.amd64.rpm
154-
```
155-
156-
**Arch Linux** (AUR):
157-
```bash
158-
yay -S mcpmux-bin
159-
```
160-
161-
**AppImage** (any distro):
162-
```bash
163-
curl -fsSL https://install.mcpmux.com | bash # auto-detects, falls back to AppImage
164-
```
165-
166-
Or download directly from [GitHub Releases](https://github.com/mcpmux/mcp-mux/releases/latest).
167-
168-
</details>
127+
> **Linux quick install:** `curl -fsSL https://install.mcpmux.com | bash`
128+
>
129+
> **macOS via Homebrew:** `brew install --cask mcpmux/tap/mcpmux`
130+
>
131+
> See [mcpmux.com/download](https://mcpmux.com/download) for all platforms and install methods.
169132
170133
---
171134

apps/desktop/src-tauri/src/commands/server_feature.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,56 @@ pub async fn get_server_feature(
129129

130130
Ok(feature.map(Into::into))
131131
}
132+
133+
/// Seed server features for E2E testing.
134+
///
135+
/// Accepts an array of feature definitions and upserts them into the database.
136+
/// This is intended for screenshot capture and E2E tests where servers aren't
137+
/// actually connected but we need realistic feature data in the UI.
138+
#[tauri::command]
139+
pub async fn seed_server_features(
140+
features: Vec<SeedFeatureInput>,
141+
state: State<'_, AppState>,
142+
) -> Result<Vec<String>, String> {
143+
let storage_features: Vec<ServerFeature> = features
144+
.into_iter()
145+
.map(|f| {
146+
let mut sf = match f.feature_type.as_str() {
147+
"tool" => ServerFeature::new_tool(&f.space_id, &f.server_id, &f.feature_name),
148+
"prompt" => ServerFeature::new_prompt(&f.space_id, &f.server_id, &f.feature_name),
149+
"resource" => {
150+
ServerFeature::new_resource(&f.space_id, &f.server_id, &f.feature_name)
151+
}
152+
_ => ServerFeature::new_tool(&f.space_id, &f.server_id, &f.feature_name),
153+
};
154+
if let Some(dn) = f.display_name {
155+
sf = sf.with_display_name(dn);
156+
}
157+
if let Some(desc) = f.description {
158+
sf = sf.with_description(desc);
159+
}
160+
sf
161+
})
162+
.collect();
163+
164+
let ids: Vec<String> = storage_features.iter().map(|f| f.id.clone()).collect();
165+
166+
state
167+
.server_feature_repository
168+
.upsert_many(&storage_features)
169+
.await
170+
.map_err(|e| format!("Failed to seed features: {e}"))?;
171+
172+
Ok(ids)
173+
}
174+
175+
/// Input for seeding a server feature (E2E testing).
176+
#[derive(Debug, serde::Deserialize)]
177+
pub struct SeedFeatureInput {
178+
pub space_id: String,
179+
pub server_id: String,
180+
pub feature_type: String,
181+
pub feature_name: String,
182+
pub display_name: Option<String>,
183+
pub description: Option<String>,
184+
}

apps/desktop/src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ pub fn run() {
741741
commands::list_server_features_by_server,
742742
commands::list_server_features_by_type,
743743
commands::get_server_feature,
744+
commands::seed_server_features,
744745
// Client commands
745746
commands::list_clients,
746747
commands::get_client,

apps/desktop/src/components/OAuthConsentModal.tsx

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,25 @@ import {
2323
CardContent,
2424
} from '@mcpmux/ui';
2525
import { listSpaces, type Space } from '@/lib/api/spaces';
26+
import { resolveKnownClientKey } from '@/lib/clientIcons';
27+
import cursorIcon from '@/assets/client-icons/cursor.svg';
28+
import vscodeIcon from '@/assets/client-icons/vscode.png';
29+
import claudeIcon from '@/assets/client-icons/claude.svg';
30+
import windsurfIcon from '@/assets/client-icons/windsurf.svg';
31+
32+
/** Bundled icon assets for known clients */
33+
const CLIENT_ICON_ASSETS: Record<string, string> = {
34+
cursor: cursorIcon,
35+
vscode: vscodeIcon,
36+
claude: claudeIcon,
37+
windsurf: windsurfIcon,
38+
};
39+
40+
/** Look up a bundled logo for a known client by name */
41+
function getClientLogo(clientName: string): string | null {
42+
const key = resolveKnownClientKey(clientName);
43+
return key ? CLIENT_ICON_ASSETS[key] ?? null : null;
44+
}
2645

2746
/** Minimal deep link payload - only request_id */
2847
interface OAuthDeepLinkPayload {
@@ -267,15 +286,24 @@ export function OAuthConsentModal() {
267286
// Consent state - show approval modal
268287
const { details } = modalState;
269288
const scopes = details.scope?.split(' ').filter(Boolean) || ['mcp'];
289+
const logoUrl = getClientLogo(details.clientName);
270290

271291
return (
272292
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
273293
<Card className="w-full max-w-md mx-4 shadow-xl animate-in fade-in zoom-in duration-200">
274294
<CardHeader>
275295
<div className="flex items-center gap-3">
276-
<div className="p-2 rounded-full bg-primary-500/10">
277-
<Shield className="h-6 w-6 text-primary-500" />
278-
</div>
296+
{logoUrl ? (
297+
<img
298+
src={logoUrl}
299+
alt={details.clientName}
300+
className="h-10 w-10 rounded-full"
301+
/>
302+
) : (
303+
<div className="p-2 rounded-full bg-primary-500/10">
304+
<Shield className="h-6 w-6 text-primary-500" />
305+
</div>
306+
)}
279307
<div>
280308
<CardTitle>Authorization Request</CardTitle>
281309
<CardDescription>
@@ -286,12 +314,21 @@ export function OAuthConsentModal() {
286314
</CardHeader>
287315
<CardContent className="space-y-4">
288316
{/* Client Info */}
289-
<div className="p-4 rounded-lg bg-surface-hover border border-[rgb(var(--border))]">
290-
<div className="font-medium text-lg">{details.clientName}</div>
291-
<div className="text-sm text-[rgb(var(--muted))] mt-1 break-all">
292-
{details.clientId.length > 50
293-
? `${details.clientId.substring(0, 50)}...`
294-
: details.clientId}
317+
<div className="flex items-center gap-3 p-4 rounded-lg bg-surface-hover border border-[rgb(var(--border))]">
318+
{logoUrl && (
319+
<img
320+
src={logoUrl}
321+
alt={details.clientName}
322+
className="h-8 w-8 rounded-lg"
323+
/>
324+
)}
325+
<div>
326+
<div className="font-medium text-lg">{details.clientName}</div>
327+
<div className="text-sm text-[rgb(var(--muted))] mt-0.5 break-all">
328+
{details.clientId.length > 50
329+
? `${details.clientId.substring(0, 50)}...`
330+
: details.clientId}
331+
</div>
295332
</div>
296333
</div>
297334

0 commit comments

Comments
 (0)