vibe-sync-mcp
v0.1.3
Published
VibeSync MCP server — zero-knowledge sync + shared agent memory for AI coding agents. OAuth login, encrypted push/pull, plain-English summaries.
Maintainers
Readme
vibe-sync-mcp
The VibeSync MCP server — a local, zero-knowledge sync layer for AI coding agents. Two or more people can build a project together through their agents (Claude Code, Cursor, Qwen Code, …) without ever touching git: the agent creates projects, invites teammates, and pushes/pulls encrypted snapshots on the user's behalf.
Everything is encrypted on-device before it leaves the laptop. The VibeSync servers only ever see ciphertext, wrapped keys, and account metadata.
- Site: https://getvibesync.vercel.app
- Companion skill: install the agent rulebook with
npx vibe-sync-skill install(npm packagevibe-sync-skill).
Quickstart
Add the MCP server to your agent config:
{
"mcpServers": {
"vibe-sync": {
"command": "npx",
"args": ["-y", "vibe-sync-mcp"]
}
}
}Then in your agent session:
vibe_init— creates the VibeSync repo in the current folder:.vibesync/with the shared memory space (user.md,memory.md,chat.md,session.md). Always the first step, likegit init.vibe_login— a browser window opens to the VibeSync consent screen; click Authorize. Tokens are stored at~/.vibesync/auth.jsonand silently refresh forever. This also generates this device's keypair (~/.vibesync/identity.json) and registers it to your account.vibe_link—create: "<name>"for a brand-new project, orjoin: "<invite link or code>"to join a teammate's.- Declare yourself once in
user.md(vibe_memory_write), then build.
From then on the agent follows the vibe-sync skill rules (npx vibe-sync-skill install):
check vibe_status + read memory at session start, document unconventional changes
in memory.md, offer vibe_push at natural pauses, vibe_pull for teammate changes,
and report memory findings to the user after each push/pull.
Updating: the default config (npx -y vibe-sync-mcp) fetches the latest version
on every agent start — just restart the agent window (or reload the MCP connections).
Re-run npx vibe-sync-skill@latest install to refresh the skill rulebook. Check the
current versions with npm view vibe-sync-mcp version and npm view vibe-sync-skill version.
⚠️ Back up
~/.vibesync/identity.jsonsomewhere safe. It is the only thing that can decrypt this device's projects. Losing it means losing access to the team's history — nobody, not even VibeSync, holds a spare.
Tools
Every tool returns a plain-English summary (no raw JSON) that the agent passes through.
| Tool | Purpose |
|---|---|
| vibe_init | First step, always. Creates .vibesync/ with the memory space (user.md, memory.md, chat.md, session.md) + ensures device identity. |
| vibe_link | After init: create a new project from this folder, or join one via invite link/code. |
| vibe_login | One-time OAuth login (PKCE, loopback redirect); registers this device. |
| vibe_whoami | Logged-in user, device, and project list. |
| vibe_projects | All projects with member names + last activity. |
| vibe_memory_list | The memory space: files present + which agents self-signed in user.md. |
| vibe_memory_read | Read a memory file (user, memory, chat, session, all). |
| vibe_memory_write | Write a memory file (read + merge first — shared files sync to everyone). |
| vibe_chat | Agent-to-agent chat: post a timestamped signed message, or read the log. |
| vibe_invite | Creates a single-use 7-day invite; returns link + code. |
| vibe_create_project | (legacy alias of vibe_link create) Creates a project; links the folder. Folder must be init'ed first. |
| vibe_join | (legacy alias of vibe_link join) Joins via code or link. Folder must be init'ed first. |
| vibe_status | Behind-count, plain-English changelog summary, and auto-completes pending key wraps with a one-line report. |
| vibe_push | Encrypts changed files, uploads only new blobs, writes an encrypted manifest (your note becomes the changelog). |
| vibe_pull | Applies safe changes, three-way-merges overlapping text edits, snapshots pre-merge state first, reports conflicts. |
| vibe_resolve | Submits the merged version of a conflicted file; "@keep-ours" / "@take-theirs" for binary conflicts. |
| vibe_rollback | Restores the working directory from the newest local checkpoint or a server snapshot. |
| vibe_usage | Current quota usage: live project count and storage used, vs the free limits. |
| vibe_leave | Leaves the current folder's project (frees a slot if you're at the limit). |
| vibe_delete_project | Soft-deletes the current folder's project (owner only); frees everyone's quota for it. |
| vibe_logout | Revokes this device's server sessions and clears local login. |
Never synced, ever: .env / .env.*, node_modules, .git, dist, build,
.next/, .DS_Store, and everything in .vibesync/ except the shared memory
files (.vibesync/memory/{user,memory,chat}.md, which sync with the project;
session.md stays local).
Shared memory & team chat
Every repo carries a shared memory space created by vibe_init at
.vibesync/memory/. These files are part of the synced tree — encrypted and pushed
with the code, so a teammate's agent pulls your memory along with your changes:
user.md— the roster. Every agent self-signs once, so everyone knows who is working and can coordinate.memory.md— persistent project memory. Agents document unconventional decisions (what changed, why, how), so a teammate's agent already has the context and doesn't waste tokens reverse-engineering it.chat.md— agent-to-agent chat across push/pull. Timestamped, signed messages for notes, task handoffs, and questions (@agent-name,@human).session.md— this device's scratch notes; local-only, never synced.
The skill makes this automatic: read memory at session start and after every pull, document unusual changes before pushing, report findings to the user, and route delegation through the user's explicit permission.
How it works
- Identity: each device generates an X25519 keypair (libsodium) at first login. The private key never leaves the device.
- Team keys: every project has a random 32-byte team key, sealed individually to each member device's public key and stored server-side as wrapped keys only.
- Snapshots: a push diffs the working tree against the last pulled snapshot,
encrypts changed files (secretbox), uploads only blobs whose ciphertext hash is
new, and writes an encrypted manifest whose
parent_idsreference what this device last pulled. - Merges: a pull classifies every differing file against the common ancestor
(auto-apply their-only changes, keep your-only changes, three-way-merge overlapping
text via
node-diff3). Genuine conflicts are reported with both sides' content so the agent can explain them and propose a resolution. Pre-merge checkpoints make every pull undoable withvibe_rollback.
OAuth login flow
- The server starts an ephemeral HTTP listener on
127.0.0.1(OS-assigned port). - It opens your browser to
/oauth/authorize?client_id=vibe-sync-mcp&redirect_uri=http://127.0.0.1:{port}/callback&response_type=code&code_challenge={S256}&code_challenge_method=S256&state={random}&scope=sync. - After you approve, the site redirects to the loopback; the server verifies
state, exchanges the code (PKCE verifier) for tokens, registers this device's public key, fetches your profile, and storesAuthStatein~/.vibesync/auth.json. - Token refresh is transparent: on a 401 the client refreshes via the OAuth refresh-token grant and retries once, persisting the new tokens.
Override the API base URL for development with VIBESYNC_URL (default:
https://getvibesync.vercel.app). VIBESYNC_HOME (default ~/.vibesync) and
VIBESYNC_SKILL_DIR are honored for tests/tooling.
Contract extensions (needed from the web API)
The frozen contract's key-wrapping surface is upload-only, but the MCP server needs
two read-side pieces. Both are one-line additions for apps/web; the orchestrator
reconciles them:
GET /api/projects/:id/keys/:deviceId→{ wrapped_key }Returns this device's wrapped team key (the row fromdevice_project_keys) so the MCP server can unwrap it locally. Server rule: active member only. Without it,vibe_status/vibe_pull/vibe_pushcannot obtain the team key.GET /api/projects/:id/keys/pendingresponses includepublic_keyper request ({ device_id, device_name, user_id, display_name, public_key }). Without the joining device's public key, an existing member cannot seal the team key to it, and the join/key-wrap queue stalls.
Both are used from src/api-ext.ts; see that file for the exact shapes.
Development
npm run build -w packages/mcp # tsup -> dist/ (esm + d.ts)
npm run typecheck -w packages/mcp # tsc --noEmit
npm run test -w packages/mcp # vitest (unit tests, no network)Tests cover the fs scan (vibeignore + always-excluded), checkpoint create/restore, state save/load round trips, and PKCE derivation. Live API tests need a running backend and are intentionally not part of the unit suite.
