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