|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Codebase Search |
| 6 | + |
| 7 | +This repository is indexed for semantic code search via the `claude-context` MCP server. Prefer `mcp__claude-context__search_code` (with path `D:\mcpmux`) over Grep/Glob when looking for implementations, understanding how features work, or finding related code across the codebase. Use Grep/Glob for exact string matches or filename patterns. If search returns an indexing error, re-index with `mcp__claude-context__index_codebase` first. |
| 8 | + |
| 9 | +## What is McpMux |
| 10 | + |
| 11 | +McpMux is a desktop app + local gateway that lets users configure MCP servers once and connect every AI client (Cursor, Claude Desktop, VS Code, Windsurf) through a single `localhost:45818` endpoint. Credentials are encrypted in the OS keychain instead of plain-text JSON files. |
| 12 | + |
| 13 | +## Repository Structure |
| 14 | + |
| 15 | +This is a multi-project workspace with 6 independent projects (not a unified monorepo): |
| 16 | + |
| 17 | +| Project | Tech | Purpose | |
| 18 | +|---------|------|---------| |
| 19 | +| `mcp-mux/` | Tauri 2 (Rust + React 19), pnpm workspace | Desktop app + local MCP gateway | |
| 20 | +| `mcpmux.bundler/` | Cloudflare Worker | GitHub webhook receiver that processes server definitions into D1/R2 | |
| 21 | +| `mcpmux.serverhub.api/` | Cloudflare Worker (Hono) | REST API for server registry discovery (KV -> D1 -> R2 fallback chain) | |
| 22 | +| `mcpmux.discover.ui/` | Next.js 16, shadcn/ui, Tailwind 4 | Web UI for browsing the server registry | |
| 23 | +| `mcp-servers/` | JSON + AJV validation | Community MCP server definitions repository | |
| 24 | +| `mcpmux.space/` | Docs | Documentation and design space | |
| 25 | + |
| 26 | +## mcp-mux (Desktop App) - Main Project |
| 27 | + |
| 28 | +### Build & Dev Commands |
| 29 | + |
| 30 | +All commands run from `mcp-mux/`: |
| 31 | + |
| 32 | +```bash |
| 33 | +pnpm setup # First-time dev environment setup (PowerShell) |
| 34 | +pnpm dev # Tauri desktop app dev mode (Rust + React hot-reload) |
| 35 | +pnpm dev:web # Web UI only (Vite, no Rust) |
| 36 | +pnpm build # Production Tauri build (all platforms) |
| 37 | +``` |
| 38 | + |
| 39 | +### Testing |
| 40 | + |
| 41 | +```bash |
| 42 | +pnpm test # All tests (Rust + TypeScript) |
| 43 | +pnpm test:rust # cargo nextest run --workspace |
| 44 | +pnpm test:rust:unit # cargo nextest run --workspace --lib |
| 45 | +pnpm test:rust:int # cargo nextest run -p tests |
| 46 | +pnpm test:rust:doc # cargo test --workspace --doc |
| 47 | +pnpm test:ts # vitest run -c tests/ts/vitest.config.ts |
| 48 | +pnpm test:ts:watch # vitest watch mode |
| 49 | +pnpm test:e2e # WebDriver IO desktop E2E (needs MCPMUX_REGISTRY_URL) |
| 50 | +pnpm test:e2e:file # Single E2E spec: pnpm test:e2e:file -- tests/e2e/specs/foo.ts |
| 51 | +pnpm test:e2e:grep # E2E by name: pnpm test:e2e:grep -- "test name" |
| 52 | +pnpm test:e2e:web # Playwright web UI E2E |
| 53 | +pnpm test:coverage # cargo llvm-cov + vitest coverage |
| 54 | +``` |
| 55 | + |
| 56 | +### Linting & Validation |
| 57 | + |
| 58 | +```bash |
| 59 | +pnpm validate # Full check: cargo fmt + clippy + check + eslint + typecheck |
| 60 | +pnpm lint # ESLint (recursive) + cargo clippy --workspace -- -D warnings |
| 61 | +pnpm lint:fix # Auto-fix lint issues |
| 62 | +pnpm format # prettier --write . && cargo fmt --all |
| 63 | +pnpm format:check # Check formatting without modifying |
| 64 | +pnpm typecheck # TypeScript type checking (recursive) |
| 65 | +``` |
| 66 | + |
| 67 | +### Rust Crate Architecture |
| 68 | + |
| 69 | +The Cargo workspace has 4 library crates + 1 app crate + 1 test crate: |
| 70 | + |
| 71 | +- **mcpmux-core** (`crates/mcpmux-core/`) - Domain layer: entities (Space, InstalledServer, FeatureSet, Client), repository traits, domain services, application services with event emission, and the central EventBus |
| 72 | +- **mcpmux-gateway** (`crates/mcpmux-gateway/`) - Axum HTTP gateway: routes MCP calls to correct servers, manages OAuth 2.1+PKCE token refresh, filters tools/resources/prompts based on FeatureSets, per-client access key auth, server connection pooling |
| 73 | +- **mcpmux-storage** (`crates/mcpmux-storage/`) - SQLite persistence with AES-256-GCM field-level encryption via ring, typed credential rows (per-token encryption), DPAPI key storage on Windows (`keychain_dpapi.rs`), OS keychain on macOS/Linux via keyring crate, zeroize for secure memory clearing |
| 74 | +- **mcpmux-mcp** (`crates/mcpmux-mcp/`) - MCP protocol client management using rmcp SDK |
| 75 | +- **apps/desktop/src-tauri** - Tauri 2 app shell, Tauri commands, system tray, deep-link handler (`mcpmux://`) |
| 76 | +- **tests/rust** - Integration test crate |
| 77 | + |
| 78 | +Key patterns: event-driven architecture (EventBus), repository pattern (trait-based storage abstraction), service layer pattern with DI via ApplicationServices builders. |
| 79 | + |
| 80 | +### Frontend Architecture |
| 81 | + |
| 82 | +- **Entry**: `apps/desktop/src/main.tsx` -> `App.tsx` |
| 83 | +- **State**: Zustand store (`stores/appStore.ts`) |
| 84 | +- **Hooks**: `useServerManager` (server CRUD), `useSpaces` (workspace switching), `useDomainEvents` (Rust event listeners), `useDataSync` (data synchronization) |
| 85 | +- **UI**: React 19 + Tailwind CSS + Lucide icons + Monaco Editor (config editing) |
| 86 | +- **Path aliases**: `@/` -> `src/`, `@mcpmux/ui` -> shared UI package |
| 87 | + |
| 88 | +### Data Flow |
| 89 | + |
| 90 | +``` |
| 91 | +AI Clients -> McpMux Gateway (localhost:45818/mcp) -> MCP Servers (stdio/HTTP) |
| 92 | + | |
| 93 | + Authenticates (access keys) |
| 94 | + Routes (per-space server config) |
| 95 | + Filters (FeatureSet permissions) |
| 96 | + OAuth token refresh (automatic) |
| 97 | + Credentials (DPAPI files on Windows / OS Keychain on macOS+Linux + encrypted SQLite) |
| 98 | +``` |
| 99 | + |
| 100 | +### Prerequisites |
| 101 | + |
| 102 | +Rust 1.75+, Node.js 20+, pnpm 9+. Linux: `gnome-keyring libsecret-1-dev librsvg2-dev pkg-config`. |
| 103 | + |
| 104 | +### Code Style |
| 105 | + |
| 106 | +- Rust: 100 char max width, 4-space indent, `clippy` with `avoid-breaking-exported-api = false` |
| 107 | +- TypeScript/JSX: Prettier with single quotes, 2-space indent, 100 char width, trailing commas (es5), Tailwind CSS plugin |
| 108 | +- Commits require `Signed-off-by` line (use `git commit -s`) |
| 109 | + |
| 110 | +## mcp-servers (Server Definitions Repository) |
| 111 | + |
| 112 | +All commands run from `mcp-servers/` (sibling repo): |
| 113 | + |
| 114 | +```bash |
| 115 | +pnpm install # Install dependencies (vitest, ajv, glob) |
| 116 | +pnpm validate:all # Validate all server definitions against JSON schema |
| 117 | +pnpm validate <file> # Validate specific server definition file(s) |
| 118 | +pnpm build # Build registry bundle (bundle/bundle.json) |
| 119 | +pnpm test # Run all tests (schema validation, consistency, categories, bundle) |
| 120 | +``` |
| 121 | + |
| 122 | +The JSON schema at `schemas/server-definition.schema.json` defines the structure for server definitions in `servers/`. Input definitions support: `id`, `label`, `type`, `required`, `secret`, `description`, `default`, `placeholder`, and `obtain` (with `url`, `instructions`, `button_label`). |
| 123 | + |
| 124 | +## Cloudflare Workers |
| 125 | + |
| 126 | +Each worker is an independent project with its own `package.json`: |
| 127 | + |
| 128 | +**mcpmux.serverhub.api/** - Hono-based registry API: |
| 129 | +```bash |
| 130 | +cd mcpmux.serverhub.api && pnpm dev # wrangler dev |
| 131 | +cd mcpmux.serverhub.api && pnpm test # vitest (cloudflare pool) |
| 132 | +``` |
| 133 | + |
| 134 | +**mcpmux.bundler/** - Webhook-triggered bundle processor: |
| 135 | +```bash |
| 136 | +cd mcpmux.bundler && pnpm dev # wrangler dev |
| 137 | +cd mcpmux.bundler && pnpm test # vitest run |
| 138 | +``` |
| 139 | + |
| 140 | +## mcpmux.discover.ui (Discovery Web UI) |
| 141 | + |
| 142 | +```bash |
| 143 | +cd mcpmux.discover.ui && pnpm dev # next dev |
| 144 | +cd mcpmux.discover.ui && pnpm test # vitest |
| 145 | +cd mcpmux.discover.ui && pnpm test:e2e # playwright |
| 146 | +``` |
| 147 | + |
| 148 | +## Important Patterns |
| 149 | + |
| 150 | +### Child Process Platform Flags |
| 151 | + |
| 152 | +When spawning child processes (e.g., stdio MCP servers), **always** use `configure_child_process_platform()` from `mcpmux_gateway::pool::transport`. This applies: |
| 153 | +- **Windows**: `CREATE_NO_WINDOW` (`0x08000000`) — prevents visible console windows in release builds (where `windows_subsystem = "windows"` means the app is a GUI subsystem process) |
| 154 | +- **Unix**: `process_group(0)` — isolates child from parent terminal signals (SIGINT, SIGTSTP) |
| 155 | + |
| 156 | +Note: `tokio::process::Command` already exposes `creation_flags()` (Windows) and `process_group()` (Unix) natively — do **not** import `std::os::unix::process::CommandExt` or `std::os::windows::process::CommandExt` as the traits are unused with Tokio's Command. |
| 157 | + |
| 158 | +### Cross-Platform CI Awareness |
| 159 | + |
| 160 | +The pre-commit hook runs `cargo clippy --workspace -- -D warnings` locally, but `#[cfg(unix)]` / `#[cfg(windows)]` blocks are only compiled on the matching platform. **CI runs on Linux**, so: |
| 161 | +- `#[cfg(unix)]` code is only linted in CI, not on a Windows dev machine |
| 162 | +- `#[cfg(windows)]` code is only linted locally on Windows, not in CI |
| 163 | +- Always validate that platform-conditional code compiles correctly on both platforms before pushing |
| 164 | + |
| 165 | +## CI |
| 166 | + |
| 167 | +GitHub Actions runs on push/PR to main: Rust format + clippy + check, ESLint + typecheck, cargo nextest, vitest, desktop E2E (Windows/macOS/Linux), web E2E (Playwright). Releases use release-please for semantic versioning with multi-platform Tauri builds. |
0 commit comments