Bridging Claude Code CLI and Desktop: Sync Settings, MCPs, Sessions

You configured MCPs, skills, and settings in the CLI — then opened Desktop and found none of it. Here's the symlink strategy that bridges both clients without trial-and-error.

Claude Code CLI and Desktop run the same underlying agent engine but read configuration from different locations — which means MCPs, custom slash commands, and settings.json values you've configured in one client don't automatically appear in the other. The fix is symlinks: point Desktop's config files at ~/.claude/ so both clients share one source of truth. This guide walks through the exact commands, covers session transcript access, and explains when it makes more sense to commit to one client entirely.


TL;DR

Claude Code CLI stores global config in ~/.claude/settings.json, custom slash commands in ~/.claude/commands/, and session transcripts in ~/.claude/projects/. Desktop reads from a separate application config path (~/Library/Application Support/Claude/ on macOS). Symlinking Desktop's files to the ~/.claude/ equivalents keeps both clients in sync. There is no native session import option in Desktop today — the symlink approach is the current workaround.


Why Do CLI and Desktop Act Like Separate Products?

As one developer noted in r/ClaudeCode: "Desktop feels like a separate product rather than the same Claude Code in a GUI. MCPs and skills don't carry over." This isn't a regression — it's a config path problem that hasn't been bridged yet.

The complete guide to Claude Code surfaces describes the intended end state as: "Configuration is portable. CLAUDE.md, MCP settings, and subagent definitions follow you across surfaces." The gap between intent and current behavior is what the steps below address.

Root cause — path divergence:

Client Settings Custom commands Session transcripts
CLI ~/.claude/settings.json ~/.claude/commands/ ~/.claude/projects/<encoded-path>/<id>.jsonl
Desktop (macOS) ~/Library/Application Support/Claude/settings.json Desktop-specific path Desktop-specific path

Both clients write to their own paths. Neither reads the other's by default.


Prerequisites

  • Claude Code CLI installed and authenticated (claude --version returns cleanly)
  • Claude Code Desktop installed and opened at least once (to initialize its config directory)
  • macOS — Linux users substitute ~/.config/ equivalents where noted; Windows paths vary
  • Basic shell comfort: ln -s, jq, ls -la

Optional: Grass CLI (npm install -g @grass-ai/ide) — covered in the final section as an alternative that avoids this split entirely.


Step 1: Find Where Desktop Actually Reads Config

Before creating any symlinks, confirm Desktop's real config path on your machine.

# Common macOS paths to check
ls -la ~/Library/Application\ Support/Claude/
ls -la ~/Library/Application\ Support/Claude\ Code/

If you're not sure which file Desktop is reading, change one preference inside Desktop, then find what changed:

find ~/Library/Application\ Support/ -name "*.json" -newer ~/.claude/settings.json 2>/dev/null

The file with the updated modification time is Desktop's active config. Note that path — you'll use it in the next steps.


Step 2: Symlink settings.json

Back up Desktop's current settings, then replace the file with a symlink to the CLI's:

# Back up Desktop's settings first
cp ~/Library/Application\ Support/Claude/settings.json \
   ~/Library/Application\ Support/Claude/settings.json.bak

# Create the symlink
ln -sf ~/.claude/settings.json \
   ~/Library/Application\ Support/Claude/settings.json

Verify it:

ls -la ~/Library/Application\ Support/Claude/settings.json
# → ... -> /Users/you/.claude/settings.json

From this point, claude config set changes and manual edits to ~/.claude/settings.json are immediately visible to Desktop — and vice versa. Quit and relaunch Desktop after creating the symlink; it caches config on startup.


Step 3: Sync MCP Server Definitions

MCP servers are defined in the mcpServers key of settings.json. If you already symlinked settings.json in Step 2, your MCPs are shared — skip ahead to Step 4.

If Desktop uses a separate config file (some versions use claude_desktop_config.json), use a targeted sync script instead of a full symlink:

