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}