mcpmesh 0.25.1

Share MCP servers with people you trust β€” peer to peer, default-deny, no accounts
Documentation

🀝 What is this?

mcpmesh lets your AI use your friends' tools.

Got an MCP server running on your machine β€” your notes, your code search, your scripts? Share it with a specific person, and their AI client (Claude Desktop, Claude Code, or anything that speaks MCP) mounts it as if it were local. πŸͺ„

  • 🚫 No hosting. The server runs on your machine. Nothing is uploaded anywhere.
  • πŸ”’ No middleman. An end-to-end-encrypted connection straight between your two machines.
  • πŸ™… No accounts, no OAuth, nothing to sign up for.

🎬 New here? The tech presentation is a two-minute visual tour of what mcpmesh is and how it works.

πŸš€ Quick start

One minute, two machines β€” call them you and friend:

cargo install mcpmesh    # on both machines (macOS, Linux, or Windows)

# πŸ–₯️  you β€” share a folder of notes as an MCP server, under a name you pick:
mcpmesh serve notes -- npx -y @modelcontextprotocol/server-filesystem ~/notes
mcpmesh invite notes     # prints a one-time mcpmesh-invite:… line β€” send it to your friend

# πŸ“© friend β€” redeem the invite:
mcpmesh pair mcpmesh-invite:…

That's it. βœ… pair prints the exact next steps β€” the safety code to confirm, and the one command that wires the share into Claude Code (claude mcp add you-notes -- mcpmesh connect you/notes) or the entry to paste into Claude Desktop. (Run mcpmesh use you/notes any time to see them again.)

Both of you also see the same short code, like tango-fig-cabbage. πŸ—£οΈ Read it to each other β€” matching words mean the pairing is authentic. Now your friend's AI can search your notes over an encrypted peer-to-peer link, and it works both ways whenever they want to share back.

πŸ€– Driving mcpmesh from a script or an AI agent? See AGENTS.md β€” every verb takes --json, errors carry a machine-branchable code, and nothing ever prompts.

πŸ¦€ Building in Rust? Embed a full node in-process β€” no daemon, no sidecar: mcpmesh-node boots your app's own mesh identity in a few lines, speaking the same control API the daemon serves.

πŸ§ͺ Try it alone

Only one machine? Fake the friend. A daemon's whole world lives under HOME + XDG_RUNTIME_DIR, so a scratch HOME is a complete second identity β€” and the two pair on one machine exactly as two machines would (macOS/Linux):

# A real (symlink-free) path β€” NOT /tmp or $TMPDIR: on macOS both resolve through a symlink
# (/tmp β†’ /private/tmp), and the filesystem MCP server then rejects every path you give it.
FH=$HOME/.mcpmesh-demo-friend; mkdir -p $FH/notes $FH/run && echo hi > $FH/notes/hello.md

# the "friend" serves a folder and mints a real invite under the scratch identity…
HOME=$FH XDG_RUNTIME_DIR=$FH/run mcpmesh serve notes -- npx -y @modelcontextprotocol/server-filesystem $FH/notes
HOME=$FH XDG_RUNTIME_DIR=$FH/run mcpmesh invite notes

# …and YOUR identity redeems it, exactly as a real friend would:
mcpmesh pair mcpmesh-invite:…

From pair onward you're in the two-machine flow β€” safety code, mount, connect. Nothing is mocked: real keys, a real one-time invite, a real encrypted session. The guided version with a live end-to-end proof and cleanup is docs/loopback.md, or just run docs/loopback.sh.

πŸ›‘οΈ Why it's safe to try

  • πŸ” Default-deny. Nothing is shared until you run serve + invite. Every grant names exactly who gets access, and mcpmesh pair --remove cuts a peer off instantly.
  • πŸ•΅οΈ No middleman. Traffic is end-to-end encrypted, machine to machine (iroh/QUIC with NAT hole-punching). No account to create, no server of ours in the trust path.
  • βœ‹ Tamper-evident invites. Invites are one-time and expiring, and the spoken safety code catches a tampered invite before anything is shared.

πŸ‘₯ The full walkthrough: Alice and Bob

Two people, two machines. Alice shares an MCP server with Bob; then Bob shares one back. Install on both machines first β€” cargo install mcpmesh, or via Homebrew on macOS/Linux:

brew tap counterpunchtech/mcpmesh https://github.com/counterpunchtech/mcpmesh
brew install counterpunchtech/mcpmesh/mcpmesh

