Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **Stale document tracker on external file changes** — `DocumentTracker::ensure_open` now stats the file on every call and resyncs the LSP server via a single full-replacement `textDocument/didChange` when the on-disk content changed. Previously a file was only ever read once per session, so external edits (git checkout/stash, formatters, the MCP host's own Edit/Write tools) went unnoticed by mcpls and produced stale hover/diagnostics/completion results until the process restarted. (#102)
- **CodeQL fork PR checkout** — `actions/checkout@v7` refuses to check out a fork PR's head SHA in `pull_request_target` workflows unless explicitly opted in; set `allow-unsafe-pr-checkout: true` on the CodeQL workflow's checkout step, which was failing every fork-originated PR since the checkout v6 → v7 bump. Safe here because the job carries no secrets and permissions are scoped to `security-events: write` + `contents: read` only.
- **Notification loss and cached-read stalls under translator-lock contention** — `NotificationCache` is now held behind its own `Arc<Mutex<NotificationCache>>`, independent of `Arc<Mutex<Translator>>`, and workspace-root path validation for cache-only reads uses a lock-free `Arc<[PathBuf]>` snapshot instead of locking the translator. Previously, `diagnostics_pump` wrote into the cache through the translator lock: while that lock was held elsewhere for the duration of a slow `textDocument/diagnostic` round-trip, incoming `publishDiagnostics`/log/message notifications were silently dropped (the LSP transport forwards them via a non-blocking `try_send`), and `get_cached_diagnostics`/`read_resource`/`subscribe` — which only ever needed a cached read or a path check — queued behind that same round-trip for as long as it took to complete. (#104)
- **`get_cached_diagnostics` still held the cache lock across `canonicalize()` and result mapping** — the `Translator::handle_cached_diagnostics` split above narrowed but didn't fully close the same lock-contention class: path validation (a filesystem `canonicalize()` syscall) and the cached-diagnostics-to-MCP-shape mapping still ran while holding the `notification_cache` mutex that `diagnostics_pump` also needs. Split into `Translator::cached_diagnostics_uri` (path validation, no lock involved) and `Translator::diagnostics_from_cache_entry` (pure mapping over an already-cloned entry, no lock involved); `get_cached_diagnostics` now holds the cache lock only for the `get_diagnostics(&uri).cloned()` lookup itself. (#104)
- **`read_resource` used the raw, non-canonicalized path to build its diagnostics-cache URI** — `validate_path_against_roots` returns the canonicalized path but the result was discarded, so `read_resource` looked up the cache under the client's original (possibly symlinked or `..`-containing) path instead of the canonical form `diagnostics_pump` actually stores diagnostics under, causing spurious cache misses for otherwise-valid, tracked files. Fixed to build the URI from the validated/canonical path, matching `get_cached_diagnostics`. (#104)

### Changed

- **`DocumentState` API** — Breaking change: `DocumentState` gains a `disk: Option<DiskSync>` field tracking the filesystem snapshot behind the resync mechanism above; existing struct-literal construction must add this field. Acceptable pre-1.0. (#102)
- **`rmcp` 2.2.0** — Breaking change upstream: bump `rmcp` from 1.8.0 to 2.2.0 to align with the MCP 2025-11-25 spec. `rmcp::model::RawResource` and the `Annotated<RawResource>` wrapper were merged into a single flat `rmcp::model::Resource` struct; `McplsServer::list_resources` updated accordingly.
- **`McplsServer::new` / `HandlerContext::new` signatures** — Breaking change: both now take an additional `Arc<Mutex<NotificationCache>>` and an `Arc<[PathBuf]>` workspace-roots snapshot, alongside the existing translator and subscriptions. `Translator::handle_cached_diagnostics` was removed and replaced by `Translator::cached_diagnostics_uri(workspace_roots, file_path) -> Result<String>` and `Translator::diagnostics_from_cache_entry(Option<&DiagnosticInfo>) -> DiagnosticsResult`, so callers control exactly how long the `NotificationCache` lock is held between the two; `Translator::handle_server_logs`/`handle_server_messages` became associated functions taking `&NotificationCache` directly, since neither needed translator state. Acceptable pre-1.0. (#104)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions crates/mcpls-core/src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use notifications::{
};
pub use resources::ResourceSubscriptions;
pub use state::{DocumentState, DocumentTracker, path_to_uri, uri_to_path};
pub(crate) use translator::validate_path_against_roots;
pub use translator::{
Completion, CompletionsResult, DefinitionResult, Diagnostic, DiagnosticSeverity,
DiagnosticsResult, DocumentChanges, DocumentSymbolsResult, FormatDocumentResult, HoverResult,
Expand Down
Loading
Loading