#!/bin/bash
# sync-mcps.sh — copy MCP definitions from CLI config into Desktop config

CLI_SETTINGS="$HOME/.claude/settings.json"
DESKTOP_CONFIG="$HOME/Library/Application Support/Claude/claude_desktop_config.json"

# Extract CLI mcpServers and merge into Desktop config
CLI_MCPS=$(jq '.mcpServers // {}' "$CLI_SETTINGS")
jq --argjson mcps "$CLI_MCPS" '.mcpServers = $mcps' "$DESKTOP_CONFIG" \
  > /tmp/desktop_merged.json && mv /tmp/desktop_merged.json "$DESKTOP_CONFIG"

echo "MCPs synced."

Run this after adding any new MCP server on the CLI side. Wire it to a shell alias (alias sync-mcps='~/scripts/sync-mcps.sh') so you don't forget.


Step 4: Share Custom Slash Commands (Skills)

CLI slash commands are .md files in ~/.claude/commands/ (global) or .claude/commands/ at the project root. If Desktop has its own commands directory, symlink it to the CLI's:

# Find Desktop's commands directory
ls ~/Library/Application\ Support/Claude/commands/ 2>/dev/null

# Symlink it to CLI's commands dir
rm -rf ~/Library/Application\ Support/Claude/commands
ln -sf ~/.claude/commands ~/Library/Application\ Support/Claude/commands

Any .md file you drop into ~/.claude/commands/ after this is immediately available as a slash command in both clients.


Step 5: Make CLI Sessions Visible in Desktop

This is the thorniest part. CLI session transcripts live at:

~/.claude/projects/<url-encoded-project-path>/<session-id>.jsonl

For a project at /Users/you/projects/myapp, the encoded path is:

~/.claude/projects/-Users-you-projects-myapp/

There is no native import option for CLI sessions in Desktop — users searching for it find nothing. The workaround is making Desktop read from ~/.claude/projects/ directly:

# Replace Desktop's session store with a symlink to the CLI's
rm -rf ~/Library/Application\ Support/Claude/projects
ln -sf ~/.claude/projects ~/Library/Application\ Support/Claude/projects

Relaunch Desktop. Sessions from the CLI will appear in Desktop's history because both clients are now reading the same .jsonl files. This restores the full conversation history — but not in-session UI state like open diffs or file viewers.


Step 6: Verify Everything

Run this to confirm all symlinks are in place:

for path in \
  "$HOME/Library/Application Support/Claude/settings.json" \
  "$HOME/Library/Application Support/Claude/commands" \
  "$HOME/Library/Application Support/Claude/projects"; do
  if [ -L "$path" ]; then
    echo "OK: $path -> $(readlink "$path")"
  else
    echo "WARNING — not a symlink: $path"
  fi
done

Then do a live check:

  1. Add a test MCP via CLI: claude mcp add test-server npx @modelcontextprotocol/server-filesystem /tmp
  2. Quit and relaunch Desktop
  3. Confirm the MCP appears in Desktop's MCP panel

Troubleshooting

Desktop ignores the symlink after relaunch

Some macOS apps resolve symlinks at launch and cache the resolved path. If changes aren't propagating:

  • Confirm the symlink target: readlink -f ~/Library/Application\ Support/Claude/settings.json
  • As a fallback, use a hard copy synced by a file watcher (fswatch ~/.claude/settings.json | xargs -I {} cp {} ~/Library/Application\ Support/Claude/settings.json)

MCP shows in CLI but not in Desktop

Desktop may require re-registering MCPs through its own UI even if the config file is shared. Register once inside Desktop — subsequent config changes via the shared file will propagate.

Write conflicts when both clients run simultaneously

Both clients writing to the same settings.json is last-write-wins. Avoid changing settings while both are open. For read-heavy usage (running agents, not changing config), simultaneous access is safe.

Cloud Desktop has even less parity