1️⃣ Alice serves a server and invites Bob

mcpmesh doesn't know or care what a server does β€” you register any MCP server under a name you pick, and that name is how peers refer to it:

# Everything after `--` is just the command that runs the server;
# "notes" is Alice's alias for it (any name works):
alice$ mcpmesh serve notes -- npx -y @modelcontextprotocol/server-filesystem ~/notes

alice$ mcpmesh invite notes
One-time invite (expires in 24h). Share it out-of-band:
  mcpmesh-invite:…
Whoever redeems it can access: notes

Next: send them that line over any channel. They redeem it with `mcpmesh pair <line>`,
which prints a short safety code β€” run `mcpmesh status` to see yours and confirm the two
match, out loud. Same words = the pairing is authentic.

πŸ’‘ Every command tells you what to do next, so you can follow the whole flow without the docs open.

Alice sends that mcpmesh-invite:… line to Bob over any channel he can paste from β€” chat, email, a shared doc. (It's a long line, a couple of KB of text β€” not one to copy by hand.) The channel doesn't need to be secure: tampering with the invite makes the next step's code mismatch, and the invite is one-time and expiring.

2️⃣ Bob redeems the invite

bob$ mcpmesh pair mcpmesh-invite:…
Paired with alice β€” code: tango-fig-cabbage
Next: confirm this code matches what alice sees, out loud (they see it under `mcpmesh status`).
Same words = the pairing is authentic.

You can now use: alice/notes

Tools from alice run on alice's machine. Treat their output as you would anything else alice sends you.

To use in Claude Code, run:
  claude mcp add alice-notes -- mcpmesh connect alice/notes

To use in Claude Desktop, add this under "mcpServers" in
  ~/Library/Application Support/Claude/claude_desktop_config.json
then quit and restart Claude Desktop:
  "alice-notes": {"command": "mcpmesh", "args": ["connect", "alice/notes"]}

Any other MCP client: add a stdio server with the command `mcpmesh connect alice/notes`.

One redemption pairs the two machines both ways πŸ”„ β€” each side now knows and trusts the other's device. Access stays one-way until granted: right now Bob can reach alice/notes, and Alice can reach nothing of Bob's.

ℹ️ "alice" here is Bob's nickname for Alice β€” suggested by the invite, local to Bob. Names in mcpmesh are always your names for your peers, never global identities.

3️⃣ Both sides confirm the code πŸ—£οΈ

Alice checks her side:

alice$ mcpmesh status
…
recent pairings (confirm the code with the other side):
  bob Β· code: tango-fig-cabbage Β· just now

Alice and Bob compare codes out loud (or over any channel they already trust). Same words on both screens = the pairing is authentic. βœ… A mismatch means someone tampered with the invite in transit β€” unpair and start over. ⚠️ Do this before using the service; it's the whole ceremony.

4️⃣ Bob mounts the service in his AI client

Bob follows the instruction pair already printed β€” one command for Claude Code:

bob$ claude mcp add alice-notes -- mcpmesh connect alice/notes

…or the pasted entry for Claude Desktop. (Lost the block? mcpmesh use alice/notes prints it again.) Alice's notes server now appears as a normal local MCP server, and Bob's AI can search it β€” every request travels the encrypted P2P session straight to Alice's machine. πŸŽ‰

πŸ“ Run that command as printed, with no --scope: it lands in Claude Code's default local scope and connects right away. Adding --scope project instead writes a checked-in .mcp.json, which Claude Code holds at pending approval until someone approves it interactively β€” a sensible guard against servers arriving through a shared repo, but it looks like the mount silently failed if you weren't expecting it.

mcpmesh prints these steps rather than editing your AI client's config for you: it's your config, and a line you pasted yourself is one you can find and remove later.

5️⃣ Bob shares something back

The same three verbs in the other direction β€” pairing is already mutual, so Bob just serves, invites, and Alice redeems:

bob$   mcpmesh serve code -- npx -y @modelcontextprotocol/server-filesystem ~/projects
bob$   mcpmesh invite code

alice$ mcpmesh pair mcpmesh-invite:…       # Bob's invite this time
Paired with bob β€” code: quartz-melon-drift
Next: confirm this code matches what bob sees, out loud …

You can now use: bob/code
…
alice$ claude mcp add bob-code -- mcpmesh connect bob/code   # the command `pair` just printed

βž• Grants accumulate β€” this second pairing adds bob/code to what Alice may dial without touching what Bob already had, and nobody's chosen names for their peers change. Repeat with more people and more services; every grant is explicit and per-service.

6️⃣ See and undo everything

mcpmesh status               # what you serve, to whom; which peers are online (+ RTT); recent pairings; next steps
mcpmesh use alice/notes      # the exact steps to mount a peer's service in your AI client
mcpmesh doctor               # local, read-only health check of this machine's setup
mcpmesh pair --remove alice  # unpair: alice loses access to YOUR services from now on

πŸ“‘ status shows a reachability line per paired peer β€” online or offline, with a round-trip time β€” so you can see who's actually up before you dial. It's advisory and on-demand (a peer that's off just reads offline; it never blocks a dial), and it works both ways once you're paired.

🧭 status also closes with a next steps block β€” the exact command for whatever this machine can do from where it is (share something, invite someone to a service nobody can reach yet, redeem an invite, or mount a peer's service). It's silent when there's nothing to nudge.

🏒 For teams: roster mode

Pairing is person-to-person. Roster mode layers team management on the same machinery: an operator creates an org (mcpmesh org create), members join with mcpmesh join, and membership lives in a signed roster file you can host on any static web host β€” the signature is the trust boundary, so the host, the network, and the gossip layer are all untrusted transport. Revoking a member updates the roster and severs their live sessions everywhere. The full ceremony (including the fingerprint confirmations that make it safe) is in the πŸ“˜ operator runbook.

🧩 What's underneath

Piece What it is
mcpmesh (CLI + daemon) The binary: porcelain verbs above, plus a per-user daemon it auto-starts
mcpmesh-net The transport kernel: iroh/QUIC sessions, accept-time trust gating
mcpmesh-trust Keys, signed rosters, device→user bindings — no network code
mcpmesh-codec The one NDJSON frame codec both ends of every wire share
mcpmesh-local-api The local control API (mcpmesh-local/1) clients and plugins build on

πŸͺͺ Identity is self-sovereign: devices hold keys that never leave the machine, people are verified deviceβ†’user bindings, and the names you see are nicknames you chose. By default connections bootstrap via iroh's public relays; relay_mode = "custom" / discovery_mode = "custom" in the config let you self-host both, and "disabled" runs fully hermetic (LAN/localhost only). mcpmesh doctor validates whatever combination you configure.

βš–οΈ The trust boundary, in one line: mcpmesh authenticates who you're talking to and encrypts the pipe β€” it does not vet what a peer's MCP server says. Treat tool output from a peer like any content from that person.

πŸ› οΈ Building on mcpmesh

To share a tool, you write an ordinary MCP server β€” the same artifact you'd hand to Claude Desktop β€” and serve it. That works in any language with an MCP SDK, with no mcpmesh code: the mesh hands your server the cryptographically verified caller identity per call (an env var for a spawned server, an initialize _meta field for a warm one), so you can scope what you disclose to who is asking.

To drive the mesh β€” a GUI, a launcher, a plugin daemon β€” talk to the per-user daemon over mcpmesh-local/1: newline-delimited JSON over a same-user local endpoint (a Unix socket on macOS/Linux, a named pipe on Windows), simple enough to implement in any language. The mcpmesh-local/1 protocol spec documents the framing, the handshake, every method, and the identity contract. Rust clients can depend on the mcpmesh-local-api crate directly instead. Runnable samples of both β€” the typed Rust client and a dependency-free ~60-line Python client proving the any-language claim β€” live in local-api/examples/.

πŸ“Ί For a live view of the mesh, the daemon exposes an event stream (subscribe): an initial snapshot of active sessions and peer reachability, then per-event telemetry β€” sessions opening and closing, per-request latency and byte counts, trust changes β€” as it happens, enough to render the mesh in real time. mcpmesh internal watch is a thin reference consumer you can run to watch the stream in a terminal; the protocol spec has the frame shapes.

πŸ’» Platform support

macOS, Linux, and Windows 10+ (x86_64 / aarch64). Rust β‰₯ 1.95 to build from source. On Windows, the local control plane uses a per-user named pipe with an owner-only DACL instead of a Unix socket β€” same-user-only access, enforced by the kernel either way.

🚦 Status

Pre-release. The wire protocol and config format can still change without migration paths. πŸ” Security reports: knotanotsea@protonmail.com.

πŸ“„ License: MIT OR Apache-2.0.