From 185c3c999ef053808a8be5745f289a166d2d7e8d Mon Sep 17 00:00:00 2001 From: Riz Ashraf Date: Wed, 30 Sep 2026 08:47:57 +0100 Subject: [PATCH] fix(mcp): Add missing Cargo.lock, lua bindings, and workspace fixes for tools --- Cargo.lock | 44 ++++++++++++++++++ gemini-ui.lua | 34 ++++++++++++++ nvim-core/src/lib.rs | 75 +++++++++++++++++++++++++++++++ server/src/handlers/workspaces.rs | 2 +- 4 files changed, 154 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index aac5f84..e1cfd79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2130,6 +2130,10 @@ dependencies = [ "tracing-appender", "tracing-subscriber", "tree-sitter", + "tree-sitter-c", + "tree-sitter-cpp", + "tree-sitter-go", + "tree-sitter-java", "tree-sitter-python", "tree-sitter-rust", "tree-sitter-typescript", @@ -4209,6 +4213,46 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "tree-sitter-c" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b2eb57a55fed6b00812912e730b7a275cf4fe98bfd6a5d76263d4438371728" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-cpp" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2196ea9d47b4ab4a31b9297eaa5a5d19a0b121dceb9f118f6790ad0ab94743" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-go" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8560a4d2f835cc0d4d2c2e03cbd0dde2f6114b43bc491164238d333e28b16ea" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-java" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "tree-sitter-language" version = "0.1.8" diff --git a/gemini-ui.lua b/gemini-ui.lua index 050abb0..ae13cc9 100644 --- a/gemini-ui.lua +++ b/gemini-ui.lua @@ -197,4 +197,38 @@ function M.setup() vim.keymap.set('n', 'ar', M.reject_diff, { desc = "Reject Agent Diff", noremap = true, silent = true }) end +function M.send_to_terminal(command) + local term_buf = nil + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buftype == "terminal" then + term_buf = buf + break + end + end + + if not term_buf then + vim.cmd("split | term") + term_buf = vim.api.nvim_get_current_buf() + else + local win_found = false + for _, win in ipairs(vim.api.nvim_list_wins()) do + if vim.api.nvim_win_get_buf(win) == term_buf then + vim.api.nvim_set_current_win(win) + win_found = true + break + end + end + if not win_found then + vim.cmd("split") + vim.api.nvim_win_set_buf(0, term_buf) + end + end + + local job_id = vim.b[term_buf].terminal_job_id + if job_id then + vim.api.nvim_chan_send(job_id, command .. "\r\n") + vim.api.nvim_win_set_cursor(0, {vim.api.nvim_buf_line_count(term_buf), 0}) + end +end + return M diff --git a/nvim-core/src/lib.rs b/nvim-core/src/lib.rs index 94010cc..21a4b17 100644 --- a/nvim-core/src/lib.rs +++ b/nvim-core/src/lib.rs @@ -1118,6 +1118,31 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) { "type": "object", "properties": {} } + }, + { + "name": "nvim_send_to_terminal", + "description": "Send a given command string to a Neovim split terminal.", + "inputSchema": { + "type": "object", + "properties": { + "command": { "type": "string" } + }, + "required": ["command"] + } + }, + { + "name": "nvim_set_preview", + "description": "Overlay proposed code changes as Ghost Text. The preview mechanism uses extmarks for inline suggestions.", + "inputSchema": { + "type": "object", + "properties": { + "file_path": { "type": "string" }, + "start_line": { "type": "integer" }, + "end_line": { "type": "integer" }, + "preview_content": { "type": "string" } + }, + "required": ["file_path", "start_line", "end_line", "preview_content"] + } } ] })), @@ -1574,6 +1599,56 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) { send_error(id, -32602, "Missing 'code'").await; } } + "nvim_send_to_terminal" => { + if let Some(cmd) = args.get("command").and_then(|v| v.as_str()) { + let escaped_cmd = cmd.replace('\\', "\\\\").replace('"', "\\\""); + let lua_code = format!(r#" + if _G.gemini and _G.gemini.send_to_terminal then + _G.gemini.send_to_terminal("{escaped_cmd}") + return "Command sent to terminal" + else + error("gemini UI not loaded") + end + "#); + handle_lua_result!(id, execute_nvim_lua(&lua_code)); + } else { + send_error(id, -32602, "Missing 'command'").await; + } + } + "nvim_set_preview" => { + if let (Some(file), Some(start_line), Some(end_line), Some(preview_content)) = ( + args.get("file_path").and_then(|v| v.as_str()), + args.get("start_line").and_then(|v| v.as_i64()), + args.get("end_line").and_then(|v| v.as_i64()), + args.get("preview_content").and_then(|v| v.as_str()) + ) { + use std::time::{SystemTime, UNIX_EPOCH}; + let diff_id = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis().to_string(); + + let mut lines_str = String::from("{"); + for line in preview_content.lines() { + lines_str.push_str(&format!(r#""{}", "#, line.replace('\\', "\\\\").replace('"', "\\\""))); + } + lines_str.push('}'); + + let escaped_file = file.replace('\\', "\\\\").replace('"', "\\\""); + let lua_code = format!(r#" + if _G.gemini and _G.gemini.preview_diff then + local bufnr = vim.fn.bufnr("{escaped_file}") + if bufnr == -1 then + bufnr = 0 + end + _G.gemini.preview_diff("{diff_id}", bufnr, {start_line}, {end_line}, {lines_str}) + return "Preview added" + else + error("gemini UI not loaded") + end + "#); + handle_lua_result!(id, execute_nvim_lua(&lua_code)); + } else { + send_error(id, -32602, "Missing arguments").await; + } + } "nvim_get_server_info" => { let os = if cfg!(windows) { "Windows" } else { "Linux" }; let exe_path = std::env::current_exe() diff --git a/server/src/handlers/workspaces.rs b/server/src/handlers/workspaces.rs index 6a79a77..1fe7378 100644 --- a/server/src/handlers/workspaces.rs +++ b/server/src/handlers/workspaces.rs @@ -543,7 +543,7 @@ impl McpTool for ReadDirectoryArchitectureHandler { fn visit_dirs(dir: &std::path::Path, summary: &mut String, depth: usize) -> std::io::Result<()> { if dir.is_dir() { - let mut entries = fs::read_dir(dir)?.collect::, std::io::Result<_>>>()?; + let mut entries = fs::read_dir(dir)?.collect::, std::io::Error>>()?; entries.sort_by_key(|e| e.path()); for entry in entries {