server.rs

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