Agents
Four tools locally, and one agent calling another device's tools through the pairing.
There are two separate things here and they are easy to confuse.
Locally, Sparsh gives your agent four tools for moving files to devices you have paired with. Remotely, Sparsh carries MCP itself, so an agent on one machine can call the tools a server on another machine exposes, with no port, no VPN and no account.
Locally: four tools
claude mcp add sparsh -- sparsh mcp-toolssparsh_status, sparsh_devices, sparsh_send and sparsh_inbox. The
reference is generated from the running server, so it cannot
drift from what your agent actually sees.
Two rules shape them:
Tools return paths, never bytes. That is the MCP maintainers' own position and it is obvious at these sizes: a 1.59 GB file base64'd into a tool result is a context-window fire, not a feature.
There is no pairing tool. An agent must never pair itself, and the surest way to guarantee that is to give it no verb for it.
Sending a file from inside the working directory goes straight through. Anything
outside it asks you first, because send(path) is "read any file and ship it",
which is exactly what a prompt-injected agent reaches for. SPARSH_ALLOWED_ROOTS
widens what counts as inside.
Remotely: MCP over the wire
On the device that has the server, a human grants one pairing the right to drive one command:
sparsh expose "Mac · Claude Code" -- npx -y @modelcontextprotocol/server-filesystem ~/DocumentsOn the device that wants it:
claude mcp add homebox -- sparsh mcp "Home box"That is all. The real MCP client and the real MCP server now talk to each other.
How it actually works
The two daemons are a transparent bridge, not a proxy. A fourth data channel opens on the session that is already warm, carrying MCP's own stdio framing: newline-delimited JSON-RPC, exactly what a local server speaks. Neither end knows Sparsh is there, so protocol negotiation, resources, prompts, progress and cancellation all pass through unchanged, now and in the next spec revision.
Measured: a tools/call through the whole chain is
0.67 ms median, p95 1.15 ms, on
loopback. The raw channel round trip is 0.21 ms, so the
four process hops cost about half a millisecond between them.
Who may
Identity is the connection. The peer is the key its SDP was signed with, and WebRTC checks the DTLS certificate against the fingerprint inside that SDP. There is no token to steal and no header to forge.
Authorisation is one grant, made by a human. No pairing has it by default.
The grant names one server, not a shell. sparsh unexpose <device> takes it back
and closes any live channel immediately.
Two requests never cross toward you. sampling/createMessage, so a remote
server cannot spend your model, and roots/list, so it cannot read your project
layout.
Every call leaves one audit line with the peer, the tool and the size. Never the arguments: those are your data and routinely contain secrets.
What it cannot protect you from
A remote server's replies are text in your agent's context, exactly as with any third-party MCP server. The device is authenticated; the agent on it may still be prompt-injected. Your MCP client's own approval prompts are the human in the loop, and they apply here as anywhere.
Read Security before exposing anything.