mcpls_core/
lib.rs

1//! # mcpls-core
2//!
3//! Core library for MCP (Model Context Protocol) to LSP (Language Server Protocol) translation.
4//!
5//! This crate provides the fundamental building blocks for bridging AI agents with
6//! language servers, enabling semantic code intelligence through MCP tools.
7//!
8//! ## Architecture
9//!
10//! The library is organized into several modules:
11//!
12//! - [`lsp`] - LSP client implementation for communicating with language servers
13//! - [`mcp`] - MCP tool definitions and handlers
14//! - [`bridge`] - Translation layer between MCP and LSP protocols
15//! - [`config`] - Configuration types and loading
16//! - [`error`] - Error types for the library
17//!
18//! ## Example
19//!
20//! ```rust,ignore
21//! use mcpls_core::{serve, ServerConfig};
22//!
23//! #[tokio::main]
24//! async fn main() -> Result<(), mcpls_core::Error> {
25//!     let config = ServerConfig::load()?;
26//!     serve(config).await
27//! }
28//! ```
29
30pub mod bridge;
31pub mod config;
32pub mod error;
33pub mod lsp;
34pub mod mcp;
35
36use std::path::PathBuf;
37use std::sync::Arc;
38
39use bridge::Translator;
40pub use config::ServerConfig;
41pub use error::Error;
42use lsp::{LspServer, ServerInitConfig};
43use rmcp::ServiceExt;
44use tokio::sync::Mutex;
45
46/// Start the MCPLS server with the given configuration.
47///
48/// This is the primary entry point for running the MCP-LSP bridge.
49///
50/// # Errors
51///
52/// Returns an error if:
53/// - LSP server initialization fails
54/// - MCP server setup fails
55/// - Configuration is invalid
56pub async fn serve(config: ServerConfig) -> Result<(), Error> {
57    tracing::info!("Starting MCPLS server...");
58
59    let mut translator = Translator::new();
60    let workspace_roots = if config.workspace.roots.is_empty() {
61        vec![PathBuf::from(".")]
62    } else {
63        config.workspace.roots.clone()
64    };
65
66    translator.set_workspace_roots(workspace_roots.clone());
67
68    for lsp_config in config.lsp_servers {
69        tracing::info!(
70            "Spawning LSP server for language '{}': {} {:?}",
71            lsp_config.language_id,
72            lsp_config.command,
73            lsp_config.args
74        );
75
76        let server_init_config = ServerInitConfig {
77            server_config: lsp_config.clone(),
78            workspace_roots: workspace_roots.clone(),
79            initialization_options: lsp_config.initialization_options.clone(),
80        };
81
82        let server = LspServer::spawn(server_init_config).await?;
83        let client = server.client().clone();
84
85        translator.register_client(lsp_config.language_id.clone(), client);
86    }
87
88    let translator = Arc::new(Mutex::new(translator));
89
90    tracing::info!("Starting MCP server with rmcp...");
91    let mcp_server = mcp::McplsServer::new(translator);
92
93    tracing::info!("MCPLS server initialized successfully");
94    tracing::info!("Listening for MCP requests on stdio...");
95
96    let service = mcp_server
97        .serve(rmcp::transport::stdio())
98        .await
99        .map_err(|e| Error::McpServer(format!("Failed to start MCP server: {e}")))?;
100
101    service
102        .waiting()
103        .await
104        .map_err(|e| Error::McpServer(format!("MCP server error: {e}")))?;
105
106    tracing::info!("MCPLS server shutting down");
107    Ok(())
108}