LSP and MCP ends share global Arc<Config>

Phillip Davis created

Change summary

src/lsp/client.rs     | 26 +++++++++++-------
src/main.rs           |  3 +
src/mcp/server.rs     | 23 +++++----------
src/mcp/tools/read.rs | 63 ++++++++++++++++++++++----------------------
4 files changed, 57 insertions(+), 58 deletions(-)

Detailed changes

src/lsp/client.rs 🔗

@@ -1,5 +1,6 @@
 use std::ops::ControlFlow;
 use std::path::PathBuf;
+use std::sync::Arc;
 
 use async_lsp::lsp_types::notification::{Progress, PublishDiagnostics, ShowMessage};
 use async_lsp::lsp_types::{
@@ -12,7 +13,10 @@ use async_process::{Child, Command as ProcessCommand, Stdio};
 use futures::channel::oneshot;
 use tower::ServiceBuilder;
 
+use crate::config::Config;
+
 pub struct LSPClientState {
+    pub config: Arc<Config>,
     pub indexed_tx: Option<oneshot::Sender<()>>,
 }
 
@@ -28,22 +32,24 @@ pub struct LSPClient {
 
 impl LSPClient {
     pub async fn setup(
-        lsp_command: &str,
-        lsp_args: &[String],
-        project_root: &PathBuf,
+        config: Arc<Config>,
     ) -> anyhow::Result<Self> {
-        let child = ProcessCommand::new(lsp_command)
-            .args(lsp_args)
-            .current_dir(project_root)
+        let child = ProcessCommand::new(&config.lsp_server_command.command)
+            .args(&config.lsp_server_command.args)
+            .current_dir(&config.project_root)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::inherit())
             .kill_on_drop(true)
             .spawn()
-            .expect(format!("Failed to start lsp: {} {:?}", lsp_command, lsp_args).as_ref());
+            .expect(format!("Failed to start lsp: {} {:?}", &config.lsp_server_command.command, &config.lsp_server_command.args).as_ref());
 
-        let (mainloop, mut server) = async_lsp::MainLoop::new_client(|_server| {
-            let mut router = Router::new(LSPClientState { indexed_tx: None });
+        let config_clone = config.clone();
+        let (mainloop, mut server) = async_lsp::MainLoop::new_client(move |_server| {
+            let mut router = Router::new(LSPClientState {
+                config: config_clone,
+                indexed_tx: None
+            });
 
             router
                 .notification::<Progress>(|_this, _params| ControlFlow::Continue(()))
@@ -54,7 +60,7 @@ impl LSPClient {
             ServiceBuilder::new().service(router)
         });
 
-        let project_root_canonicalized = tokio::fs::canonicalize(project_root)
+        let project_root_canonicalized = tokio::fs::canonicalize(&config.project_root)
             .await
             .expect("Failed to canonicalize project root");
 

src/main.rs 🔗

@@ -2,6 +2,7 @@ mod config;
 mod lsp;
 mod mcp;
 
+use std::sync::Arc;
 use clap::Parser;
 
 use config::{CommandLineArgs, Config};
@@ -16,7 +17,7 @@ async fn main() -> anyhow::Result<()> {
         .init();
 
     let args = CommandLineArgs::parse();
-    let config = Config::load(&args.config).await?;
+    let config = Arc::new(Config::load(&args.config).await?);
 
     let (mcp_server, lsp_client) = mcp::setup(config).await?;
     mcp_server.run(lsp_client).await?;

src/mcp/server.rs 🔗

@@ -1,4 +1,5 @@
 use std::path::PathBuf;
+use std::sync::Arc;
 
 use rmcp::ErrorData as MCPError;
 use rmcp::ServerHandler as MCPServerHandler;
@@ -16,31 +17,23 @@ pub use crate::mcp::tools::read::*;
 
 pub struct MCPServer {
     pub(crate) tool_router: ToolRouter<Self>,
-    pub(crate) project_root: PathBuf,
+    pub(crate) config: Arc<Config>,
     pub(crate) lsp_server: LSPServerSocket,
 }
 
-pub async fn setup(config: Config) -> anyhow::Result<(MCPServer, LSPClient)> {
-    let project_root = tokio::fs::canonicalize(&config.project_root)
-        .await
-        .expect("Failed to canonicalize project root");
+pub async fn setup(config: Arc<Config>) -> anyhow::Result<(MCPServer, LSPClient)> {
+    let lsp_client = LSPClient::setup(config.clone())
+        .await?;
 
-    let lsp_client = LSPClient::setup(
-        &config.lsp_server_command.command,
-        &config.lsp_server_command.args,
-        &project_root,
-    )
-    .await?;
-
-    let server = MCPServer::new(project_root, lsp_client.server.clone());
+    let server = MCPServer::new(config, lsp_client.server.clone());
 
     Ok((server, lsp_client))
 }
 
 impl MCPServer {
-    pub fn new(project_root: PathBuf, lsp_server: LSPServerSocket) -> Self {
+    pub fn new(config: Arc<Config>, lsp_server: LSPServerSocket) -> Self {
         Self {
-            project_root,
+            config,
             lsp_server,
             tool_router: Self::tool_router(),
         }

src/mcp/tools/read.rs 🔗

@@ -27,36 +27,35 @@ pub async fn call(
     server: &MCPServer,
     Parameters(args): Parameters<ReadToolArgs>,
 ) -> Result<CallToolResult, MCPError> {
-    let file_path = server.project_root.join(&args.path);
-    let file_path = tokio::fs::canonicalize(file_path).await.unwrap();
-    let content = tokio::fs::read_to_string(&file_path)
-        .await
-        .map_err(|e| MCPError::invalid_request(format!("Failed to read file: {e}"), None))?;
-
-    let mut lsp_server = server.lsp_server.clone();
-
-    let diagnostic_report = lsp_server
-        .document_diagnostic(DocumentDiagnosticParams {
-            text_document: TextDocumentIdentifier::new(Url::from_file_path(file_path).unwrap()),
-            identifier: None,
-            previous_result_id: None,
-            work_done_progress_params: WorkDoneProgressParams {
-                work_done_token: None,
-            },
-            partial_result_params: PartialResultParams {
-                partial_result_token: None,
-            },
-        })
-        .await
-        .unwrap();
-
-    Ok(CallToolResult {
-        content: vec![],
-        structured_content: Some(serde_json::json!(ReadToolOutput {
-            content: Some(content),
-            diagnostics: diagnostic_report
-        })),
-        is_error: None,
-        meta: None,
-    })
+    Err(MCPError::internal_error("Not yet implemented", None))
+    // let content = tokio::fs::read_to_string(&file_path)
+    //     .await
+    //     .map_err(|e| MCPError::invalid_request(format!("Failed to read file: {e}"), None))?;
+
+    // let mut lsp_server = server.lsp_server.clone();
+
+    // let diagnostic_report = lsp_server
+    //     .document_diagnostic(DocumentDiagnosticParams {
+    //         text_document: TextDocumentIdentifier::new(Url::from_file_path(file_path).unwrap()),
+    //         identifier: None,
+    //         previous_result_id: None,
+    //         work_done_progress_params: WorkDoneProgressParams {
+    //             work_done_token: None,
+    //         },
+    //         partial_result_params: PartialResultParams {
+    //             partial_result_token: None,
+    //         },
+    //     })
+    //     .await
+    //     .unwrap();
+
+    // Ok(CallToolResult {
+    //     content: vec![],
+    //     structured_content: Some(serde_json::json!(ReadToolOutput {
+    //         content: Some(content),
+    //         diagnostics: diagnostic_report
+    //     })),
+    //     is_error: None,
+    //     meta: None,
+    // })
 }