|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +Guidance for coding agents working inside the `mcp-mux` repo — the McpMux desktop app and local gateway. Complements [`README.md`](README.md) and [`CONTRIBUTING.md`](CONTRIBUTING.md); when anything here conflicts with an explicit user instruction in the current session, the user wins. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +McpMux is a Tauri 2 desktop app (Rust + React 19) with a local Axum HTTP gateway on `localhost:45818`. It lets users configure MCP servers once and connect every AI client (Cursor, Claude Desktop, VS Code, Windsurf) through a single endpoint, with credentials encrypted in the OS keychain instead of plain-text JSON files. |
| 8 | + |
| 9 | +A more detailed map of the workspace lives in [`CLAUDE.md`](CLAUDE.md) at the repo root — read it for the crate layout, frontend architecture, and cross-project context. This file captures the minimum an agent needs to make safe, useful changes here. |
| 10 | + |
| 11 | +## Workspace Layout |
| 12 | + |
| 13 | +``` |
| 14 | +mcp-mux/ |
| 15 | +├── apps/desktop/ # Tauri shell — React frontend (src/) + Rust Tauri commands (src-tauri/) |
| 16 | +├── crates/ |
| 17 | +│ ├── mcpmux-core/ # Domain entities, repository traits, service layer, EventBus |
| 18 | +│ ├── mcpmux-gateway/ # Axum gateway — routing, OAuth refresh, FeatureSet filtering |
| 19 | +│ ├── mcpmux-storage/ # SQLite + AES-256-GCM field encryption + OS keychain |
| 20 | +│ └── mcpmux-mcp/ # MCP protocol client wrapper (rmcp SDK) |
| 21 | +├── packages/ui/ # Shared UI components (`@mcpmux/ui`) |
| 22 | +├── schemas/ # JSON Schemas surfaced in the Monaco editor |
| 23 | +└── tests/ # Rust integration, TS unit (vitest), desktop E2E (WDIO), web E2E (playwright) |
| 24 | +``` |
| 25 | + |
| 26 | +## Build & Dev Commands |
| 27 | + |
| 28 | +Run everything from `mcp-mux/`: |
| 29 | + |
| 30 | +| Command | What it does | |
| 31 | +|---------|--------------| |
| 32 | +| `pnpm setup` | First-time dev environment setup (PowerShell on Windows). | |
| 33 | +| `pnpm dev` | Tauri desktop dev mode (Rust + React hot-reload). | |
| 34 | +| `pnpm dev:web` | Web UI only via Vite — no Rust, no Tauri shell. | |
| 35 | +| `pnpm build` | Production Tauri build for the current platform. | |
| 36 | +| `pnpm validate` | Full correctness gate — runs the items below in sequence. | |
| 37 | +| `pnpm lint` | ESLint (recursive) + `cargo clippy --workspace -- -D warnings`. | |
| 38 | +| `pnpm lint:fix` | Auto-fix lint issues. | |
| 39 | +| `pnpm format` | `prettier --write .` + `cargo fmt --all`. | |
| 40 | +| `pnpm format:check` | Formatting check (no writes). | |
| 41 | +| `pnpm typecheck` | Recursive TypeScript typecheck. | |
| 42 | + |
| 43 | +**Before claiming a change is done**, run `pnpm validate` (or the relevant subset) — it mirrors what CI enforces. |
| 44 | + |
| 45 | +## Testing |
| 46 | + |
| 47 | +| Command | Scope | |
| 48 | +|---------|-------| |
| 49 | +| `pnpm test` | Rust + TypeScript, everything. | |
| 50 | +| `pnpm test:rust` | `cargo nextest run --workspace`. | |
| 51 | +| `pnpm test:rust:unit` | `cargo nextest run --workspace --lib`. | |
| 52 | +| `pnpm test:rust:int` | `cargo nextest run -p tests` — integration crate in `tests/rust`. | |
| 53 | +| `pnpm test:rust:doc` | `cargo test --workspace --doc`. | |
| 54 | +| `pnpm test:ts` | Vitest run (`tests/ts/vitest.config.ts`). | |
| 55 | +| `pnpm test:ts:watch` | Vitest watch. | |
| 56 | +| `pnpm test:e2e` | Desktop E2E via WebDriver IO — requires `MCPMUX_REGISTRY_URL`. | |
| 57 | +| `pnpm test:e2e:file -- tests/e2e/specs/foo.ts` | One WDIO spec file. | |
| 58 | +| `pnpm test:e2e:grep -- "test name"` | WDIO tests matching a name. | |
| 59 | +| `pnpm test:e2e:web` | Playwright on the web UI. | |
| 60 | +| `pnpm test:coverage` | `cargo llvm-cov` + Vitest coverage. | |
| 61 | + |
| 62 | +Prefer narrow commands over `pnpm test` while iterating — the full suite is slow. |
| 63 | + |
| 64 | +## Code Style |
| 65 | + |
| 66 | +- **Rust:** 100-char max width, 4-space indent. Clippy runs with `avoid-breaking-exported-api = false`; all warnings are denied in CI. |
| 67 | +- **TypeScript / JSX:** Prettier — single quotes, 2-space indent, 100-char width, trailing commas (es5), Tailwind CSS plugin for class ordering. |
| 68 | +- **Path aliases:** `@/` → `apps/desktop/src/`; `@mcpmux/ui` → `packages/ui`. |
| 69 | +- **No emojis in code or commits** unless the user explicitly asks for them. |
| 70 | +- **Comments:** only when the *why* is non-obvious. Identifiers should explain the *what*. |
| 71 | + |
| 72 | +## Commit & PR Guidelines |
| 73 | + |
| 74 | +- Commits must be **signed off** (DCO): `git commit -s -m "..."`. CI rejects unsigned commits. |
| 75 | +- Prefer conventional-style subjects — releases use release-please for semantic versioning. |
| 76 | +- PRs follow [`.github/pull_request_template.md`](.github/pull_request_template.md): describe the change, how you tested, and check the `pnpm test` / `pnpm lint` / `pnpm typecheck` boxes. |
| 77 | +- Don't bypass hooks (`--no-verify`) or DCO signing unless explicitly told to. |
| 78 | + |
| 79 | +## Platform Gotchas |
| 80 | + |
| 81 | +### Child-process flags |
| 82 | + |
| 83 | +Anything that spawns a child process (stdio MCP servers, installers, etc.) **must** go through `mcpmux_gateway::pool::transport::configure_child_process_platform()`. That helper applies: |
| 84 | + |
| 85 | +- **Windows:** `CREATE_NO_WINDOW` (`0x08000000`) — release builds use `windows_subsystem = "windows"`, so without this the OS briefly flashes a console window when a child starts. |
| 86 | +- **Unix:** `process_group(0)` — stops SIGINT/SIGTSTP from the parent terminal from tearing down the child. |
| 87 | + |
| 88 | +`tokio::process::Command` already exposes `creation_flags()` (Windows) and `process_group()` (Unix). **Do not** import `std::os::*::process::CommandExt` — those traits are unused with Tokio's `Command` and trigger clippy. |
| 89 | + |
| 90 | +### Cross-platform CI |
| 91 | + |
| 92 | +- The pre-commit hook runs `cargo clippy --workspace -- -D warnings` on your dev machine. |
| 93 | +- `#[cfg(unix)]` only compiles on Unix; `#[cfg(windows)]` only on Windows. CI is Linux, so Windows-gated code is **not** linted in CI, and Unix-gated code is not linted on a Windows dev box. |
| 94 | +- When you touch platform-conditional code, check the *other* platform compiles before pushing — CI won't catch a Windows-only clippy regression. |
| 95 | + |
| 96 | +### Secret handling |
| 97 | + |
| 98 | +- Never log tokens, API keys, headers with auth material, or raw OAuth responses. Use the existing sanitised-log helpers in `mcpmux-gateway`. |
| 99 | +- Credentials encrypt at rest via AES-256-GCM in SQLite plus DPAPI (Windows) / OS keychain (macOS, Linux). Don't add new code paths that persist secrets any other way. |
| 100 | +- Secrets should be wiped from memory after use via `zeroize`. |
| 101 | +- The gateway binds to `127.0.0.1`. Don't bind to `0.0.0.0` or expose it on the network. |
| 102 | + |
| 103 | +## Frontend Notes |
| 104 | + |
| 105 | +- Entry point: `apps/desktop/src/main.tsx` → `App.tsx`. |
| 106 | +- Global state: a single Zustand store at `src/stores/appStore.ts`. |
| 107 | +- Key hooks: `useServerManager` (server CRUD), `useSpaces` (workspace switching), `useDomainEvents` (Rust-side EventBus listener), `useDataSync`. |
| 108 | +- UI: React 19, Tailwind CSS, Lucide icons, Monaco Editor for JSON config surfaces. |
| 109 | +- Open external URLs through `openExternal` in `apps/desktop/src/lib/contribute.ts` — it routes through the Tauri opener plugin so links open in the user's default browser, not the webview. |
| 110 | +- For UI changes, launch `pnpm dev` and exercise the feature in the running app before reporting done — typecheck and tests verify correctness, not UX regressions. |
| 111 | + |
| 112 | +## Rust Architecture Cues |
| 113 | + |
| 114 | +- Cross-layer communication goes through the `EventBus` in `mcpmux-core`. Prefer emitting a domain event over reaching across module boundaries directly. |
| 115 | +- Storage is behind repository traits — don't call SQLx or SQLite APIs directly from gateway or app code; add or use a repo method. |
| 116 | +- Services are wired up via the `ApplicationServices` builders in `mcpmux-core`. New services should follow the same DI pattern. |
| 117 | + |
| 118 | +## MCP Specification |
| 119 | + |
| 120 | +The full MCP spec is vendored at `../modelcontextprotocol/docs/specification/`. Default to the latest stable version (`2025-11-25`) and **read the relevant section before** implementing or modifying protocol behaviour (transports, lifecycle, capability negotiation, OAuth flows, tools / resources / prompts). For features targeting a specific protocol version, use that version's folder. |
| 121 | + |
| 122 | +## Server Definitions |
| 123 | + |
| 124 | +Server catalog entries live in the separate [`mcp-servers`](https://github.com/mcpmux/mcp-servers) repo — **not here**. If a task involves adding, editing, or fixing a server definition, switch to that repo and follow its `AGENTS.md`. |
| 125 | + |
| 126 | +## Things Not To Do |
| 127 | + |
| 128 | +- Don't add backwards-compatibility shims, deprecated aliases, or `// removed` placeholder comments when removing code — delete it cleanly. |
| 129 | +- Don't introduce new fallbacks or input validation for states that are already framework-guaranteed. Trust internal invariants; validate only at the boundary (user input, external APIs). |
| 130 | +- Don't edit generated files: `CHANGELOG.md`, release-please manifests, `bundle/*.json` in sibling repos, `packages/ui/dist`. |
| 131 | +- Don't commit screenshots, videos, or large binaries to the repo — link out instead. |
0 commit comments