@maind-dev/mcp-bridge
v0.40.0
Published
Local MCP bridge for maind.dev — wraps any MCP-compatible client (Claude Code, Cursor, Windsurf) and pins your API key to a single device via the maind device authorization flow.
Maintainers
Readme
@maind-dev/mcp-bridge
A tiny local MCP wrapper that pins your maind API key to
a single device. The bridge speaks MCP over stdio to your IDE (Claude Code,
Cursor, Windsurf, …) and forwards every request to https://mcp.maind.dev/mcp
with an X-Maind-Device-Id header, so the server can refuse calls from any
other machine that tries to reuse the same key.
Required for Pro and higher tiers; optional for Free.
How it works
- First run opens your browser at
https://maind.dev/devices?code=…and prints a short verification code. You sign in (if needed) and click Approve — the bridge receives an API key and stores it locally under~/.config/maind/(or platform equivalent). A stable device-id is created in the same directory. - Every subsequent run reuses the cached key plus device-id and forwards
stdio JSON-RPC frames to maind, attaching both the
Authorization: Bearer …andX-Maind-Device-Idheaders. - If you re-pair from the dashboard (or move to a new machine), the next
call returns
401 device_revoked. The bridge drops its cached key and re-runs the device flow once before exiting non-zero on a second failure.
The device-id is an opaque UUID; knowing it does not authorize anything. The bridge does not transmit your hostname, OS user, or any other identifying metadata beyond what the dashboard already shows for VS Code extension pairings.
Install / configure
You normally don't install anything — npx fetches the bridge on first run.
Add this to your MCP client's config (.mcp.json or equivalent):
{
"mcpServers": {
"maind": {
"command": "npx",
"args": ["-y", "@maind-dev/mcp-bridge"]
}
}
}If you want maind to attribute calls to the real upstream agent while still using the bridge transport, add a client marker via env:
{
"mcpServers": {
"maind": {
"command": "npx",
"args": ["-y", "@maind-dev/mcp-bridge"],
"env": {
"MAIND_CLIENT_FAMILY": "codex"
}
}
}
}Restart the MCP client. The first invocation walks you through the device flow; subsequent ones are silent (you should never see the verification code again unless you re-pair).
Commands
| Command | Effect |
|---|---|
| npx -y @maind-dev/mcp-bridge | Run as MCP server (default; what your IDE invokes) |
| npx -y @maind-dev/mcp-bridge install | One-shot setup: MCP configs, preamble files, Claude Code hooks |
| npx -y @maind-dev/mcp-bridge --reauth | Drop the cached key and re-run the device flow |
| npx -y @maind-dev/mcp-bridge --version | Print the bridge version |
| npx -y @maind-dev/mcp-bridge --help | Show usage |
Claude Code hook subcommands
install wires these into ~/.claude/settings.json for you (consent is asked
per hook, and an absolute interpreter-pinned path is written rather than npx).
They read the hook payload on stdin and authenticate with the bridge's own
paired credential — there is no separate init step. Since 0.16.0 they live
here; the standalone maind-hook binary they came from is retired (ADR-245).
| Subcommand | Hook event | Effect |
|---|---|---|
| on-session-start | SessionStart | Open a tracked session for the flat Tier-3 metrics |
| on-tool-use | PreToolUse | Count tool calls; warn on stderr when token usage crosses your handoff thresholds (ADR-039) |
| on-session-end | Stop | Post session communication analytics; close and flush the tracked session |
| sync-config | — | Pull your context thresholds from the server |
| flush | — | Post queued Tier-3 metrics now |
| status | — | Show pairing, queued metrics and active thresholds |
Everything these post is gated server-side by Tier-3 consent
(https://app.maind.dev/consent) — with it off, the rows are dropped, by design.
Thresholds are set at https://app.maind.dev/settings; run sync-config
afterwards on each machine.
Where things live
| Path | Purpose |
|---|---|
| ~/Library/Application Support/maind/device-id (macOS) | Stable per-install UUID |
| ~/.config/maind/device-id (Linux/BSD) | Same, on XDG-style systems |
| %APPDATA%\maind\device-id (Windows) | Same, on Windows |
| device-id-sibling file api-key | API key returned by approve flow (mode 0600) |
| ~/.maind/state.json | Hook subcommand state: tracked sessions, queued Tier-3 metrics, synced thresholds (mode 0600) |
To rebind the bridge to a different account: delete the directory above (or run
--reauth) and restart the bridge.
Troubleshooting
"device_mismatch" on every call. Another machine has bound this key —
the server is doing the right thing by refusing yours. Either unpair the
other device from https://maind.dev/devices, or --reauth here to bind
this machine and revoke the other.
Browser doesn't open. The bridge falls back to printing the URL + verification code to stderr. Copy the URL into any browser, paste the code on the page, click Approve.
Behind a corporate proxy. Set HTTPS_PROXY=https://proxy:port before
launching your MCP client. The bridge uses Node's built-in fetch, which
respects the proxy env vars on Node 22+.
npx can't reach registry.npmjs.org. Install the bridge globally
(npm install -g @maind-dev/mcp-bridge) and point your .mcp.json at
"command": "maind-mcp-bridge" instead of npx.
Environment overrides
Rarely useful outside maind's own development environments:
| Variable | Default | Purpose |
|---|---|---|
| MAIND_MCP_URL | https://mcp.maind.dev/mcp | Streamable-HTTP endpoint to forward to |
| MAIND_CLIENT_FAMILY | unset | Forwards X-Maind-Client-Family (claude-code, codex, copilot, cursor, windsurf, …) so telemetry tracks the real upstream agent instead of generic mcp-bridge |
| MAIND_CLIENT_VERSION | unset | Forwards optional X-Maind-Client-Version marker (max 64 chars) |
| MAIND_FUNCTIONS_ORIGIN | maind's Supabase Edge Functions URL | Device-flow base URL |
| XDG_CONFIG_HOME | ~/.config (Linux/BSD) | Parent of the maind/ config dir |
License
MIT — see LICENSE.
