//! Connect to an LSP language server. //! //! Starts a new language server process and registers it in the //! shared LSP manager for subsequent tool invocations. use crate::tools::{Tool, ToolCtx}; use anyhow::Result; use serde_json::{json, Value}; use tracing::{info, error, instrument}; /// Tool that connects to an LSP language server for a given language. /// /// Flow: parse language + command + args → lock LSP manager → call /// `manager.start()` → confirm connection in the response string. pub struct LspConnect; impl Tool for LspConnect { fn name(&self) -> &'static str { "lsp_connect" } fn description(&self) -> &'static str { "Connect to an LSP language server for a given language" } fn parameters(&self) -> Value { json!({ "type": "object", "properties": { "language": { "type": "string", "description": "Language identifier (e.g. 'rust', 'python')" }, "command": { "type": "string", "description": "Command to start the language server" }, "args": { "type": "array", "items": {"type": "string"}, "description": "Arguments for the language server command" } }, "required": ["language", "command"] }) } #[instrument(skip(self, ctx, args))] fn run(&self, ctx: &ToolCtx, args: &Value) -> Result { let language = crate::tools::arg_str(args, "language")?; let command = crate::tools::arg_str(args, "command")?; let extra_args: Vec = args .get("args") .and_then(|v| v.as_array()) .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect()) .unwrap_or_default(); info!(language, command, extra_args = ?extra_args, "LSP connect requested"); let mut manager = match ctx.lsp_manager.lock() { Ok(g) => g, Err(poisoned) => { error!("LSP manager mutex poisoned, recovering"); poisoned.into_inner() } }; manager.start(&language, &command, &extra_args)?; info!(language, "LSP connected successfully"); Ok(format!("Connected LSP for '{language}'")) } }