mcpmesh 0.3.0

Share MCP servers with people you trust — peer to peer, default-deny, no accounts
Documentation
<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/logo-dark.svg">
    <img src="docs/logo.svg" width="440" alt="mcpmesh">
  </picture>
</p>

<p align="center"><b>Share MCP servers with people you trust — peer to peer, no accounts, no cloud.</b></p>

mcpmesh lets your AI use your friends' tools. Take any
[MCP](https://modelcontextprotocol.io) server running on your machine — your notes, your code
search, your scripts — and share it with a specific person; their AI client (Claude Desktop, or
anything that speaks MCP) mounts it as if it were local. No hosting, no OAuth, nothing published
to the internet: an end-to-end-encrypted connection straight between your two machines.

Check out the [tech presentation](https://counterpunchtech.github.io/mcpmesh/presentation.html)
for a quick tour of what mcpmesh is and how it works.

## Quick start

One minute, two machines — call them `you` and `friend`:

```sh
cargo install mcpmesh    # on both machines (macOS/Linux)

# 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:…
```

`pair` prints the exact next steps: the safety code to confirm, and the one command that plugs 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 sides also see the same short code (like `tango-fig-cabbage`). Read it to each other —
matching words mean the pairing is authentic. That's it: your friend's AI can search your notes
over an encrypted peer-to-peer link, and the pairing works both ways when they want to share back.

## 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]https://iroh.computer/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 will share an MCP server with Bob; then Bob will share one back.
Install on both machines first (`cargo install mcpmesh` as above, or via Homebrew:
`brew tap counterpunchtech/mcpmesh https://github.com/counterpunchtech/mcpmesh && brew install --HEAD counterpunchtech/mcpmesh/mcpmesh`).

### 1. Alice serves an MCP 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:

```sh
# 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 flow without the docs open.

Alice sends that `mcpmesh-invite:…` line to Bob over any channel — chat, email, a sticky note.
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

```sh
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 petname 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:

```sh
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 instructions `pair` already printed — one command for Claude Code:

```sh
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:

```sh
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

```sh
mcpmesh status               # what you serve, to whom; what you can reach; recent pairings; what to do next
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` 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](docs/operator.md).

## 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
names you see are petnames *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](https://modelcontextprotocol.io) 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.

If you're instead building something that *drives* the mesh — a GUI, a launcher, a plugin daemon —
it talks to the per-user daemon over `mcpmesh-local/1`: newline-delimited JSON over a same-user Unix
socket, simple enough to implement in any language. The
[**`mcpmesh-local/1` protocol spec**](docs/local-protocol.md) documents the framing, the handshake,
every method, and the identity contract. Rust clients can depend on the
[`mcpmesh-local-api`](https://crates.io/crates/mcpmesh-local-api) crate directly instead.

## Platform support

macOS and Linux (x86_64 / aarch64). Rust ≥ 1.95 to build from source. Windows is not supported
(the local-socket security model is built on Unix peer credentials) and is not planned for v1.

## Status

Pre-release. The wire protocol and config format can still change without migration paths.
Security reports: security@runbolo.com.

License: MIT OR Apache-2.0.