@@ -2655,12 +2655,12 @@ def test_create_mcp_config_file_permission_error(self) -> None:
26552655
26562656 @patch ('platform.system' , return_value = 'Linux' )
26572657 @patch ('setup_environment.find_command_robust' , return_value = 'claude' )
2658- def test_configure_mcp_server_profile_scope_skipped (
2658+ def test_configure_mcp_server_profile_scope_removes_from_all_scopes (
26592659 self ,
26602660 mock_find : MagicMock ,
26612661 _mock_system : MagicMock ,
26622662 ) -> None :
2663- """Test that profile-scoped servers are skipped by configure_mcp_server ."""
2663+ """Test that profile-scoped servers are removed from all scopes but not added via claude mcp add ."""
26642664 del mock_find # Unused but required for patch
26652665 del _mock_system # Unused but required for patch
26662666
@@ -2670,13 +2670,29 @@ def test_configure_mcp_server_profile_scope_skipped(
26702670 'scope' : 'profile' ,
26712671 }
26722672
2673- # Should return True (success) but not actually configure
26742673 with patch ('setup_environment.run_command' ) as mock_run :
2674+ mock_run .return_value = subprocess .CompletedProcess ([], 0 , '' , '' )
26752675 result = setup_environment .configure_mcp_server (server )
26762676
26772677 assert result is True
2678- # run_command should NOT be called for profile-scoped servers
2679- mock_run .assert_not_called ()
2678+ # run_command should be called 3 times (once per scope) for removal
2679+ assert mock_run .call_count == 3
2680+
2681+ # Verify removal was attempted from all scopes
2682+ calls = mock_run .call_args_list
2683+ scopes_removed = set ()
2684+ for call in calls :
2685+ args = call [0 ][0 ] # First positional argument is the command list
2686+ # Verify it's a removal command
2687+ assert 'mcp' in args
2688+ assert 'remove' in args
2689+ assert 'profile-server' in args
2690+ # Extract scope
2691+ scope_idx = args .index ('--scope' ) + 1
2692+ scopes_removed .add (args [scope_idx ])
2693+
2694+ # Verify all three scopes were attempted
2695+ assert scopes_removed == {'user' , 'local' , 'project' }
26802696
26812697 @patch ('platform.system' , return_value = 'Linux' )
26822698 @patch ('setup_environment.find_command_robust' , return_value = 'claude' )
@@ -2840,3 +2856,68 @@ def test_configure_all_mcp_servers_mixed_scopes(
28402856 assert 'profile-2' in config ['mcpServers' ]
28412857 assert 'default-scope' not in config ['mcpServers' ]
28422858 assert 'explicit-user' not in config ['mcpServers' ]
2859+
2860+ def test_stale_mcp_config_file_removed_when_no_profile_servers (self ) -> None :
2861+ """Test that stale MCP config file is removed when no profile servers exist."""
2862+ with tempfile .TemporaryDirectory () as tmpdir :
2863+ config_path = Path (tmpdir ) / 'test-mcp.json'
2864+ # Create a stale MCP config file
2865+ config_path .write_text ('{"mcpServers": {"stale-server": {}}}' )
2866+ assert config_path .exists ()
2867+
2868+ # Configure with NO profile servers (only user-scoped)
2869+ servers = [
2870+ {'name' : 'global-server' , 'transport' : 'http' , 'url' : 'http://a.com' , 'scope' : 'user' },
2871+ ]
2872+
2873+ with patch ('setup_environment.configure_mcp_server' , return_value = True ):
2874+ success , profile_servers = setup_environment .configure_all_mcp_servers (
2875+ servers , config_path ,
2876+ )
2877+
2878+ # Verify stale file was removed
2879+ assert success is True
2880+ assert len (profile_servers ) == 0
2881+ assert not config_path .exists ()
2882+
2883+ def test_mcp_config_file_not_touched_when_path_none (self ) -> None :
2884+ """Test that no file operations occur when profile_mcp_config_path is None."""
2885+ servers = [
2886+ {'name' : 'server' , 'transport' : 'http' , 'url' : 'http://a.com' , 'scope' : 'user' },
2887+ ]
2888+
2889+ with patch ('setup_environment.configure_mcp_server' , return_value = True ):
2890+ success , profile_servers = setup_environment .configure_all_mcp_servers (
2891+ servers , None , # No profile config path
2892+ )
2893+
2894+ assert success is True
2895+ assert len (profile_servers ) == 0
2896+ # No assertion about file - just verify no crash
2897+
2898+ def test_stale_mcp_config_removal_failure_logged_as_warning (self ) -> None :
2899+ """Test that stale MCP config removal failure is logged as warning, not error."""
2900+ with tempfile .TemporaryDirectory () as tmpdir :
2901+ config_path = Path (tmpdir ) / 'test-mcp.json'
2902+ # Create a stale MCP config file
2903+ config_path .write_text ('{"mcpServers": {"stale-server": {}}}' )
2904+
2905+ servers = [
2906+ {'name' : 'server' , 'transport' : 'http' , 'url' : 'http://a.com' , 'scope' : 'user' },
2907+ ]
2908+
2909+ with (
2910+ patch ('setup_environment.configure_mcp_server' , return_value = True ),
2911+ patch .object (Path , 'unlink' , side_effect = OSError ('Permission denied' )),
2912+ patch ('setup_environment.warning' ) as mock_warning ,
2913+ ):
2914+ success , profile_servers = setup_environment .configure_all_mcp_servers (
2915+ servers , config_path ,
2916+ )
2917+
2918+ # Should still return success (non-fatal error)
2919+ assert success is True
2920+ assert len (profile_servers ) == 0
2921+ # Warning should have been logged
2922+ mock_warning .assert_called_once ()
2923+ assert 'Permission denied' in str (mock_warning .call_args )
0 commit comments