Local MCP Gateway Manager for AI Agents
Aggregate multiple MCP servers into a single endpoint, filter tools by Profile, and manage everything from a beautiful native UI.
Install · Quickstart · Features · Architecture · Development · API
English · 中文
AI Agents need tools, but managing dozens of MCP servers across different clients is a mess. I wanted a single gateway that aggregates everything, filters by context, and keeps running in the background — all controllable from a beautiful native UI.
Moor exposes one endpoint (
http://127.0.0.1:<port>/mcp) that dynamically serves only the tools you want, based on your active Profile. Switch profiles without disconnecting your Agent, and every tool call is audited. That's why I built it.
Download the .dmg from Releases, drag to Applications, done. The app bundles the Node.js sidecar as a standalone binary — no pre-installed runtime required.
Requires macOS (Apple Silicon / Intel), Node.js >= 20, pnpm >= 9, and Rust >= 1.77.
git clone https://github.com/yourusername/moor.git
cd moor
vp installSee Development for build instructions.
Open Moor.app. The Dashboard shows your active Profile, server status, and recent audit logs at a glance.
Moor can automatically detect MCP servers you've already configured for Claude Code, Codex, and OpenCode:
- Go to Servers → Import
- Click Scan — Moor reads
~/.claude/settings.json,~/.codex/config.toml, and~/.config/opencode/opencode.json/.jsonc - Select the servers you want to import
You can also paste a JSON MCP configuration with Import JSON. Moor imports stdio and HTTP/SSE servers, and reports unsupported entries such as OpenAPI configs without saving them.
Profiles let you group servers and control which tools are exposed to Agents:
- Go to Profiles → New Profile
- Name it (e.g., "Coding", "Research")
- Toggle servers on/off
- Expand a server to enable/disable individual tools
- Click Activate — the change is instant
Point any MCP-compatible client to Moor's single endpoint:
http://127.0.0.1:9223/mcp
9223 is the default sidecar port. If it is already in use, Moor picks the next available port and shows the actual endpoint in the Dashboard and Client Config pages.
The /mcp endpoint is loopback-only and does not require X-Moor-Token. Moor uses X-Moor-Token only for local management APIs between the WebView and sidecar, so you do not need to paste it into agent configs.
Moor handles the rest — aggregating tools/list, routing tools/call, and filtering based on your active Profile.
A single HTTP endpoint (/mcp) proxies all backend MCP servers. Agents see a unified tool catalog — no need to configure multiple endpoints.
Connect to both stdio (subprocess) and HTTP/SSE MCP servers. Moor manages connection lifecycles, restarts, and health checks automatically.
Create unlimited Profiles for different workflows. Each Profile stores:
- Which servers are enabled
- Which tools are disabled per server
- A global active state
Switch Profiles with hot-swap — connected Agents stay connected, and the next tools/list reflects the new configuration immediately.
Beyond server-level on/off, drill into any server to disable specific tools. Disabled tools disappear from the Agent's tool catalog in real time.
One-click import from:
- Claude Code:
~/.claude/settings.json - Codex:
~/.codex/config.toml - OpenCode:
~/.config/opencode/opencode.json/.jsonc
Manual entry and pasted JSON batch import are also supported for stdio and HTTP/SSE servers.
Generate ready-to-copy configuration snippets for Claude Code, Codex, and OpenCode. The snippets contain only the /mcp endpoint; Moor's X-Moor-Token is reserved for internal management API calls.
Every tools/call is recorded with:
- Timestamp, Profile, Server, Tool name
- Arguments (with sensitive data redaction)
- Result or error
- Duration and Agent info
Filter by time range, server, or tool. View aggregate statistics on the Dashboard.
Close the window — Moor keeps running in the macOS menu bar. The gateway stays alive, so your Agents never lose connection.
Server status changes and Profile switches are pushed to the UI via SSE. No refresh needed.
Architecture Diagram
Moor.app
├── UI Layer React + Vite + TypeScript + Tailwind CSS v4 + shadcn/ui
├── Desktop Layer Tauri 2 / Rust
│ ├── Window management + tray icon
│ ├── macOS Keychain access
│ └── Sidecar process lifecycle management
├── Gateway Daemon Node.js / TypeScript Sidecar (bundled as SEA standalone binary)
│ ├── MCP protocol gateway POST /mcp — init, tools/list, tools/call
│ ├── Server management stdio spawn + HTTP/SSE client
│ ├── Profile routing Global active Profile, hot-swap
│ ├── Audit logging Async batch write (500ms / 50 entries)
│ └── SSE push Real-time status sync to WebView
└── Storage SQLite (node:sqlite)
├── servers (configs, status)
├── profiles (server groups + tool toggles)
└── audit_logs (tool calls, params, results, errors)
AI Agent ──HTTP──▶ POST /mcp ──▶ Moor Gateway ──stdio/HTTP──▶ MCP Servers
│
WebView ──fetch──▶ /api/* ────┘
WebView ◀──SSE──── /api/events
- Business operations: WebView → HTTP
fetch()→ Sidecar (Node.js) - System operations: WebView → Tauri IPC → Rust (macOS Keychain, tray, window)
- macOS (Apple Silicon / Intel)
- Node.js >= 20
- pnpm >= 9
- Rust >= 1.77
- Xcode Command Line Tools
vp installStart both frontend and sidecar:
pnpm dev:all- Frontend: http://localhost:1420
- Sidecar API: http://localhost:9223
Start the full desktop app (Tauri):
pnpm tauri devpnpm tauri buildOutputs:
src-tauri/target/release/bundle/macos/Moor.appsrc-tauri/target/release/bundle/dmg/Moor_0.1.0_aarch64.dmg
vp check # format + lint + type check
vp lint # lint only
vp lint --fix # auto-fix
vp fmt # format# Sidecar tests
pnpm --filter moor-sidecar test
# Frontend tests
vp test| Method | Path | Description |
|---|---|---|
ALL |
/mcp |
MCP protocol endpoint (Streamable HTTP) |
| Method | Path | Description |
|---|---|---|
GET |
/api/servers |
List all servers |
POST |
/api/servers |
Add server |
GET |
/api/servers/:id |
Server detail |
PUT |
/api/servers/:id |
Update server config |
DELETE |
/api/servers/:id |
Remove server |
POST |
/api/servers/:id/start |
Start server |
POST |
/api/servers/:id/stop |
Stop server |
GET |
/api/servers/:id/tools |
Get discovered tools |
| Method | Path | Description |
|---|---|---|
GET |
/api/profiles |
List all profiles |
POST |
/api/profiles |
Create profile |
PUT |
/api/profiles/:id |
Update profile |
DELETE |
/api/profiles/:id |
Delete profile |
PUT |
/api/profiles/:id/activate |
Set as active profile |
PUT |
/api/profiles/:id/servers/:sid |
Update server toggle + disabled tools |
| Method | Path | Description |
|---|---|---|
GET |
/api/logs |
Query logs (with filters) |
GET |
/api/logs/stats |
Aggregate statistics |
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Health check |
GET |
/api/runtime |
Runtime info (port, URL) |
GET |
/api/events |
SSE real-time event stream |
POST |
/api/import/scan |
Scan local client configs |
POST |
/api/import/parse |
Preview pasted JSON import |
POST |
/api/import/execute |
Execute import |
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 6, TypeScript 5.7, Tailwind CSS v4 |
| UI Components | shadcn/ui (New York style) |
| Desktop | Tauri 2 (Rust) |
| Sidecar | Node.js, TypeScript, Hono, @hono/node-server |
| Database | SQLite (node:sqlite) |
| MCP Protocol | @modelcontextprotocol/sdk (stdio + HTTP/SSE) |
| Icons | Lucide React |
| Tooling | Vite+ (vp CLI), Oxlint, Oxfmt, Vitest |
Thanks to the linuxdo community for discussion, sharing, and feedback.
