@@ -66,6 +66,27 @@ def is_running_in_pytest() -> bool:
6666 return 'pytest' in sys .modules or 'py.test' in sys .argv [0 ]
6767
6868
69+ def is_debug_enabled () -> bool :
70+ """Check if debug logging is enabled via environment variable.
71+
72+ Returns:
73+ True if CLAUDE_CODE_TOOLBOX_DEBUG is set to '1', 'true', or 'yes' (case-insensitive)
74+ """
75+ debug_value = os .environ .get ('CLAUDE_CODE_TOOLBOX_DEBUG' , '' ).lower ()
76+ return debug_value in ('1' , 'true' , 'yes' )
77+
78+
79+ def debug_log (message : str ) -> None :
80+ """Log debug message if debug mode is enabled.
81+
82+ Args:
83+ message: Debug message to log
84+ """
85+ if is_debug_enabled ():
86+ # Use distinct prefix for easy filtering
87+ print (f' [DEBUG] { message } ' , file = sys .stderr )
88+
89+
6990# Windows UAC elevation helper functions
7091def is_admin () -> bool :
7192 """Check if running with admin privileges on Windows.
@@ -457,9 +478,13 @@ def find_bash_windows() -> str | None:
457478 Prioritizes Git Bash locations over PATH search to avoid
458479 accidentally finding WSL's bash.exe at C:\\ Windows\\ System32.
459480 """
481+ debug_log ('find_bash_windows() called' )
482+
460483 # Check CLAUDE_CODE_GIT_BASH_PATH env var first
461484 env_path = os .environ .get ('CLAUDE_CODE_GIT_BASH_PATH' )
485+ debug_log (f'CLAUDE_CODE_GIT_BASH_PATH={ env_path } ' )
462486 if env_path and Path (env_path ).exists ():
487+ debug_log (f'Found via env var: { env_path } ' )
463488 return str (Path (env_path ).resolve ())
464489
465490 # Check Git Bash common locations FIRST (before PATH search)
@@ -473,20 +498,28 @@ def find_bash_windows() -> str | None:
473498 os .path .expandvars (r'%LOCALAPPDATA%\Programs\Git\usr\bin\bash.exe' ),
474499 ]
475500
476- for path in common_paths :
501+ for i , path in enumerate ( common_paths ) :
477502 expanded = os .path .expandvars (path )
478- if Path (expanded ).exists ():
503+ exists = Path (expanded ).exists ()
504+ debug_log (f'Common path [{ i } ]: { expanded } - exists={ exists } ' )
505+ if exists :
506+ debug_log (f'Found via common path: { expanded } ' )
479507 return str (Path (expanded ).resolve ())
480508
481509 # Fall back to PATH search (may find Git Bash if installed elsewhere)
482510 bash_path = find_command ('bash.exe' )
511+ debug_log (f'PATH search result: { bash_path } ' )
483512 if bash_path :
484513 # Skip WSL bash in System32/SysWOW64
485514 bash_lower = bash_path .lower ()
486- if 'system32' not in bash_lower and 'syswow64' not in bash_lower :
515+ is_wsl = 'system32' in bash_lower or 'syswow64' in bash_lower
516+ debug_log (f'Is WSL bash: { is_wsl } ' )
517+ if not is_wsl :
518+ debug_log (f'Returning PATH bash: { bash_path } ' )
487519 return bash_path
488- # WSL bash found - don't use it, return None instead
520+ debug_log ( 'Skipping WSL bash' )
489521
522+ debug_log ('No suitable bash found, returning None' )
490523 return None
491524
492525
@@ -508,23 +541,42 @@ def run_bash_command(
508541 Returns:
509542 subprocess.CompletedProcess with the result
510543 """
544+ debug_log ('run_bash_command() called' )
545+ cmd_preview = command [:200 ] + '...' if len (command ) > 200 else command
546+ debug_log (f' command: { cmd_preview } ' )
547+ debug_log (f' capture_output: { capture_output } ' )
548+ debug_log (f' login_shell: { login_shell } ' )
549+
511550 if sys .platform == 'win32' :
512551 bash_path = find_bash_windows ()
513552 else :
514553 bash_path = shutil .which ('bash' )
515554
555+ debug_log (f'bash_path resolved to: { bash_path } ' )
556+
516557 if not bash_path :
517558 error ('Bash not found!' )
559+ debug_log ('ERROR: Returning early - bash not found' )
518560 return subprocess .CompletedProcess ([], 1 , '' , 'bash not found' )
519561
520562 args = [bash_path ]
521563 if login_shell :
522564 args .append ('-l' )
523565 args .extend (['-c' , command ])
524566
567+ debug_log (f'Executing: { args } ' )
568+
525569 try :
526- return subprocess .run (args , capture_output = capture_output , text = True )
527- except FileNotFoundError :
570+ result = subprocess .run (args , capture_output = capture_output , text = True )
571+ debug_log (f'Exit code: { result .returncode } ' )
572+ if capture_output :
573+ stdout_preview = result .stdout [:500 ] if result .stdout else '(empty)'
574+ stderr_preview = result .stderr [:500 ] if result .stderr else '(empty)'
575+ debug_log (f'stdout: { stdout_preview } ' )
576+ debug_log (f'stderr: { stderr_preview } ' )
577+ return result
578+ except FileNotFoundError as e :
579+ debug_log (f'FileNotFoundError: { e } ' )
528580 return subprocess .CompletedProcess (args , 1 , '' , f'bash not found: { bash_path } ' )
529581
530582
@@ -3502,6 +3554,9 @@ def configure_mcp_server(server: dict[str, Any]) -> bool:
35023554 # Windows HTTP transport - use bash for consistent cross-platform behavior
35033555 # This eliminates PowerShell's exit code quirks and CMD escaping issues
35043556 if system == 'Windows' :
3557+ debug_log (f'=== MCP Server Configuration: { name } ===' )
3558+ debug_log (f'claude_cmd: { claude_cmd } ' )
3559+
35053560 # Build explicit PATH including Node.js location
35063561 nodejs_path = r'C:\Program Files\nodejs'
35073562 current_path = os .environ .get ('PATH' , '' )
@@ -3516,6 +3571,10 @@ def configure_mcp_server(server: dict[str, Any]) -> bool:
35163571 unix_explicit_path = convert_path_env_to_unix (windows_explicit_path )
35173572 unix_claude_cmd = convert_to_unix_path (str (claude_cmd ))
35183573
3574+ debug_log (f'unix_claude_cmd: { unix_claude_cmd } ' )
3575+ path_preview = unix_explicit_path [:200 ] + '...' if len (unix_explicit_path ) > 200 else unix_explicit_path
3576+ debug_log (f'unix_explicit_path: { path_preview } ' )
3577+
35193578 env_flags = ' ' .join (f'--env "{ e } "' for e in env_list ) if env_list else ''
35203579 env_part = f' { env_flags } ' if env_flags else ''
35213580 header_part = f' --header "{ header } "' if header else ''
@@ -3526,7 +3585,12 @@ def configure_mcp_server(server: dict[str, Any]) -> bool:
35263585 f'--transport { transport } { header_part } "{ url } "'
35273586 )
35283587
3588+ bash_cmd_preview = bash_cmd [:300 ] + '...' if len (bash_cmd ) > 300 else bash_cmd
3589+ debug_log (f'First attempt bash_cmd: { bash_cmd_preview } ' )
35293590 result = run_bash_command (bash_cmd , capture_output = True , login_shell = True )
3591+ debug_log (f'First attempt result: returncode={ result .returncode } ' )
3592+ if result .returncode != 0 :
3593+ debug_log (f'First attempt failed! stdout={ result .stdout } , stderr={ result .stderr } ' )
35303594 else :
35313595 # On Unix, use bash with updated PATH (consistent with Windows)
35323596 parent_dir = Path (claude_cmd ).parent
@@ -3591,10 +3655,23 @@ def configure_mcp_server(server: dict[str, Any]) -> bool:
35913655 # Convert Windows path to Git Bash Unix-style path
35923656 unix_claude_cmd = convert_to_unix_path (str (claude_cmd ))
35933657
3658+ # Build explicit PATH including Node.js location (same as first attempt)
3659+ nodejs_path = r'C:\Program Files\nodejs'
3660+ current_path = os .environ .get ('PATH' , '' )
3661+ if Path (nodejs_path ).exists () and nodejs_path not in current_path :
3662+ windows_explicit_path = f'{ nodejs_path } ;{ current_path } '
3663+ else :
3664+ windows_explicit_path = current_path
3665+ unix_explicit_path = convert_path_env_to_unix (windows_explicit_path )
3666+
3667+ # Include PATH export in retry (same as first attempt)
35943668 bash_cmd = (
3669+ f'export PATH="{ unix_explicit_path } :$PATH" && '
35953670 f'"{ unix_claude_cmd } " mcp add --scope { scope } { name } { env_part_retry } '
35963671 f'--transport { transport } { header_part } "{ url } "'
35973672 )
3673+ bash_cmd_preview = bash_cmd [:300 ] + '...' if len (bash_cmd ) > 300 else bash_cmd
3674+ debug_log (f'Retry bash_cmd: { bash_cmd_preview } ' )
35983675 info (f'Retrying with bash command: { bash_cmd } ' )
35993676 result = run_bash_command (bash_cmd , capture_output = False )
36003677 else :
0 commit comments