claude-desktop-to-code
Launches Claude Code sessions in the Alacritty terminal, allowing a headless session to be taken over in a real terminal window.
Launches Claude Code sessions in iTerm2 via AppleScript, allowing a headless session to be taken over in a terminal window.
Launches Claude Code sessions in WezTerm using its CLI, allowing a headless session to be taken over in a terminal window.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@claude-desktop-to-codeAsk Claude Code to review the latest commit in my scratch profile"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
claude-desktop-to-code
An MCP server that routes work from a Claude Desktop chat into Claude Code, either headlessly, or by opening a terminal you can take over.
The point is the handoff. You ask a question in chat, it runs headlessly in the right directory, and when you want to drive it yourself the same session opens in a real terminal with its history intact.
Profiles
A profile is a directory containing a CLAUDE.md. That is the whole definition. Each
one has its own instructions and its own folder-scoped memory, so "which profile" is
really "which agent".
Everyone organises these differently, so setup reads three layers rather than assuming
one convention:
Names you already chose. If your shell rc defines a
CLAUDE_PROFILESmap, those names and paths are used as-is and always survive--limit. Nobody has to adopt anyone else's naming scheme. The written list is alphabetical, not ranked.Where you actually work.
~/.claude.jsonrecords every directory you have run Claude Code in and when, so the list is ranked by real use rather than by guesswork. Worktrees, temp dirs, and$HOMEare filtered out.A filesystem scan, for a machine with no history yet.
Each layer degrades to the next, so a fresh install with no conventions at all still
produces a sensible list. --limit caps how many are kept (12 by default).
Every tool runs inside a named profile. There is no free-form path parameter. A directory that is not in your config cannot be touched.
Related MCP server: claude-tmux-bridge
Install
Paste this repo's URL to your coding agent and say "install this". It will read AGENTS.md, work out your profiles, show them to you, and ask before writing anything.
By hand:
git clone https://github.com/abhaymettu/claude-desktop-to-code
cd claude-desktop-to-code
python3 -m venv .venv && .venv/bin/pip install -e .
.venv/bin/python -m cc_handoff setup --dry-run # see the plan first
.venv/bin/python -m cc_handoff setup --default <one of your profiles>
.venv/bin/python -m cc_handoff doctor # confirm it workssetup walks a few roots for CLAUDE.md files, detects which terminal you use and pins
it, writes ~/.config/cc-handoff/config.toml, and registers itself in
claude_desktop_config.json (backing up whatever was there). Restart Claude Desktop
afterward.
Re-running setup keeps every profile already in the file and only refreshes the
terminal and default. Pass --rescan to look for new profiles, which adds but never
drops. The file is rewritten rather than patched, so comments and any keys cc-handoff
does not know about are lost; keep notes elsewhere.
It over-collects on purpose. Open the toml and delete what you do not want:
default_profile = "scratch"
[profiles]
brain = "/Users/you/Documents/Brain"
scratch = "/Users/you/scratch"Other commands: cc-handoff profiles, cc-handoff terminals, cc-handoff doctor.
The console script is cc-handoff; python -m cc_handoff is equivalent.
setup --dry-run prints the plan and writes nothing. Add --json to either setup or
doctor for machine-readable output, which is what an agent driving the install uses.
doctor exits non-zero when something fatal is wrong.
Tools
Tool | What it does |
| Names, paths, and a one-line gist from each |
| Headless |
| Reopens that session in a terminal via |
| Writes |
allow_edits defaults to false and must be set deliberately.
Permissions do not survive the handoff
allow_edits applies only to the headless run. handoff_to_terminal resumes that
session with no permission flags, so once a window is open you are under ordinary
interactive Claude Code rules and approve actions yourself.
This is deliberate. A read-only headless answer becoming a read-only terminal would be
the wrong default: you are at the keyboard now, and the interactive permission prompt is
a better gate than a flag inherited from a chat message. But it does mean a restricted
headless call can be continued into an unrestricted session, so do not treat
allow_edits=False as a durable sandbox.
Terminals
Terminal | Status | How it launches |
Ghostty | tested |
|
Terminal.app | tested | AppleScript |
iTerm2 | tested | AppleScript |
kitty | untested | needs its CLI |
WezTerm | untested | needs its CLI |
Alacritty | untested | needs its CLI |
Untested means never run, not "probably works". Expect the three that need a CLI to
fail unless you have installed it: kitty, WezTerm and Alacritty ship their command
line tool inside the .app bundle and do not put it on PATH on their own. cc-handoff
looks on PATH first and then inside the bundle, and refuses to select a terminal it
cannot actually launch, so the failure is an error message rather than a hang.
cc-handoff looks for each .app in /Applications and ~/Applications, since casks
and manual installs land in either.
Auto-selection prefers tested recipes. An untested one is only chosen if it is the terminal you are currently running in, or if nothing tested is installed.
setup picks your terminal once and writes it to the config, so it is a decision you can
see and edit rather than a guess made on every call. Selection order at runtime: the
tool's terminal argument, then terminal in the config, then $CC_HANDOFF_TERMINAL,
then whatever is running (TERM_PROGRAM), then whatever is installed. macOS only for now.
One window per profile
open_in_claude_code checks whether a session is already running in that directory. If
one is, it updates HANDOFF.md and tells you to switch to that window and say "reread
.claude/HANDOFF.md" instead of stacking up another. Pass new_window=true to override.
Tabs are not an option: open -na has to start a separate instance to pass -e, plain
open -a ignores --args, Ghostty's +new-window action is Linux-only, and macOS
native tabbing cannot merge windows across instances.
Ghostty is launched with open -na Ghostty.app --args ... -e <command>, which is what
Ghostty's own help tells you to do: running the bundle binary directly is unsupported on
macOS, starts a fresh instance per call, restores every saved tab, and makes macOS
re-prompt for permission to exec the CLI each time.
--window-save-state=never is passed so a handoff window opens empty rather than
restoring an old session. Reusing an already-running instance is not possible. Plain
open -a accepts --args and silently ignores them.
Quoting
The brief never meets a shell. It is written with Path.write_text and never enters an
argv. Commands are built as argv lists and run with shell=False for four of the six
terminals; iTerm2 and Terminal.app are the exception, see below.
iTerm2 and Terminal.app are the exception: AppleScript takes a command string, not an
argv, so those recipes build cd <dir> && <command> with shlex.quote and then escape
the result for the AppleScript literal. That string is run by a shell.
tests/quoting_probe.py covers both halves. It pushes a hostile brief through the wire
and asserts the file on disk is byte identical, then takes ten hostile arguments,
including newlines, quotes, backslashes and AppleScript injection bait, generates the
real Terminal.app script, compiles it with osascript, and asserts the command comes
back unchanged. Reverting the escaper to a version that does not handle newlines makes
the probe fail, which is the point.
Environment
Variable | Default |
|
|
|
|
| auto-detected |
|
|
| unset; when set, terminal launches return the argv instead of opening a window |
Pointing CC_HANDOFF_CLI at another agent CLI mostly works, as long as it accepts
-p and --resume.
Tests
.venv/bin/python tests/run_all.py # protocol and quoting
.venv/bin/python tests/run_all.py --with-e2e # adds ask -> handoff, spends real tokensIndividually: stdio_probe.py does a real MCP handshake over a pipe to a subprocess,
quoting_probe.py covers the brief and the AppleScript escaping, and e2e_probe.py
runs a headless prompt and hands the session to a terminal. Only the last one costs
anything.
Relation to other projects
This is not a fork. It shares no code with any other project and was written from scratch.
The closest existing thing is steipete/claude-code-mcp
(JavaScript, MIT), which wraps the Claude Code CLI in a single claude_code tool for
one-shot delegation. If all you want is "let my MCP client run a Claude Code prompt",
use that. It is mature and widely used.
That server is not one-shot: its claude_code tool takes a sessionId, and repeated
calls with the same id resume the same session. Session continuity is not the
difference. The differences are:
Handing a session to a human. cc-handoff opens the session in a real terminal window with its history intact, so you stop being the relay between a chat box and your own machine. That is the whole reason this exists.
Profile routing. Work is addressed to a named directory rather than a path supplied per call, so "which agent" is a first-class argument and directories not in your config cannot be reached at all.
Permission default.
allow_editsis false unless asked for. That server defaults tobypassPermissionsfor backwards compatibility, with apermissionModeargument to opt out.
It is also Python rather than JavaScript, and macOS only, where that server is cross-platform. If you want a mature, cross-platform way to run a Claude Code prompt from an MCP client, use theirs.
Requirements
macOS, Python 3.10+, mcp (2.x or 1.x, the import is version-guarded), and the
claude CLI on PATH.
Available Tools
4 toolsask_claude_codeA
Run a prompt through Claude Code headlessly in a profile and return the answer.
Set allow_edits only when the user has asked for files to be changed. The returned session_id can be passed to handoff_to_terminal to continue the same session on screen.
| Name | Required | Description | Default |
|---|---|---|---|
| prompt | Yes | ||
| profile | No | ||
| allow_edits | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose the headless mode, that answers are returned, that a session_id comes back, and that edits only occur under an explicit flag. It omits permissions/auth requirements, what a profile implies, and any side-effect or concurrency caveats for a tool that can modify files.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences, front-loaded with the core action, followed by the two pieces of routing/behavioral guidance that matter most. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema and no annotations, so the description must stand alone; it covers the return value and the handoff flow but leaves the profile parameter and its interaction with list_profiles undefined. Adequate for a 3-parameter tool but with a visible gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains allow_edits well but leaves profile ('in a profile') and prompt unexplained, and the relationship to the list_profiles sibling is not clarified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Run a prompt through Claude Code headlessly') and the headless qualifier implicitly separates it from the screen-oriented sibling open_in_claude_code. An agent can identify the operation without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear conditional rule for allow_edits ('only when the user has asked for files to be changed') and explains the handoff path via session_id. It does not, however, state when to prefer this headless tool over open_in_claude_code or how profile selection relates to list_profiles.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
handoff_to_terminalC
Reopen a headless session from ask_claude_code in a real terminal window.
The session keeps its full history, so the user picks up exactly where the headless run left off.
| Name | Required | Description | Default |
|---|---|---|---|
| profile | No | ||
| terminal | No | ||
| session_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully discloses that the full session history is preserved, but omits what happens to the headless session (terminated or still live?), whether the terminal must be specified, and any permission or side-effect implications of a state transition.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the action and followed by the payoff. Little waste, though the second sentence is more justification than instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter state-transition tool with no annotations, no output schema, and zero parameter documentation, the description should explain the handoff mechanics and the meaning of profile/terminal. It covers purpose and history continuity but leaves the agent guessing about everything else.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for all three parameters (session_id, profile, terminal), and the description adds no semantics for any of them. profile and terminal are entirely unexplained in both places, so the description fails to compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb and resource: 'Reopen a headless session ... in a real terminal window,' and it ties the operation to the sibling tool ask_claude_code so the agent can place it in the workflow. It does not, however, distinguish itself from open_in_claude_code, which on name alone sounds like an overlapping handoff action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'picks up exactly where the headless run left off' implies the trigger case (continuing an ask_claude_code session) but never states an explicit when-to-use rule or why an agent would choose this over open_in_claude_code. Usage is inferable rather than instructed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_profilesA
List the profiles work can be routed to. Each is a directory with its own CLAUDE.md.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that each returned entry is a filesystem directory containing a CLAUDE.md, which tells the agent something about what gets returned, but says nothing about ordering, whether the list can be empty, permissions, or any side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the core action front-loaded and the explanatory gloss following it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-argument, read-only listing tool with no output schema, the description is nearly sufficient: it identifies the resource and gives a concrete sense of what each result entry is. Only minor detail about the return shape is absent, which is acceptable at this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4; there is nothing for the description to disambiguate beyond the resource itself.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) and resource (profiles) and adds a clarifying gloss: profiles are routing targets, each a directory with its own CLAUDE.md. It does not name or contrast with any of the sibling tools (ask_claude_code, handoff_to_terminal, open_in_claude_code), but the operation itself is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implicitly conveyed via the phrase 'profiles work can be routed to', which hints this is a discovery step before routing/handoff work. There is no explicit when-to-use statement, no prerequisites, and no mention of the related sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_in_claude_codeA
Write a brief to /.claude/HANDOFF.md and open Claude Code there.
Use for work the user should drive themselves. The brief is written verbatim; it is never passed through a shell.
If a session is already open in that profile, no second window is opened. Tell the user to switch to it and say "reread HANDOFF.md". Pass new_window to open one anyway.
| Name | Required | Description | Default |
|---|---|---|---|
| brief | Yes | ||
| profile | No | ||
| terminal | No | ||
| new_window | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the verbatim write, that the brief never goes through a shell, and the no-second-window behavior plus the new_window override. It stops short of saying what the call returns or what happens on success/failure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, then the selection rationale, then the mechanism and the session-collision rule. Every sentence carries distinct information and nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description could say more about the return value, and the terminal parameter is left unexplained despite 0% schema coverage. Otherwise it covers the action, safety behavior, and side-effect rules thoroughly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for 4 parameters. The description clarifies brief (written verbatim, not shell-interpreted) and new_window (forces a second window), and implies profile via the path template, but terminal is undocumented in both schema and description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resources: writes a brief to <profile>/.claude/HANDOFF.md and opens Claude Code there. Very clear what happens, but it never names or contrasts the siblings (ask_claude_code, handoff_to_terminal), so an agent must infer the boundary from the one usage hint.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use for work the user should drive themselves" gives a clear selection criterion and implicitly separates it from an agent-driven sibling, and the existing-session paragraph tells the user what to do next. No explicit when-not or named alternative, so it stops short of 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
ask_claude_code - First observed
handoff_to_terminal - First observed
list_profiles - First observed
open_in_claude_code
TDQS
Scored across 4 tools
The three Claude Code-invoking tools (ask_claude_code, handoff_to_terminal, open_in_claude_code) share the same underlying target, but their descriptions clearly separate headless execution, continuing a session on screen, and opening a user-driven window. A small amount of overlap remains between handoff_to_terminal and open_in_claude_code since both surface a window, but the intent differences are spelled out.
All names are lowercase snake_case starting with a verb (list_, ask_, handoff_, open_), which is readable and predictable. The pattern varies slightly between verb_noun and verb_preposition_noun forms, but that maps onto meaning rather than being arbitrary.
Four tools is well-scoped for a bridge server that routes and hands off work to Claude Code. Each tool covers a distinct step in the workflow and none feels redundant or superfluous.
The surface covers profile discovery, headless execution, session continuation, and user-driven handoff, which is a coherent lifecycle. Minor gaps exist around session/state management (e.g., listing or managing existing sessions or profiles), but core workflows are reachable.
Maintenance
Related MCP Connectors
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceThe self-hosted MCP bridge between Claude Chat and Claude Code.46AGPL 3.0
- AlicenseAqualityDmaintenanceAn MCP server that connects Claude Desktop to an interactive Claude Code session running in a tmux terminal.1MIT
- AlicenseAqualityAmaintenanceA minimal local MCP server that lets Claude Desktop or Claude Code delegate coding tasks to Grok Build running headless as a subagent.567 npm1MIT
- AlicenseNot gradedqualityBmaintenanceAn MCP server that bridges Claude Desktop with Claude Code, allowing users to delegate tasks to Claude Code directly from Claude Desktop conversations, supporting both synchronous and background execution with session reuse.3 npmMIT