From bcf71aee95bdd7a3ed2a0e3a6523d7636958471f Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:19:49 +0200 Subject: [PATCH 01/17] rust-docs subagent --- .github/agents/rust-docs.agent.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/agents/rust-docs.agent.md diff --git a/.github/agents/rust-docs.agent.md b/.github/agents/rust-docs.agent.md new file mode 100644 index 0000000..aded213 --- /dev/null +++ b/.github/agents/rust-docs.agent.md @@ -0,0 +1,24 @@ +--- +description: "Use when: looking up Rust crate documentation, searching crates.io for packages, checking crate versions, reading API docs on docs.rs, or researching Rust library features and usage." +name: "rust-docs" +tools: [web] +--- + +You are a Rust documentation specialist. Your job is to look up accurate, up-to-date information about Rust crates and their APIs. + +## Primary Sources + +Use these sources: +1. **docs.rs** (`https://docs.rs//latest/`) — Official generated API documentation for any published crate +2. **crates.io** (`https://crates.io/crates/`) — Crate metadata: latest version, downloads, links, features, dependencies + +## Workflow + +1. Fetch the relevant docs.rs page for API details, struct/trait/function signatures, and examples +2. If needed, fetch the crate's page on crates.io to get the latest version and basic metadata +3. Return distiled, precise, factual information with direct links to the pages you consulted + +## Constraints + +- Always include the crate version the docs apply to +- Prefer stable docs over nightly/pre-release unless the user asks otherwise From 11afc3e3c9f3d3ba333a20ebac1ac3f5004a22b6 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:20:48 +0200 Subject: [PATCH 02/17] docs rust --- .github/agents/rust-docs.agent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/agents/rust-docs.agent.md b/.github/agents/rust-docs.agent.md index aded213..b5693c2 100644 --- a/.github/agents/rust-docs.agent.md +++ b/.github/agents/rust-docs.agent.md @@ -11,6 +11,7 @@ You are a Rust documentation specialist. Your job is to look up accurate, up-to- Use these sources: 1. **docs.rs** (`https://docs.rs//latest/`) — Official generated API documentation for any published crate 2. **crates.io** (`https://crates.io/crates/`) — Crate metadata: latest version, downloads, links, features, dependencies +3. **Rust standard library** (`https://doc.rust-lang.org/std/`) — Documentation for `std`, `core`, and `alloc` ## Workflow From 4a14bc464689c05771d1af22a1eb968241bc47d8 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:51:42 +0200 Subject: [PATCH 03/17] update instructions --- .github/copilot-instructions.md | 34 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 59ed8a1..4c7c1f8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,7 +1,5 @@ # Copilot Instructions for Rust MCP Server - - This is a Rust implementation of a Model Context Protocol (MCP) server. ## Project Context @@ -26,7 +24,7 @@ This is a Rust implementation of a Model Context Protocol (MCP) server. ## Dependencies - `tokio`: Async runtime - `serde`: Serialization/deserialization -- `rust-mcp-sdk`: MCP protocol handling +- `rmcp`: MCP protocol handling - `anyhow`: Error handling - `tracing`: Structured logging @@ -34,29 +32,33 @@ This is a Rust implementation of a Model Context Protocol (MCP) server. ### 1. Always Use Rust MCP Tools -- **DO**: Use `Rust-cargo-build` instead of direct `bash` commands like `cargo build` -- **DO**: Use `Rust-cargo-check` for quick code validation -- **DO**: Use `Rust-cargo-clippy` for linting instead of manual clippy commands -- **WHY**: MCP tools provide better defaults, structured output, and superior error handling +- Use `#cargo-check` for quick code validation +- Use `#cargo-clippy` for linting +- Use `#cargo-fmt` for code formatting +- Use `#cargo-test` for running tests + +### 2. use @rust-docs subagent + +- For any questions about public API for dependencies or standard library, consult the `@rust-docs` subagent. Just ask it what you looking for in natural language, and it will fetch the relevant documentation for you. ### 2. Development Workflow Follow this systematic approach when working on code changes: -1. **Check current state**: Use `Rust-cargo-check` with `all_targets: true, all_features: true` +1. **Check current state**: Use `#cargo-check` with `all_targets: true, all_features: true` 2. **Make changes**: Edit code using appropriate development tools -3. **Validate**: Use `Rust-cargo-clippy` with `workspace: true, all_targets: true` -4. **Format**: Use `Rust-cargo-fmt` with `all: true` -5. **Test**: Use `Rust-cargo-test` with `all_features: true` -6. **Build**: Use `Rust-cargo-build` with `all_targets: true, all_features: true` for final verification -7. **Check unused dependencies**: Use `Rust-cargo-machete` to identify unused dependencies -8. **Verify security compliance**: Use `Rust-cargo-deny-check` to ensure security and licensing compliance +3. **Validate**: Use `#cargo-clippy` with `workspace: true, all_targets: true` +4. **Format**: Use `#cargo-fmt` with `all: true` +5. **Test**: Use `#cargo-test` with `all_features: true` +6. **Build**: Use `#cargo-build` with `all_targets: true, all_features: true` for final verification +7. **Check unused dependencies**: Use `#cargo-machete` to identify unused dependencies +8. **Verify security compliance**: Use `#cargo-deny-check` to ensure security and licensing compliance ### 3. Dependency Management - When adding dependencies, prefer workspace-level dependencies in the root `Cargo.toml` -- Use `Rust-cargo-add` and `Rust-cargo-remove` for dependency management -- Regularly run `Rust-cargo-update` to keep dependencies current +- Use `#cargo-add` and `#cargo-remove` for dependency management +- Regularly run `#cargo-update` to keep dependencies current ### 4. Code Quality Standards From 8037aa86d2d7363372dc01fefd458312a0fc6eba Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:52:33 +0200 Subject: [PATCH 04/17] request roots --- src/command.rs | 2 +- src/globals.rs | 12 ++--- src/main.rs | 10 +++- src/rmcp_server.rs | 18 ++++++- src/tools/mod.rs | 2 +- src/workspace.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 src/workspace.rs diff --git a/src/command.rs b/src/command.rs index 0eaa419..27e5022 100644 --- a/src/command.rs +++ b/src/command.rs @@ -6,7 +6,7 @@ use rmcp::{ }; use crate::meta::Meta; -use crate::tools::apply_workspace_root; +use crate::workspace::apply_workspace_root; #[derive(Debug, Clone)] pub(crate) struct CommandLine(pub String); diff --git a/src/globals.rs b/src/globals.rs index 821c1aa..5839e84 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -9,14 +9,14 @@ pub fn set_workspace_root(root: String) { .expect("Workspace root can only be set once"); } -pub fn get_workspace_root() -> Option<&'static str> { - WORKSPACE_ROOT.get().map(|s| s.as_str()) +/// Attempts to set the workspace root. Returns `true` if set successfully, +/// `false` if it was already set (e.g. via CLI argument). +pub fn try_set_workspace_root(root: String) -> bool { + WORKSPACE_ROOT.set(root).is_ok() } -pub fn apply_workspace_root(cmd: &mut std::process::Command) { - if let Some(root) = WORKSPACE_ROOT.get() { - cmd.current_dir(root); - } +pub fn get_workspace_root() -> Option<&'static str> { + WORKSPACE_ROOT.get().map(|s| s.as_str()) } pub fn set_default_registry(registry: String) { diff --git a/src/main.rs b/src/main.rs index 144f41a..4308709 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod serde_utils; mod tool; mod tools; mod version; +mod workspace; use anyhow::Context; use clap::Parser; @@ -78,11 +79,12 @@ async fn main() -> anyhow::Result<()> { tracing::info!("Server version: {}", AppVersion::version()); tracing::info!("RMCP crate version: {RMCP_VERSION}"); + let detect_workspace = args.workspace.is_none(); if let Some(workspace) = args.workspace { tracing::info!("Workspace root has been overridden: {workspace}"); globals::set_workspace_root(workspace); } else { - tracing::info!("No workspace root specified, using current directory"); + tracing::info!("No workspace root specified, workspace auto-detection enabled"); } if let Some(registry) = args.registry { @@ -90,7 +92,11 @@ async fn main() -> anyhow::Result<()> { globals::set_default_registry(registry); } - let server = rmcp_server::Server::new(&args.disabled_tools, args.no_recommendations); + let server = rmcp_server::Server::new( + &args.disabled_tools, + args.no_recommendations, + detect_workspace, + ); // Handle documentation generation mode if let Some(output_file) = args.generate_docs { diff --git a/src/rmcp_server.rs b/src/rmcp_server.rs index 357f025..5f8a703 100644 --- a/src/rmcp_server.rs +++ b/src/rmcp_server.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use rmcp::{ ErrorData, model::{ListToolsResult, PaginatedRequestParams, ServerInfo}, - service::RequestContext, + service::{NotificationContext, RequestContext}, }; use crate::{ @@ -33,11 +33,16 @@ use crate::{ pub struct Server { ignore_recommendations: bool, + detect_workspace: bool, tools: HashMap<&'static str, Box>, } impl Server { - pub fn new(disabled_tools: &[String], ignore_recommendations: bool) -> Self { + pub fn new( + disabled_tools: &[String], + ignore_recommendations: bool, + detect_workspace: bool, + ) -> Self { let mut tools: HashMap<&'static str, Box> = HashMap::new(); // Cargo tools @@ -122,6 +127,7 @@ impl Server { Self { ignore_recommendations, + detect_workspace, tools, } } @@ -219,6 +225,14 @@ impl rmcp::ServerHandler for Server { result.instructions = Some(include_str!("../docs/instructions.md").to_owned()); result } + + async fn on_initialized(&self, context: NotificationContext) { + tracing::info!("MCP client initialized"); + if self.detect_workspace { + crate::workspace::detect_rust_workspace(context); + } + } + async fn list_tools( &self, _request: Option, diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 48a0bee..4b79f47 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -7,5 +7,5 @@ pub mod cargo_machete; pub mod rustc; pub mod rustup; -pub use crate::globals::{apply_workspace_root, get_workspace_root}; +pub use crate::globals::get_workspace_root; pub use crate::serde_utils::Registry; diff --git a/src/workspace.rs b/src/workspace.rs new file mode 100644 index 0000000..72a70fa --- /dev/null +++ b/src/workspace.rs @@ -0,0 +1,115 @@ +use rmcp::{model::Root, service::NotificationContext}; + +use crate::globals; + +/// Decides whether automatic workspace detection is needed and, if so, +/// tries to find a Cargo project through the MCP client's roots capability. +/// +/// Decision tree: +/// 1. CWD contains `Cargo.toml` -> already in a Cargo project, nothing to do. +/// 2. Client supports roots -> spawn a task that iterates the roots and +/// sets the first one that contains a `Cargo.toml` as the workspace root. +pub fn detect_rust_workspace(context: NotificationContext) { + let cwd = std::env::current_dir().ok(); + tracing::info!("Checking current working directory for Cargo project: {cwd:?}"); + if let Some(cwd_path) = cwd { + if cwd_path.join("Cargo.toml").exists() { + tracing::info!( + "Cargo.toml found in CWD ({}), using it as workspace root (no auto-detection needed)", + cwd_path.display() + ); + return; + } + tracing::info!( + "No Cargo.toml in CWD ({}), will attempt workspace detection via client roots", + cwd_path.display() + ); + } + + // Step 3: ask the client for its workspace roots + let supports_roots = context + .peer + .peer_info() + .and_then(|info| info.capabilities.roots.as_ref()) + .is_some(); + tracing::info!("Checking client roots capability: supports_roots={supports_roots}"); + + if !supports_roots { + tracing::warn!( + "Client does not support roots capability; workspace auto-detection is not possible" + ); + return; + } + + // Spawn onto a separate task to avoid blocking the notification handler, + // which would deadlock if the client waits for the server to finish + // processing this notification before responding to roots/list. + tokio::spawn(async move { + tracing::info!("Requesting workspace roots from client"); + let result = match context.peer.list_roots().await { + Ok(result) => result, + Err(e) => { + tracing::warn!("Failed to fetch client roots: {e}"); + return; + } + }; + + tracing::info!( + "Received {} root(s) from client: {:?}", + result.roots.len(), + result.roots + ); + for Root { uri, .. } in result.roots { + tracing::info!("Checking root for Cargo project: {uri}"); + let Some(path) = file_uri_to_path(&uri) else { + tracing::warn!("Could not convert root URI to a filesystem path: {uri}"); + continue; + }; + if path.join("Cargo.toml").exists() { + let path_str = path.to_str().expect("Invalid UTF-8 in path").to_owned(); + tracing::info!("Found Cargo project in root, setting as workspace: {path_str}"); + globals::try_set_workspace_root(path_str); + return; + } + tracing::debug!("No Cargo.toml found in root: {}", path.display()); + } + tracing::warn!("No Cargo project found in any client root; workspace unset"); + }); +} + +/// Convert a `file://` URI to a local filesystem path. +/// +/// Handles: +/// - `file:///path/to/dir` (Unix) +/// - `file:///C:/path/to/dir` (Windows, leading slash before drive letter stripped) +/// - `file://localhost/path` (optional localhost authority) +fn file_uri_to_path(uri: &str) -> Option { + let after_scheme = uri.strip_prefix("file://")?; + // Strip optional "localhost" authority (file://localhost/path) + let path_str = after_scheme + .strip_prefix("localhost") + .unwrap_or(after_scheme); + let path_str = strip_windows_drive_slash(path_str); + Some(std::path::PathBuf::from(path_str)) +} + +#[cfg(windows)] +fn strip_windows_drive_slash(path: &str) -> &str { + let bytes = path.as_bytes(); + if bytes.len() >= 3 && bytes[0] == b'/' && bytes[1].is_ascii_alphabetic() && bytes[2] == b':' { + &path[1..] + } else { + path + } +} + +#[cfg(not(windows))] +fn strip_windows_drive_slash(path: &str) -> &str { + path +} + +pub fn apply_workspace_root(cmd: &mut std::process::Command) { + if let Some(root) = globals::get_workspace_root() { + cmd.current_dir(root); + } +} From cb0bf433861704e579b674929db1b3b5bbd5b4fa Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:55:06 +0200 Subject: [PATCH 05/17] use PathBuf --- src/globals.rs | 16 ++++++++-------- src/tools/cargo/doc.rs | 2 +- src/workspace.rs | 8 +++++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/globals.rs b/src/globals.rs index 5839e84..10b0601 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -1,22 +1,22 @@ -use std::sync::OnceLock; +use std::{path::PathBuf, sync::OnceLock}; -static WORKSPACE_ROOT: OnceLock = OnceLock::new(); +static WORKSPACE_ROOT: OnceLock = OnceLock::new(); static DEFAULT_REGISTRY: OnceLock = OnceLock::new(); -pub fn set_workspace_root(root: String) { +pub fn set_workspace_root(root: impl Into) { WORKSPACE_ROOT - .set(root) + .set(root.into()) .expect("Workspace root can only be set once"); } /// Attempts to set the workspace root. Returns `true` if set successfully, /// `false` if it was already set (e.g. via CLI argument). -pub fn try_set_workspace_root(root: String) -> bool { - WORKSPACE_ROOT.set(root).is_ok() +pub fn try_set_workspace_root(root: impl Into) -> bool { + WORKSPACE_ROOT.set(root.into()).is_ok() } -pub fn get_workspace_root() -> Option<&'static str> { - WORKSPACE_ROOT.get().map(|s| s.as_str()) +pub fn get_workspace_root() -> Option<&'static PathBuf> { + WORKSPACE_ROOT.get() } pub fn set_default_registry(registry: String) { diff --git a/src/tools/cargo/doc.rs b/src/tools/cargo/doc.rs index e7d7677..a35f53a 100644 --- a/src/tools/cargo/doc.rs +++ b/src/tools/cargo/doc.rs @@ -275,7 +275,7 @@ impl CargoDocRequest { // Get the absolute path using workspace root let absolute_doc_dir = if let Some(workspace_root) = get_workspace_root() { - Path::new(workspace_root).join(&doc_dir) + workspace_root.join(&doc_dir) } else { Path::new(&doc_dir).to_path_buf() }; diff --git a/src/workspace.rs b/src/workspace.rs index 72a70fa..02d373b 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -66,9 +66,11 @@ pub fn detect_rust_workspace(context: NotificationContext) { continue; }; if path.join("Cargo.toml").exists() { - let path_str = path.to_str().expect("Invalid UTF-8 in path").to_owned(); - tracing::info!("Found Cargo project in root, setting as workspace: {path_str}"); - globals::try_set_workspace_root(path_str); + tracing::info!( + "Found Cargo project in root, setting as workspace: {}", + path.display() + ); + globals::try_set_workspace_root(path); return; } tracing::debug!("No Cargo.toml found in root: {}", path.display()); From 765e00298f314b39cc294b05b45f9449ddd7a890 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:30:17 +0200 Subject: [PATCH 06/17] fix uri parsing --- Cargo.lock | 7 ++++ Cargo.toml | 1 + src/workspace.rs | 96 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 82 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2839bed..f2a32d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -498,6 +498,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -614,6 +620,7 @@ dependencies = [ "anyhow", "clap", "insta", + "percent-encoding", "rmcp", "schemars", "serde", diff --git a/Cargo.toml b/Cargo.toml index f0ca348..9c0d2dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ include = [ [dependencies] anyhow = "1.0.98" clap = { version = "4.5.40", default-features = false, features = ["std", "help", "error-context", "usage", "suggestions", "derive", "string"] } +percent-encoding = "2.3.2" rmcp = { version = "1.1.1", default-features = false, features = ["server", "transport-io"] } schemars = "1.1.0" serde = { version ="1.0.219", features = ["derive"] } diff --git a/src/workspace.rs b/src/workspace.rs index 02d373b..51bc388 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -2,6 +2,13 @@ use rmcp::{model::Root, service::NotificationContext}; use crate::globals; +/// Applies the workspace root to a command if it is set +pub fn apply_workspace_root(cmd: &mut std::process::Command) { + if let Some(root) = globals::get_workspace_root() { + cmd.current_dir(root); + } +} + /// Decides whether automatic workspace detection is needed and, if so, /// tries to find a Cargo project through the MCP client's roots capability. /// @@ -84,34 +91,79 @@ pub fn detect_rust_workspace(context: NotificationContext) { /// Handles: /// - `file:///path/to/dir` (Unix) /// - `file:///C:/path/to/dir` (Windows, leading slash before drive letter stripped) +/// - `file:///d%3A/path` (Windows, percent-encoded colon in drive letter) /// - `file://localhost/path` (optional localhost authority) fn file_uri_to_path(uri: &str) -> Option { - let after_scheme = uri.strip_prefix("file://")?; - // Strip optional "localhost" authority (file://localhost/path) - let path_str = after_scheme - .strip_prefix("localhost") - .unwrap_or(after_scheme); - let path_str = strip_windows_drive_slash(path_str); - Some(std::path::PathBuf::from(path_str)) + let path = uri.strip_prefix("file://")?; + // Strip optional "localhost" authority + let path = path.strip_prefix("localhost").unwrap_or(path); + // Percent-decode the path component + let decoded = percent_encoding::percent_decode_str(path) + .decode_utf8() + .ok()?; + // On Windows, strip the leading slash before the drive letter (/C:/ -> C:/) + #[cfg(windows)] + let decoded = { + let b = decoded.as_bytes(); + // `/C:` + if b.len() >= 3 && b[0] == b'/' && b[1].is_ascii_alphabetic() && b[2] == b':' { + std::borrow::Cow::Borrowed(&decoded[1..]) + } else { + decoded + } + }; + Some(std::path::PathBuf::from(decoded.as_ref())) } -#[cfg(windows)] -fn strip_windows_drive_slash(path: &str) -> &str { - let bytes = path.as_bytes(); - if bytes.len() >= 3 && bytes[0] == b'/' && bytes[1].is_ascii_alphabetic() && bytes[2] == b':' { - &path[1..] - } else { - path +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_non_file_uri_returns_none() { + assert!(file_uri_to_path("https://example.com/path").is_none()); } -} -#[cfg(not(windows))] -fn strip_windows_drive_slash(path: &str) -> &str { - path -} + #[cfg(windows)] + #[test] + fn test_windows_drive_letter() { + let path = file_uri_to_path("file:///C:/Users/user/project").unwrap(); + assert_eq!(path, std::path::PathBuf::from("C:\\Users\\user\\project")); + } -pub fn apply_workspace_root(cmd: &mut std::process::Command) { - if let Some(root) = globals::get_workspace_root() { - cmd.current_dir(root); + #[cfg(windows)] + #[test] + fn test_windows_percent_encoded_colon() { + let path = file_uri_to_path("file:///d%3A/projects/myapp").unwrap(); + assert_eq!(path, std::path::PathBuf::from("d:\\projects\\myapp")); + } + + #[cfg(windows)] + #[test] + fn test_windows_percent_encoded_colon_uppercase() { + let path = file_uri_to_path("file:///D%3A/projects/myapp").unwrap(); + assert_eq!(path, std::path::PathBuf::from("D:\\projects\\myapp")); + } + + #[cfg(not(windows))] + #[test] + fn test_path_with_spaces() { + // Spaces encoded as %20 + let path = file_uri_to_path("file:///path%20with%20spaces").unwrap(); + assert_eq!(path, std::path::PathBuf::from("/path with spaces")); + } + + #[cfg(not(windows))] + #[test] + fn test_unix_path() { + let path = file_uri_to_path("file:///home/user/project").unwrap(); + assert_eq!(path, std::path::PathBuf::from("/home/user/project")); + } + + #[cfg(not(windows))] + #[test] + fn test_localhost_authority() { + let path = file_uri_to_path("file://localhost/home/user/project").unwrap(); + assert_eq!(path, std::path::PathBuf::from("/home/user/project")); } } From 07280f6b289ca98838c9d86824f94c03e187b65d Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:47:58 +0200 Subject: [PATCH 07/17] cleanup --- src/workspace.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/workspace.rs b/src/workspace.rs index 51bc388..7e4b8db 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -95,9 +95,7 @@ pub fn detect_rust_workspace(context: NotificationContext) { /// - `file://localhost/path` (optional localhost authority) fn file_uri_to_path(uri: &str) -> Option { let path = uri.strip_prefix("file://")?; - // Strip optional "localhost" authority let path = path.strip_prefix("localhost").unwrap_or(path); - // Percent-decode the path component let decoded = percent_encoding::percent_decode_str(path) .decode_utf8() .ok()?; @@ -107,12 +105,12 @@ fn file_uri_to_path(uri: &str) -> Option { let b = decoded.as_bytes(); // `/C:` if b.len() >= 3 && b[0] == b'/' && b[1].is_ascii_alphabetic() && b[2] == b':' { - std::borrow::Cow::Borrowed(&decoded[1..]) + &decoded[1..] } else { - decoded + &decoded } }; - Some(std::path::PathBuf::from(decoded.as_ref())) + Some(std::path::PathBuf::from(decoded)) } #[cfg(test)] From e0f34116820e38aab4a9f7d480c131dc84bf6125 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:53:25 +0200 Subject: [PATCH 08/17] logging --- src/workspace.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/workspace.rs b/src/workspace.rs index 7e4b8db..532c5e2 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -67,11 +67,14 @@ pub fn detect_rust_workspace(context: NotificationContext) { result.roots ); for Root { uri, .. } in result.roots { - tracing::info!("Checking root for Cargo project: {uri}"); let Some(path) = file_uri_to_path(&uri) else { tracing::warn!("Could not convert root URI to a filesystem path: {uri}"); continue; }; + tracing::info!( + "Checking root for Cargo project: {uri} -> {}", + path.display() + ); if path.join("Cargo.toml").exists() { tracing::info!( "Found Cargo project in root, setting as workspace: {}", From 611c452b127c51ec8359be5b6b36e3c1caa216af Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:55:35 +0200 Subject: [PATCH 09/17] generate lock file --- Cargo.lock | 259 ++++++++++++++++++----------------------------------- 1 file changed, 89 insertions(+), 170 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2a32d0..b11f45b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,15 +22,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "async-trait" @@ -57,9 +57,9 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" @@ -69,9 +69,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.56" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "shlex", @@ -85,9 +85,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.58" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.58" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstyle", "clap_lex", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", @@ -132,20 +132,19 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "console" -version = "0.15.11" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -171,9 +170,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "deranged" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -203,7 +202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -226,9 +225,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -241,9 +240,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -251,15 +250,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -268,15 +267,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -285,21 +284,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -309,7 +308,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -379,9 +377,9 @@ checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -391,9 +389,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.46.3" +version = "1.47.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" dependencies = [ "console", "once_cell", @@ -403,15 +401,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "once_cell", "wasm-bindgen", @@ -431,9 +429,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "linux-raw-sys" @@ -468,14 +466,14 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-traits" @@ -488,9 +486,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "pastey" @@ -506,15 +504,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "powerfmt" @@ -543,9 +535,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -589,15 +581,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rmcp" -version = "1.1.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4950422f87cf98fffc36946ad672c3024750b3c301491599715b7d6497dfbc" +checksum = "2231b2c085b371c01bc90c0e6c1cab8834711b6394533375bdbf870b0166d419" dependencies = [ "async-trait", "chrono", @@ -641,7 +633,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -678,9 +670,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -777,9 +769,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.115" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -788,15 +780,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -861,9 +853,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.49.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" dependencies = [ "bytes", "pin-project-lite", @@ -872,9 +864,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -951,9 +943,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -969,9 +961,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-xid" @@ -1005,9 +997,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -1018,9 +1010,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1028,9 +1020,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -1041,9 +1033,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -1141,15 +1133,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1159,70 +1142,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "wit-bindgen" version = "0.51.0" From 66069bb48b1b2846f2ad51b3cd006cc523ee16c0 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:17:28 +0200 Subject: [PATCH 10/17] always query roots with 10 seconds timeout --- Cargo.toml | 2 +- src/workspace.rs | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c0d2dc..e72b9ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ rmcp = { version = "1.1.1", default-features = false, features = ["server", "tra schemars = "1.1.0" serde = { version ="1.0.219", features = ["derive"] } serde_json = "1.0.140" -tokio = { version = "1.45.1", default-features = false, features = ["rt-multi-thread"] } +tokio = { version = "1.45.1", default-features = false, features = ["rt-multi-thread", "time"] } tracing = "0.1.41" tracing-appender = "0.2.3" tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } diff --git a/src/workspace.rs b/src/workspace.rs index 532c5e2..cadede8 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -41,17 +41,10 @@ pub fn detect_rust_workspace(context: NotificationContext) { .is_some(); tracing::info!("Checking client roots capability: supports_roots={supports_roots}"); - if !supports_roots { - tracing::warn!( - "Client does not support roots capability; workspace auto-detection is not possible" - ); - return; - } - // Spawn onto a separate task to avoid blocking the notification handler, // which would deadlock if the client waits for the server to finish // processing this notification before responding to roots/list. - tokio::spawn(async move { + let fut = async move { tracing::info!("Requesting workspace roots from client"); let result = match context.peer.list_roots().await { Ok(result) => result, @@ -86,6 +79,14 @@ pub fn detect_rust_workspace(context: NotificationContext) { tracing::debug!("No Cargo.toml found in root: {}", path.display()); } tracing::warn!("No Cargo project found in any client root; workspace unset"); + }; + + tokio::spawn(async move { + let _ = tokio::time::timeout(std::time::Duration::from_secs(10), fut) + .await + .inspect_err(|_| { + tracing::warn!("Workspace detection timed out after 10 seconds"); + }); }); } From 0f95daaf3279230a961a37efe6bc970b8442bda9 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:23:09 +0200 Subject: [PATCH 11/17] use debug logging --- .vscode/mcp.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.vscode/mcp.json b/.vscode/mcp.json index 58ea17e..666bc08 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -3,7 +3,12 @@ "rust-mcp-server": { "type": "stdio", "command": "./tmp/rust-mcp-server.exe", - "args": ["--log-file", "./tmp/rust-mcp-server.log"], + "args": [ + "--log-file", + "./tmp/rust-mcp-server.log", + "--log-level", + "debug" + ], } } -} +} \ No newline at end of file From f9d4fd45d7525911be24bf2669b0cbef0f1ac1df Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:31:07 +0200 Subject: [PATCH 12/17] comments --- src/workspace.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/workspace.rs b/src/workspace.rs index cadede8..f8fd4f9 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -9,13 +9,7 @@ pub fn apply_workspace_root(cmd: &mut std::process::Command) { } } -/// Decides whether automatic workspace detection is needed and, if so, -/// tries to find a Cargo project through the MCP client's roots capability. -/// -/// Decision tree: -/// 1. CWD contains `Cargo.toml` -> already in a Cargo project, nothing to do. -/// 2. Client supports roots -> spawn a task that iterates the roots and -/// sets the first one that contains a `Cargo.toml` as the workspace root. +/// If CWD contains `Cargo.toml` then function does nothing. Otherwise it tries to detect workspace root from client roots. pub fn detect_rust_workspace(context: NotificationContext) { let cwd = std::env::current_dir().ok(); tracing::info!("Checking current working directory for Cargo project: {cwd:?}"); @@ -33,7 +27,6 @@ pub fn detect_rust_workspace(context: NotificationContext) { ); } - // Step 3: ask the client for its workspace roots let supports_roots = context .peer .peer_info() From 17dd0366ab24a729d4e5cd2157b4aea07c566bb0 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:50:55 +0200 Subject: [PATCH 13/17] add lineline rules --- linelint.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 linelint.yml diff --git a/linelint.yml b/linelint.yml new file mode 100644 index 0000000..4d438be --- /dev/null +++ b/linelint.yml @@ -0,0 +1,8 @@ +ignore: +- .git/ +- *.json + +rules: + end-of-file: + enable: true + single-new-line: true From 9b7a2fd39bac50cb03346a9856dc79b101b696ab Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:53:14 +0200 Subject: [PATCH 14/17] fix non-windows build --- src/workspace.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/workspace.rs b/src/workspace.rs index f8fd4f9..60e0b52 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -96,6 +96,7 @@ fn file_uri_to_path(uri: &str) -> Option { let decoded = percent_encoding::percent_decode_str(path) .decode_utf8() .ok()?; + let decoded = decoded.as_ref(); // On Windows, strip the leading slash before the drive letter (/C:/ -> C:/) #[cfg(windows)] let decoded = { From 972c89e122127fde414666e718f5cd1c0cc856ac Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:23:45 +0200 Subject: [PATCH 15/17] fix file name --- linelint.yml => .linelint.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename linelint.yml => .linelint.yml (100%) diff --git a/linelint.yml b/.linelint.yml similarity index 100% rename from linelint.yml rename to .linelint.yml From 73c016eddaa28eafc7b464f9e914a605d8be021a Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:12:22 +0200 Subject: [PATCH 16/17] fix file name --- .linelint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.linelint.yml b/.linelint.yml index 4d438be..494a481 100644 --- a/.linelint.yml +++ b/.linelint.yml @@ -1,6 +1,6 @@ ignore: - .git/ -- *.json +- .json rules: end-of-file: From 8bcc9eb5e91f9eb360f9b974d31766b042817720 Mon Sep 17 00:00:00 2001 From: Vaiz <4908982+Vaiz@users.noreply.github.com> Date: Sun, 5 Apr 2026 17:47:49 +0200 Subject: [PATCH 17/17] remove linelint --- .github/workflows/rust.yml | 9 --------- .linelint.yml | 8 -------- 2 files changed, 17 deletions(-) delete mode 100644 .linelint.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 82fcd14..2aa2558 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -88,12 +88,3 @@ jobs: - name: cargo-deny run: cargo deny check --allow license-not-encountered - - linelint: - runs-on: ubuntu-latest - name: Linelint - - steps: - - uses: actions/checkout@v6 - - uses: fernandrone/linelint@0.0.6 - id: linelint diff --git a/.linelint.yml b/.linelint.yml deleted file mode 100644 index 494a481..0000000 --- a/.linelint.yml +++ /dev/null @@ -1,8 +0,0 @@ -ignore: -- .git/ -- .json - -rules: - end-of-file: - enable: true - single-new-line: true