diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index 362498cb..1ce5158b 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -113,6 +113,10 @@ impl Client {
         self.offset_encoding
     }
 
+    pub fn config(&self) -> Option<&Value> {
+        self.config.as_ref()
+    }
+
     /// Execute a RPC request on the language server.
     async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
     where
@@ -246,6 +250,13 @@ impl Client {
             root_uri: root,
             initialization_options: self.config.clone(),
             capabilities: lsp::ClientCapabilities {
+                workspace: Some(lsp::WorkspaceClientCapabilities {
+                    configuration: Some(true),
+                    did_change_configuration: Some(lsp::DynamicRegistrationClientCapabilities {
+                        dynamic_registration: Some(false),
+                    }),
+                    ..Default::default()
+                }),
                 text_document: Some(lsp::TextDocumentClientCapabilities {
                     completion: Some(lsp::CompletionClientCapabilities {
                         completion_item: Some(lsp::CompletionItemCapability {
@@ -330,6 +341,16 @@ impl Client {
         self.exit().await
     }
 
+    // -------------------------------------------------------------------------------------------
+    // Workspace
+    // -------------------------------------------------------------------------------------------
+
+    pub fn did_change_configuration(&self, settings: Value) -> impl Future<Output = Result<()>> {
+        self.notify::<lsp::notification::DidChangeConfiguration>(
+            lsp::DidChangeConfigurationParams { settings },
+        )
+    }
+
     // -------------------------------------------------------------------------------------------
     // Text document
     // -------------------------------------------------------------------------------------------
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 109546d0..389dfb4d 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -191,6 +191,7 @@ pub mod util {
 pub enum MethodCall {
     WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
     ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
+    WorkspaceConfiguration(lsp::ConfigurationParams),
 }
 
 impl MethodCall {
@@ -209,6 +210,12 @@ impl MethodCall {
                     .expect("Failed to parse ApplyWorkspaceEdit params");
                 Self::ApplyWorkspaceEdit(params)
             }
+            lsp::request::WorkspaceConfiguration::METHOD => {
+                let params: lsp::ConfigurationParams = params
+                    .parse()
+                    .expect("Failed to parse WorkspaceConfiguration params");
+                Self::WorkspaceConfiguration(params)
+            }
             _ => {
                 log::warn!("unhandled lsp request: {}", method);
                 return None;
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index 986df703..2a7c9c21 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -532,6 +532,13 @@ impl Application {
                                 }
                             };
 
+                        // Trigger a workspace/didChangeConfiguration notification after initialization.
+                        // This might not be required by the spec but Neovim does this as well, so it's
+                        // probably a good idea for compatibility.
+                        if let Some(config) = language_server.config() {
+                            tokio::spawn(language_server.did_change_configuration(config.clone()));
+                        }
+
                         let docs = self.editor.documents().filter(|doc| {
                             doc.language_server().map(|server| server.id()) == Some(server_id)
                         });
@@ -788,6 +795,37 @@ impl Application {
                             })),
                         ));
                     }
+                    MethodCall::WorkspaceConfiguration(params) => {
+                        let language_server =
+                            match self.editor.language_servers.get_by_id(server_id) {
+                                Some(language_server) => language_server,
+                                None => {
+                                    warn!("can't find language server with id `{}`", server_id);
+                                    return;
+                                }
+                            };
+                        let result: Vec<_> = params
+                            .items
+                            .iter()
+                            .map(|item| {
+                                let mut config = match &item.scope_uri {
+                                    Some(scope) => {
+                                        let path = scope.to_file_path().ok()?;
+                                        let doc = self.editor.document_by_path(path)?;
+                                        doc.language_config()?.config.as_ref()?
+                                    }
+                                    None => language_server.config()?,
+                                };
+                                if let Some(section) = item.section.as_ref() {
+                                    for part in section.split('.') {
+                                        config = config.get(part)?;
+                                    }
+                                }
+                                Some(config)
+                            })
+                            .collect();
+                        tokio::spawn(language_server.reply(id, Ok(json!(result))));
+                    }
                 }
             }
             e => unreachable!("{:?}", e),