The community thread on cloud vs local Claude Code describes the cloud-hosted Desktop as "a very bare version" with no effort-level control or CLI parity. The symlink approach only works for the locally installed Desktop app — cloud Desktop doesn't expose a config filesystem you can target.


Which Client Should You Commit To?

If the symlink setup feels like maintenance overhead, pick a client and standardize on it.

Situation Recommended client
Heavy MCP usage, custom skills CLI — best config control today
Parallel sessions on different branches Desktop — built-in Git worktree isolation
Continue session from phone or browser Claude Code Remote Control (research preview) or Grass
Session must persist across machine sleep/close Remote VM + CLI (see below)
Agent-agnostic access from any surface Grass

How Grass Makes This Workflow Better

The CLI/Desktop config split exists because both clients are local and have no shared config layer. Grass takes a different approach: it exposes the CLI through a remote HTTP interface, so you're always talking to the same process — same ~/.claude/settings.json, same MCPs, same custom commands — regardless of what surface you use to reach it.

Run grass start in a project directory, and Grass wraps your existing CLI setup:

npm install -g @grass-ai/ide
cd ~/projects/myapp
grass start
# Scan the QR code from the Grass iOS app, or open the URL in any browser

Because there's only one running process, the config divergence problem doesn't arise:

  • MCPs work everywhere. Your ~/.claude/settings.json is the only config that matters — no Desktop-specific re-registration step.
  • Sessions survive disconnects. The CLI keeps running on your machine when you close the browser tab or app. Reconnecting replays buffered events from the exact point where you dropped off.
  • Permission requests forward to your phone. When the agent needs to run a bash command or write a file, the approval modal surfaces on your phone — no need to be at your laptop. This is especially useful when you're managing Claude Code's approval gates remotely across long-running tasks.
  • Agent-agnostic. Grass works with Claude Code and OpenCode from the same interface — your CLI config applies to both.

The Getting Started with Grass in 5 Minutes guide walks through the full setup. For teams running agents on cloud VMs rather than a local machine — which eliminates the laptop-tether as well as the config split — Grass also connects to Daytona workspaces so your agent keeps running even when your laptop is closed.


FAQ

Why don't MCP servers carry over from Claude Code CLI to Desktop?

Claude Code CLI reads MCP definitions from ~/.claude/settings.json. Desktop reads from a separate application config path that doesn't point to ~/.claude/ by default. Symlinking Desktop's settings.json to ~/.claude/settings.json resolves this — both clients share one mcpServers block.

How do I import existing Claude Code CLI sessions into the Desktop app?

There's no native import option. The workaround is symlinking Desktop's projects/ directory to ~/.claude/projects/ — after which CLI sessions appear in Desktop's session history automatically, because both clients read the same .jsonl transcript files.

Do custom slash commands sync between CLI and Desktop?

Only if both clients read from ~/.claude/commands/. If Desktop has its own commands directory, symlink it to ~/.claude/commands/ to share all .md skill files between clients.

What happens if CLI and Desktop both write to the same settings.json at the same time?

Last write wins — one client's changes may overwrite the other's. This is safe for read-heavy usage (running agents) but avoid changing settings while both clients are open simultaneously.

Is there a way to avoid the CLI/Desktop config split entirely?

Yes — either commit to one client, or use a tool that exposes the CLI through a remote interface. With Grass, there's one running CLI process with one ~/.claude/ config, reachable from phone, browser, or any machine on your network. The sync problem doesn't exist because there's nothing to sync.


Next Steps

If the symlink setup is working:

  • Version your ~/.claude/settings.json in a dotfiles repo — it's now the single source of truth for both clients, and worth tracking
  • Consider running Claude Code on a remote server so the agent isn't tied to your local machine at all — the config lives on the server and the client-split problem disappears with it

If you'd rather skip symlink maintenance entirely and reach your CLI session from anywhere, Getting Started with Grass in 5 Minutes shows how to get there in a single npm install.