diff --git a/examples/servers/.vscode/launch.json b/examples/servers/.vscode/launch.json
new file mode 100644
index 00000000..f1241865
--- /dev/null
+++ b/examples/servers/.vscode/launch.json
@@ -0,0 +1,20 @@
+{
+    "configurations": [
+        {
+            "name": "pygls: Debug Server",
+            "type": "python",
+            "request": "attach",
+            "connect": {
+                "host": "${config:pygls.server.debugHost}",
+                "port": "${config:pygls.server.debugPort}"
+            },
+            "pathMappings": [
+                {
+                    "localRoot": "${workspaceFolder}",
+                    "remoteRoot": "."
+                }
+            ],
+            "justMyCode": false
+        }
+    ]
+}
diff --git a/examples/servers/.vscode/settings.json b/examples/servers/.vscode/settings.json
index 7064c02c..c2b0d23b 100644
--- a/examples/servers/.vscode/settings.json
+++ b/examples/servers/.vscode/settings.json
@@ -1,6 +1,9 @@
 {
     // Uncomment to override Python interpreter used.
     // "pygls.server.pythonPath": "/path/to/python",
+    "pygls.server.debug": false,
+    // "pygls.server.debugHost": "localhost",
+    // "pygls.server.debugPort": 5678,
     "pygls.server.launchScript": "json_server.py",
     "pygls.trace.server": "off",
     "pygls.client.documentSelector": [
diff --git a/examples/vscode-playground/README.md b/examples/vscode-playground/README.md
index 986f2b01..e73ed513 100644
--- a/examples/vscode-playground/README.md
+++ b/examples/vscode-playground/README.md
@@ -75,3 +75,10 @@ The `code_actions.py` example is intended to be used with text files (e.g. the p
 ```
 
 You can find the full list of known language identifiers [here](https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers).
+
+#### Debugging the server
+
+To debug the language server set the `pygls.server.debug` option to `true`.
+The server should be restarted and the debugger connect automatically.
+
+You can control the host and port that the debugger uses through the `pygls.server.debugHost` and `pygls.server.debugPort` options.
diff --git a/examples/vscode-playground/package.json b/examples/vscode-playground/package.json
index 869032db..9c6827d2 100644
--- a/examples/vscode-playground/package.json
+++ b/examples/vscode-playground/package.json
@@ -54,6 +54,24 @@
             "description": "The working directory from which to launch the server.",
             "markdownDescription": "The working directory from which to launch the server.\nIf blank, this will default to the `examples/servers` directory."
           },
+          "pygls.server.debug": {
+            "scope": "resource",
+            "default": false,
+            "type": "boolean",
+            "description": "Enable debugging of the server process."
+          },
+          "pygls.server.debugHost": {
+            "scope": "resource",
+            "default": "localhost",
+            "type": "string",
+            "description": "The host on which the server process to debug is running."
+          },
+          "pygls.server.debugPort": {
+            "scope": "resource",
+            "default": 5678,
+            "type": "integer",
+            "description": "The port number on which the server process to debug is listening."
+          },
           "pygls.server.launchScript": {
             "scope": "resource",
             "type": "string",
@@ -73,12 +91,14 @@
             "default": "off",
             "enum": [
               "off",
+              "messages",
               "verbose"
             ],
             "description": "Controls if LSP messages send to/from the server should be logged.",
             "enumDescriptions": [
               "do not log any lsp messages",
-              "log all lsp messages sent to/from the server"
+              "log all lsp messages sent to/from the server",
+              "log all lsp messages sent to/from the server, including their contents"
             ]
           }
         }
diff --git a/examples/vscode-playground/src/extension.ts b/examples/vscode-playground/src/extension.ts
index 8a4c23f2..8952f39c 100644
--- a/examples/vscode-playground/src/extension.ts
+++ b/examples/vscode-playground/src/extension.ts
@@ -24,13 +24,12 @@ import * as vscode from "vscode";
 import * as semver from "semver";
 
 import { PythonExtension } from "@vscode/python-extension";
-import { LanguageClient, LanguageClientOptions, ServerOptions, State } from "vscode-languageclient/node";
+import { LanguageClient, LanguageClientOptions, ServerOptions, State, integer } from "vscode-languageclient/node";
 
 const MIN_PYTHON = semver.parse("3.7.9")
 
 // Some other nice to haves.
 // TODO: Check selected env satisfies pygls' requirements - if not offer to run the select env command.
-// TODO: Start a debug session for the currently configured server.
 // TODO: TCP Transport
 // TODO: WS Transport
 // TODO: Web Extension support (requires WASM-WASI!)
@@ -144,7 +143,7 @@ async function startLangServer() {
     if (client) {
         await stopLangServer()
     }
-
+    const config = vscode.workspace.getConfiguration("pygls.server")
     const cwd = getCwd()
     const serverPath = getServerPath()
 
@@ -152,25 +151,33 @@ async function startLangServer() {
     logger.info(`server: '${serverPath}'`)
 
     const resource = vscode.Uri.joinPath(vscode.Uri.file(cwd), serverPath)
-    const pythonPath = await getPythonPath(resource)
-    if (!pythonPath) {
+    const pythonCommand = await getPythonCommand(resource)
+    if (!pythonCommand) {
         clientStarting = false
         return
     }
 
+    logger.debug(`python: ${pythonCommand.join(" ")}`)
     const serverOptions: ServerOptions = {
-        command: pythonPath,
-        args: [serverPath],
+        command: pythonCommand[0],
+        args: [...pythonCommand.slice(1), serverPath],
         options: { cwd },
     };
 
     client = new LanguageClient('pygls', serverOptions, getClientOptions());
-    try {
-        await client.start()
-        clientStarting = false
-    } catch (err) {
-        clientStarting = false
-        logger.error(`Unable to start server: ${err}`)
+    const promises = [client.start()]
+
+    if (config.get<boolean>("debug")) {
+        promises.push(startDebugging())
+    }
+
+    const results = await Promise.allSettled(promises)
+    clientStarting = false
+
+    for (const result of results) {
+        if (result.status === "rejected") {
+            logger.error(`There was a error starting the server: ${result.reason}`)
+        }
     }
 }
 
@@ -187,6 +194,17 @@ async function stopLangServer(): Promise<void> {
     client = undefined
 }
 
+function startDebugging(): Promise<void> {
+    if (!vscode.workspace.workspaceFolders) {
+        logger.error("Unable to start debugging, there is no workspace.")
+        return Promise.reject("Unable to start debugging, there is no workspace.")
+    }
+    // TODO: Is there a more reliable way to ensure the debug adapter is ready?
+    setTimeout(async () => {
+        await vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0], "pygls: Debug Server")
+    }, 2000)
+}
+
 function getClientOptions(): LanguageClientOptions {
     const config = vscode.workspace.getConfiguration('pygls.client')
     const options = {
@@ -270,7 +288,7 @@ function getCwd(): string {
 
 /**
  *
- * @returns The python script to launch the server with
+ * @returns The python script that implements the server.
  */
 function getServerPath(): string {
     const config = vscode.workspace.getConfiguration("pygls.server")
@@ -279,13 +297,49 @@ function getServerPath(): string {
 }
 
 /**
+ * Return the python command to use when starting the server.
+ *
+ * If debugging is enabled, this will also included the arguments to required
+ * to wrap the server in a debug adapter.
+ *
+ * @returns The full python command needed in order to start the server.
+ */
+async function getPythonCommand(resource?: vscode.Uri): Promise<string[] | undefined> {
+    const config = vscode.workspace.getConfiguration("pygls.server", resource)
+    const pythonPath = await getPythonInterpreter(resource)
+    if (!pythonPath) {
+        return
+    }
+    const command = [pythonPath]
+    const enableDebugger = config.get<boolean>('debug')
+
+    if (!enableDebugger) {
+        return command
+    }
+
+    const debugHost = config.get<string>('debugHost')
+    const debugPort = config.get<integer>('debugPort')
+    try {
+        const debugArgs = await python.debug.getRemoteLauncherCommand(debugHost, debugPort, true)
+        // Debugpy recommends we disable frozen modules
+        command.push("-Xfrozen_modules=off", ...debugArgs)
+    } catch (err) {
+        logger.error(`Unable to get debugger command: ${err}`)
+        logger.error("Debugger will not be available.")
+    }
+
+    return command
+}
+
+/**
+ * Return the python interpreter to use when starting the server.
+ *
  * This uses the official python extension to grab the user's currently
  * configured environment.
  *
  * @returns The python interpreter to use to launch the server
  */
-async function getPythonPath(resource?: vscode.Uri): Promise<string | undefined> {
-
+async function getPythonInterpreter(resource?: vscode.Uri): Promise<string | undefined> {
     const config = vscode.workspace.getConfiguration("pygls.server", resource)
     const pythonPath = config.get<string>('pythonPath')
     if (pythonPath) {