Most people start with one machine. Eventually a second machine appears — a laptop alongside the desktop, a personal Mac alongside a work one, a home setup alongside a travel setup. The question becomes: how do I work in the same Claude Code workspace from both? What follows is a clear separation of what syncs cleanly via git, what stays local on each machine, and what needs a deliberate strategy because it falls between the two.
The mental model
A Claude Code workspace has three layers of state, and each behaves differently across machines:
- Workspace files — the actual content you're working on, plus committed configuration like
.claude/settings.json,.claude/CLAUDE.md, skills, commands. These sync via git like any other code. - Per-machine configuration — MCP server connections, API keys, OS-specific paths, anything that depends on the local environment. These do not sync; each machine has its own.
- Conversation history — the JSONL files Claude Code writes for every session, plus any local memory or transcripts. These are local-only by default; making them available across machines requires deliberate work.
Most multi-machine pain comes from confusing layer 2 with layer 1 — committing per-machine config to git — or assuming layer 3 syncs when it doesn't.
Layer 1 — what to commit
The committed layer of a workspace should describe how Claude Code behaves in this workspace, regardless of which machine it's running on. That includes:
.claude/CLAUDE.md— the workspace's master instructions.claude/rules/*.md— auto-loaded rules.claude/settings.json— the workspace's settings layer.claude/skills/*— skills you want available on both machines.claude/commands/*— slash commands.claude/agents/*— agent definitions.mcp.jsonif you use workspace-scoped MCP servers and the secrets can be expanded from per-machine env vars
Everything in this list is intentionally portable: it describes what the workspace expects, not what any particular machine has installed.
Layer 2 — what to keep local
The per-machine layer holds anything that depends on the operating system, installed software, or credentials specific to that machine. The cleanest way to keep these separate is to put them in .claude/settings.local.json (which the standard Claude Code .gitignore excludes) and let them override the committed settings when present.
What goes here:
- OS-specific paths (a Windows machine's path to a tool will differ from a Mac's)
- Permission allowlists for paths that exist on one machine but not another
- Per-machine MCP servers that you don't want to push to teammates
- API keys and tokens — never commit these, regardless
Settings precedence runs from most-specific to least: settings.local.json overrides settings.json in the workspace, which overrides ~/.claude/settings.json at the user level. Array values like permission allowlists merge across layers rather than replacing — useful when each machine adds a few of its own.
Suggested .gitignore entries for any Claude Code workspace:
.claude/settings.local.json
.claude/state/state.local.json
.claude/projects/
.env
.env.local
.prima-memory/
Layer 3 — conversation history and session continuity
Claude Code stores every session's full conversation as a JSONL file under ~/.claude/projects/[encoded-workspace-path]/, where the encoded path replaces forward slashes and spaces with hyphens. This directory is on your home directory, not in the workspace, so git doesn't see it — and Claude Code doesn't sync it across machines.
Practical consequences:
- If you start a session on Machine A and switch to Machine B, Machine B has no record of what happened on A
--resumeonly sees sessions from the machine you're on- Searching across past conversations is per-machine
There are three rough strategies for closing the gap.
The first is to do nothing — accept that each machine has its own history and rely on commits, CLAUDE.md updates, and project state files (rather than chat history) as the durable record of what was done. For most use, this is fine — the workspace is the source of truth, the conversation is the means of editing it. Treat conversations as ephemeral by default.
The second is to capture state at session boundaries. End each session by writing a checkpoint or session summary to a tracked file in the workspace — what was done, what's pending, where to pick up. Read it on the other machine. This is what tools like the /save and /resume patterns in PRIMA workspaces do, and what a manually maintained "session log" in the workspace can do without any tooling.
The third is to use a session-history MCP server (PRIMA Memory is one example) that indexes past sessions and exposes them via tools Claude can call. This brings cross-session search but does not by itself sync history across machines — each machine still indexes its own JSONL files. Sharing the index requires syncing the underlying database file, which is workable but a deliberate step.
Pick the strategy that matches how disposable your conversations actually are. Most people overestimate how often they need full conversation replay across machines.
OAuth and per-machine re-authentication
MCP servers that use OAuth — the official Google Drive MCP, GitHub via OAuth, anything browser-based — store credentials in your operating system's credential store (macOS Keychain, Windows Credential Manager, Linux Secret Service). These credentials are by design not portable: the whole point of the credential store is that secrets stay on the machine they were created on.
What this means in practice:
- Each machine runs its own OAuth flow the first time it uses an OAuth-protected MCP server
- You'll see the browser open, you'll authorise, you'll come back to a connected server — once per machine, per OAuth-protected server
- A user-level MCP registration (
~/.claude.json) on Machine A doesn't transfer to Machine B, even if the workspace is the same
This is the right behaviour, even if it adds friction. A stolen laptop with synced OAuth tokens is a much worse outcome than re-authenticating per machine.
Static-key MCP servers (API keys, tokens)
For MCP servers configured with a static API key — most non-OAuth servers — you have two clean options:
Option A: per-machine config in ~/.claude.json. Each machine runs claude mcp add separately with its own key. Nothing syncs; nothing leaks; the cost is doing it on each machine. This is the right answer for personal API keys.
Option B: workspace config with env-var expansion in .mcp.json. The committed .mcp.json references ${API_KEY}, and each machine sets API_KEY in its shell profile or in .env.local (not committed). The workspace config is portable; the secret is not. This is the right answer for team workspaces where everyone connects to the same service with their own credentials.
The minimum viable two-machine setup
- Push the workspace to git from Machine A; clone on Machine B
- On Machine B, run
claude mcp addfor any MCP servers configured at user level on Machine A - For OAuth servers, complete the auth flow on Machine B
- For any path-specific entries in
~/.claude/settings.json, mirror them on Machine B with corrected paths - Decide whether you need conversation continuity across machines or not — and if you do, set up checkpoint files or a session-history MCP, but do this last, not first
Most people who think they need full session sync actually need step 4 done well. A good CLAUDE.md and tracked state files give you continuity without trying to make every JSONL portable.
