1use std::path::PathBuf;
 2
 3use rmcp::ErrorData as MCPError;
 4use rmcp::ServerHandler as MCPServerHandler;
 5use rmcp::handler::server::tool::ToolRouter;
 6use rmcp::handler::server::wrapper::Parameters;
 7use rmcp::model::CallToolResult;
 8use rmcp::model::{Implementation, ProtocolVersion, ServerCapabilities, ServerInfo};
 9use rmcp::{tool_handler, tool_router};
10
11use async_lsp::ServerSocket as LSPServerSocket;
12
13use crate::config::Config;
14use crate::lsp::LSPClient;
15pub use crate::mcp::tools::read::*;
16
17pub struct MCPServer {
18    pub(crate) tool_router: ToolRouter<Self>,
19    pub(crate) project_root: PathBuf,
20    pub(crate) lsp_server: LSPServerSocket,
21}
22
23pub async fn setup(config: Config) -> anyhow::Result<(MCPServer, LSPClient)> {
24    let project_root = tokio::fs::canonicalize(&config.project_root)
25        .await
26        .expect("Failed to canonicalize project root");
27
28    let lsp_client = LSPClient::setup(
29        &config.lsp_server_command.command,
30        &config.lsp_server_command.args,
31        &project_root,
32    )
33    .await?;
34
35    let server = MCPServer::new(project_root, lsp_client.server.clone());
36
37    Ok((server, lsp_client))
38}
39
40impl MCPServer {
41    pub fn new(project_root: PathBuf, lsp_server: LSPServerSocket) -> Self {
42        Self {
43            project_root,
44            lsp_server,
45            tool_router: Self::tool_router(),
46        }
47    }
48
49    pub async fn run(self, lsp_client: LSPClient) -> anyhow::Result<()> {
50        let mainloop_fut = tokio::spawn(async move {
51            lsp_client
52                .run_main_loop()
53                .await
54                .expect("Error while running main LSP loop");
55        });
56
57        let server = rmcp::ServiceExt::serve(self, rmcp::transport::stdio())
58            .await
59            .expect("Failed to start serving MCP");
60
61        let (mainloop_result, server_result) = tokio::join!(mainloop_fut, server.waiting());
62
63        mainloop_result?;
64        server_result?;
65
66        Ok(())
67    }
68}
69
70#[tool_router]
71impl MCPServer {
72    #[rmcp::tool(
73        description = "Like the 'view' tool, except it returns LSP diagnostics too. Always use this instead of 'view'"
74    )]
75    pub async fn read(
76        &self,
77        Parameters(args): Parameters<ReadToolArgs>,
78    ) -> Result<CallToolResult, MCPError> {
79        crate::mcp::tools::read::call(self, Parameters(args)).await
80    }
81}
82
83#[tool_handler]
84impl MCPServerHandler for MCPServer {
85    fn get_info(&self) -> ServerInfo {
86        ServerInfo {
87            protocol_version: ProtocolVersion::V_2024_11_05,
88            capabilities: ServerCapabilities::builder().enable_tools().build(),
89            server_info: Implementation::from_build_env(),
90            instructions: Some("This server turns standard coding agent tools like `read` and `edit` into LSP clients.".into())
91        }
92    }
93}