diff --git a/pyls/language_server.py b/pyls/language_server.py
index cfd187b..ce11da6 100644
--- a/pyls/language_server.py
+++ b/pyls/language_server.py
@@ -37,7 +37,7 @@ def start_tcp_lang_server(bind_addr, port, handler_class):
         log.info("Serving %s on (%s, %s)", handler_class.__name__, bind_addr, port)
         server.serve_forever()
     except KeyboardInterrupt:
-        server.shutdown()
+        server.exit()
     finally:
         log.info("Shutting down")
         server.server_close()
@@ -113,7 +113,7 @@ class LanguageServer(MethodJSONRPCServer):
         self.shutdown()
 
     def m_exit(self, **_kwargs):
-        self.shutdown()
+        self.exit()
 
 
 _RE_FIRST_CAP = re.compile('(.)([A-Z][a-z]+)')
diff --git a/pyls/plugins/pycodestyle_lint.py b/pyls/plugins/pycodestyle_lint.py
index a0ee0c7..668096d 100644
--- a/pyls/plugins/pycodestyle_lint.py
+++ b/pyls/plugins/pycodestyle_lint.py
@@ -64,12 +64,6 @@ class PyCodeStyleDiagnosticReport(pycodestyle.BaseReport):
             'range': range,
             'message': text,
             'code': code,
-            'severity': _get_severity(code)
+            # Are style errors really ever errors?
+            'severity': lsp.DiagnosticSeverity.Warning
         })
-
-
-def _get_severity(code):
-    if code[0] == 'E':
-        return lsp.DiagnosticSeverity.Error
-    elif code[0] == 'W':
-        return lsp.DiagnosticSeverity.Warning
diff --git a/pyls/server.py b/pyls/server.py
index 9e3fcac..83f8002 100644
--- a/pyls/server.py
+++ b/pyls/server.py
@@ -14,23 +14,36 @@ class JSONRPCServer(object):
     def __init__(self, rfile, wfile):
         self.rfile = rfile
         self.wfile = wfile
+        self._shutdown = False
 
-    def shutdown(self):
-        # TODO: we should handle this much better
+    def exit(self):
+        # Exit causes a complete exit of the server
         self.rfile.close()
         self.wfile.close()
 
+    def shutdown(self):
+        # Shutdown signals the server to stop, but not exit
+        self._shutdown = True
+        log.debug("Server shut down, awaiting exit notification")
+
     def handle(self):
         # VSCode wants us to keep the connection open, so let's handle messages in a loop
         while True:
             try:
                 data = self._read_message()
                 log.debug("Got message: %s", data)
+
+                if self._shutdown:
+                    # Handle only the exit notification when we're shut down
+                    jsonrpc.JSONRPCResponseManager.handle(data, {'exit': self.exit})
+                    break
+
                 response = jsonrpc.JSONRPCResponseManager.handle(data, self)
+
                 if response is not None:
                     self._write_message(response.data)
             except Exception:
-                log.exception("Language server shutting down for uncaught exception")
+                log.exception("Language server exiting due to uncaught exception")
                 break
 
     def call(self, method, params=None):

Severity of linting erros
I currently really dislike the displayed errors of the linters.
If I use `pycodestyle` and `Pyflakes` I would prefer that `pycodestyle`s errors like `line too long`, `missing whitespace` could be *downgraded* to warnings or info. Obviously `Pyflakes` messages are more severe, but I still would like `pycodestyles` as an information. 

I hope it can be understood what I mean. Is there a way to configure `pyls` this way?
diff --git a/test/test_language_server.py b/test/test_language_server.py
index 408fccc..e1641df 100644
--- a/test/test_language_server.py
+++ b/test/test_language_server.py
@@ -37,10 +37,10 @@ def client_server():
 
     yield client, server
 
-    try:
-        client.call('shutdown')
-    except:
-        pass
+    client.call('shutdown')
+    response = _get_response(client)
+    assert response['result'] is None
+    client.notify('exit')
 
 
 def test_initialize(client_server):
@@ -56,13 +56,6 @@ def test_initialize(client_server):
     assert 'capabilities' in response['result']
 
 
-def test_file_closed(client_server):
-    client, server = client_server
-    client.rfile.close()
-    with pytest.raises(Exception):
-        _get_response(client)
-
-
 def test_missing_message(client_server):
     client, server = client_server
 

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
cachetools==5.5.2
chardet==5.2.0
colorama==0.4.6
configparser==7.2.0
coverage==7.8.0
distlib==0.3.9
exceptiongroup==1.2.2
filelock==3.18.0
future==1.0.0
iniconfig==2.1.0
jedi==0.19.2
json-rpc==1.15.0
packaging==24.2
parso==0.8.4
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pyflakes==3.3.1
pyproject-api==1.9.0
pytest==8.3.5
pytest-cov==6.0.0
-e git+https://github.com/palantir/python-language-server.git@aa8e1b2c12759470c18910c61f55ab4c6b80d344#egg=python_language_server
tomli==2.2.1
tox==4.25.0
typing_extensions==4.13.0
versioneer==0.29
virtualenv==20.29.3
yapf==0.43.0

name: python-language-server
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cachetools==5.5.2
      - chardet==5.2.0
      - colorama==0.4.6
      - configparser==7.2.0
      - coverage==7.8.0
      - distlib==0.3.9
      - exceptiongroup==1.2.2
      - filelock==3.18.0
      - future==1.0.0
      - iniconfig==2.1.0
      - jedi==0.19.2
      - json-rpc==1.15.0
      - packaging==24.2
      - parso==0.8.4
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pyflakes==3.3.1
      - pyproject-api==1.9.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - tomli==2.2.1
      - tox==4.25.0
      - typing-extensions==4.13.0
      - versioneer==0.29
      - virtualenv==20.29.3
      - yapf==0.43.0
prefix: /opt/conda/envs/python-language-server

test/test_language_server.py::test_initialize test/test_language_server.py::test_missing_message test/test_language_server.py::test_linting
pyls/language_server.py pyls/server.py pyls/plugins/pycodestyle_lint.py
pyls/language_server.py pyls/server.py pyls/plugins/pycodestyle_lint.py