π€ 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:
# π₯οΈ you β share a folder of notes as an MCP server, under a name you pick:
# π© friend β redeem the 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.
π§ͺ 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=/.mcpmesh-demo-friend; &&
# the "friend" serves a folder and mints a real invite under the scratch identityβ¦
HOME= XDG_RUNTIME_DIR=/run
HOME= XDG_RUNTIME_DIR=/run
# β¦and YOUR identity redeems it, exactly as a real friend would:
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, andmcpmesh pair --removecuts 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:
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):
)
π‘ 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
)
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 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:
β¦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 projectinstead 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:
β 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
π‘ 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.