3434SERVER_ARGS = [str (REPO_ROOT / "src" / "mcp_server.py" )]
3535
3636
37+ def get_mcp_server_executable () -> Optional [Path ]:
38+ """Get the path to the mcp-debug-server executable in the virtualenv."""
39+ system = platform .system ()
40+ if system == "Windows" :
41+ return REPO_ROOT / ".venv" / "Scripts" / "mcp-debug-server.exe"
42+ else :
43+ return REPO_ROOT / ".venv" / "bin" / "mcp-debug-server"
44+
45+
3746# ---------------------------------------------------------------------------
3847# Utilities
3948
@@ -80,7 +89,7 @@ def save_json(path: Path, data: Dict[str, object]) -> None:
8089
8190
8291# ---------------------------------------------------------------------------
83- # VS Code
92+ # VS Code (settings.json)
8493
8594
8695def vscode_candidate_paths () -> list [Path ]:
@@ -162,6 +171,68 @@ def vscode_remove(data: Dict[str, object]) -> None:
162171 del data ["mcp.servers" ]
163172
164173
174+ # ---------------------------------------------------------------------------
175+ # VS Code Workspace (.vscode/mcp.json)
176+
177+
178+ def vscode_workspace_config_path () -> Path :
179+ """Get the path to .vscode/mcp.json in the project root."""
180+ return REPO_ROOT / ".vscode" / "mcp.json"
181+
182+
183+ def vscode_workspace_get_entry (data : Dict [str , object ]) -> Optional [Dict [str , object ]]:
184+ servers = data .get ("servers" )
185+ if isinstance (servers , dict ):
186+ entry = servers .get ("agentDebug" )
187+ if isinstance (entry , dict ):
188+ return entry
189+ return None
190+
191+
192+ def vscode_workspace_update (data : Dict [str , object ]) -> None :
193+ """Update .vscode/mcp.json with the mcp-debug-server executable path."""
194+ servers = data .setdefault ("servers" , {})
195+ if not isinstance (servers , dict ):
196+ raise RuntimeError ("Expected 'servers' to be an object in .vscode/mcp.json" )
197+
198+ mcp_server = get_mcp_server_executable ()
199+ if not mcp_server or not mcp_server .exists ():
200+ raise RuntimeError (
201+ f"mcp-debug-server executable not found. Expected at: { mcp_server } \n "
202+ "Make sure you've installed mcp-debugpy in the virtualenv."
203+ )
204+
205+ # Use Path to construct the workspace-relative path - it will use OS-appropriate separators
206+ # Path relative to workspace folder
207+ system = platform .system ()
208+ if system == "Windows" :
209+ rel_path = Path (".venv" ) / "Scripts" / "mcp-debug-server.exe"
210+ else :
211+ rel_path = Path (".venv" ) / "bin" / "mcp-debug-server"
212+
213+ # Convert to string with forward slashes for VS Code (VS Code uses forward slashes on all platforms)
214+ command = "${workspaceFolder}/" + rel_path .as_posix ()
215+
216+ servers ["agentDebug" ] = {
217+ "type" : "stdio" ,
218+ "command" : command ,
219+ "args" : [],
220+ "cwd" : "${workspaceFolder}" ,
221+ }
222+
223+ # Ensure inputs array exists
224+ if "inputs" not in data :
225+ data ["inputs" ] = []
226+
227+
228+ def vscode_workspace_remove (data : Dict [str , object ]) -> None :
229+ servers = data .get ("servers" )
230+ if isinstance (servers , dict ) and "agentDebug" in servers :
231+ del servers ["agentDebug" ]
232+ if not servers :
233+ del data ["servers" ]
234+
235+
165236# ---------------------------------------------------------------------------
166237# Claude Desktop
167238
@@ -237,6 +308,53 @@ def claude_remove(data: Dict[str, object]) -> None:
237308 del data ["mcpServers" ]
238309
239310
311+ # ---------------------------------------------------------------------------
312+ # Claude CLI (.mcp.json in project root)
313+
314+
315+ def claude_cli_config_path () -> Path :
316+ """Get the path to the .mcp.json file in the project root."""
317+ return REPO_ROOT / ".mcp.json"
318+
319+
320+ def claude_cli_get_entry (data : Dict [str , object ]) -> Optional [Dict [str , object ]]:
321+ servers = data .get ("mcpServers" )
322+ if isinstance (servers , dict ):
323+ entry = servers .get ("agentDebug" )
324+ if isinstance (entry , dict ):
325+ return entry
326+ return None
327+
328+
329+ def claude_cli_update (data : Dict [str , object ]) -> None :
330+ """Update .mcp.json with the mcp-debug-server executable path."""
331+ servers = data .setdefault ("mcpServers" , {})
332+ if not isinstance (servers , dict ):
333+ raise RuntimeError ("Expected 'mcpServers' to be an object in .mcp.json" )
334+
335+ mcp_server = get_mcp_server_executable ()
336+ if not mcp_server or not mcp_server .exists ():
337+ raise RuntimeError (
338+ f"mcp-debug-server executable not found. Expected at: { mcp_server } \n "
339+ "Make sure you've installed mcp-debugpy in the virtualenv."
340+ )
341+
342+ servers ["agentDebug" ] = {
343+ "type" : "stdio" ,
344+ "command" : str (mcp_server ),
345+ "args" : [],
346+ "env" : {},
347+ }
348+
349+
350+ def claude_cli_remove (data : Dict [str , object ]) -> None :
351+ servers = data .get ("mcpServers" )
352+ if isinstance (servers , dict ) and "agentDebug" in servers :
353+ del servers ["agentDebug" ]
354+ if not servers :
355+ del data ["mcpServers" ]
356+
357+
240358# ---------------------------------------------------------------------------
241359# CLI helpers
242360
@@ -340,6 +458,45 @@ def process_vscode(args, python_path: Path) -> None:
340458 print (f"[VS Code] Updated agentDebug entry in { settings_path } " )
341459
342460
461+ def process_vscode_workspace (args ) -> None :
462+ """Process VS Code workspace (.vscode/mcp.json) configuration."""
463+ if args .vscode_workspace_action == "skip" :
464+ return
465+
466+ config_path = vscode_workspace_config_path ()
467+ config = load_json (config_path )
468+ existing = vscode_workspace_get_entry (config )
469+ action = prompt_action (args .vscode_workspace_action , existing , "VS Code Workspace" )
470+
471+ if action == "skip" :
472+ print ("[VS Code Workspace] Skipped." )
473+ return
474+ if action == "print" :
475+ print ("[VS Code Workspace] Current entry:" )
476+ print (json .dumps (existing or {}, indent = 2 ))
477+ return
478+ if action == "remove" :
479+ if existing is None :
480+ print ("[VS Code Workspace] No entry to remove." )
481+ return
482+ vscode_workspace_remove (config )
483+ save_json (config_path , config )
484+ print (f"[VS Code Workspace] Removed agentDebug entry from { config_path } " )
485+ return
486+
487+ # update
488+ try :
489+ vscode_workspace_update (config )
490+ save_json (config_path , config )
491+ print (f"[VS Code Workspace] Updated agentDebug entry in { config_path } " )
492+ except RuntimeError as exc :
493+ print (f"[VS Code Workspace] Warning: { exc } " )
494+ if is_interactive ():
495+ choice = input ("Continue anyway? [y/N] " ).strip ().lower ()
496+ if choice not in ("y" , "yes" ):
497+ raise
498+
499+
343500def process_claude (args , python_path : Path ) -> None :
344501 if args .claude_action == "skip" :
345502 return
@@ -369,6 +526,45 @@ def process_claude(args, python_path: Path) -> None:
369526 print (f"[Claude] Updated agentDebug entry in { config_path } " )
370527
371528
529+ def process_claude_cli (args ) -> None :
530+ """Process Claude CLI (.mcp.json) configuration."""
531+ if args .claude_cli_action == "skip" :
532+ return
533+
534+ config_path = claude_cli_config_path ()
535+ config = load_json (config_path )
536+ existing = claude_cli_get_entry (config )
537+ action = prompt_action (args .claude_cli_action , existing , "Claude CLI" )
538+
539+ if action == "skip" :
540+ print ("[Claude CLI] Skipped." )
541+ return
542+ if action == "print" :
543+ print ("[Claude CLI] Current entry:" )
544+ print (json .dumps (existing or {}, indent = 2 ))
545+ return
546+ if action == "remove" :
547+ if existing is None :
548+ print ("[Claude CLI] No entry to remove." )
549+ return
550+ claude_cli_remove (config )
551+ save_json (config_path , config )
552+ print (f"[Claude CLI] Removed agentDebug entry from { config_path } " )
553+ return
554+
555+ # update
556+ try :
557+ claude_cli_update (config )
558+ save_json (config_path , config )
559+ print (f"[Claude CLI] Updated agentDebug entry in { config_path } " )
560+ except RuntimeError as exc :
561+ print (f"[Claude CLI] Warning: { exc } " )
562+ if is_interactive ():
563+ choice = input ("Continue anyway? [y/N] " ).strip ().lower ()
564+ if choice not in ("y" , "yes" ):
565+ raise
566+
567+
372568# ---------------------------------------------------------------------------
373569# Entry point
374570
@@ -382,14 +578,26 @@ def build_parser() -> argparse.ArgumentParser:
382578 "--vscode-action" ,
383579 choices = ["prompt" , "update" , "remove" , "skip" , "print" ],
384580 default = "prompt" ,
385- help = "Action to perform for VS Code configuration" ,
581+ help = "Action to perform for VS Code (settings.json) configuration" ,
582+ )
583+ parser .add_argument (
584+ "--vscode-workspace-action" ,
585+ choices = ["prompt" , "update" , "remove" , "skip" , "print" ],
586+ default = "prompt" ,
587+ help = "Action to perform for VS Code Workspace (.vscode/mcp.json) configuration" ,
386588 )
387589 parser .add_argument (
388590 "--claude-action" ,
389591 choices = ["prompt" , "update" , "remove" , "skip" , "print" ],
390592 default = "prompt" ,
391593 help = "Action to perform for Claude Desktop configuration" ,
392594 )
595+ parser .add_argument (
596+ "--claude-cli-action" ,
597+ choices = ["prompt" , "update" , "remove" , "skip" , "print" ],
598+ default = "prompt" ,
599+ help = "Action to perform for Claude CLI (.mcp.json) configuration" ,
600+ )
393601 return parser
394602
395603
@@ -410,11 +618,32 @@ def main(argv: Optional[list[str]] = None) -> int:
410618 return 2
411619
412620 try :
413- process_claude (args , python_path )
621+ process_vscode_workspace (args )
414622 except RuntimeError as exc :
415623 print (exc , file = sys .stderr )
416624 return 3
417625
626+ try :
627+ process_claude (args , python_path )
628+ except RuntimeError as exc :
629+ print (exc , file = sys .stderr )
630+ return 4
631+
632+ try :
633+ process_claude_cli (args )
634+ except RuntimeError as exc :
635+ print (exc , file = sys .stderr )
636+ return 5
637+
638+ print ("\n Configuration complete!" )
639+ print (f" OS detected: { platform .system ()} " )
640+ print (f" Project root: { REPO_ROOT } " )
641+ mcp_server = get_mcp_server_executable ()
642+ if mcp_server and mcp_server .exists ():
643+ print (f" MCP server: { mcp_server } " )
644+ else :
645+ print (f" MCP server: Not found (expected at { mcp_server } )" )
646+
418647 return 0
419648
420649
0 commit comments