1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Translation layer between MCP and LSP protocols.
//!
//! This module handles the bidirectional conversion between
//! MCP tool calls and LSP requests/responses.
use ;
use ;
use Uri;
pub use ;
// Not part of the crate's public API surface (unlike `IndexingPolicy`/`IndexingState`
// above, both referenced from public signatures) -- these three exist only
// for `config`'s default-value/validation wiring, so `pub(crate)` avoids
// widening the public surface and keeps `indexing.rs`'s own intra-doc links
// to private items (`IndexingTracker::state`, `PROGRESS_LATCH_IDLE`) valid.
pub use ;
pub use ;
pub use apply_lifecycle_notification;
pub use ;
pub use ;
pub use try_path_to_uri;
pub use ;
pub use validate_path_against_roots;
pub use ;
/// Whether `uri` resolves to a path within one of `workspace_roots`.
///
/// The single containment check shared by every path where mcpls forwards a
/// server-supplied URI it is about to *write through* or otherwise treat as
/// authoritative on the MCP client's behalf: the diagnostics pump
/// (`crate::diagnostic_path_in_workspace`) and rename/code-action
/// `WorkspaceEdit` results (`bridge::translator::edits`). A compromised or
/// misbehaving LSP server is treated as untrusted input on both, so they
/// must reject the same out-of-workspace URIs the same way rather than risk
/// the two checks drifting apart.
///
/// Deliberately **not** applied to read-only navigation results
/// (`get_definition`/`get_references`/`get_implementation`/
/// `get_type_definition`/call hierarchy, and `search_workspace_symbols`):
/// a legitimate result routinely points outside the workspace (the standard
/// library, a crates.io dependency), so dropping those would break ordinary
/// navigation. Any subsequent attempt to open or read the path such a
/// location names still goes through the inbound
/// `validate_path_against_roots` gate (`mcp/server.rs`), which fails closed,
/// so the untrusted-URI concern is already covered downstream for that case.
/// `get_document_symbols`' legacy flat (`SymbolInformation`) response shape
/// takes a different approach again: rather than filter, it normalizes every
/// entry against the already-resolved, trusted queried document instead of
/// trusting the server-reported per-entry URI (see
/// `bridge::translator::symbols::handle_document_symbols`), since
/// `document_symbols` is a single-document request by construction.
///
/// Deliberately does not canonicalize -- this runs on every response
/// location and notification, and LSP servers report already-resolved
/// canonical paths, so a prefix check is enough to reject a URI a legitimate
/// server would never publish, without a filesystem syscall per call.
///
/// # Preconditions
///
/// `workspace_roots` must itself already be canonical, or every URI silently
/// fails to match and is dropped. [`Translator::workspace_roots`] and
/// `serve_with`'s `workspace_roots_snapshot` both satisfy this via
/// `resolve_workspace_roots`.
///
/// An empty `workspace_roots` (no workspace configured) rejects every URI,
/// matching [`validate_path_against_roots`]'s fail-closed
/// `Error::NoWorkspaceRoots` behavior: without a configured root there is
/// nothing to treat as authoritative, so no server-supplied URI is trusted.
pub
/// Lock a `std::sync::Mutex`, recovering the guard if a previous holder
/// panicked while holding it.
///
/// Every lock guarded this way protects a short, synchronous, panic-free
/// critical section (a `HashMap`/`HashSet` lookup or insert), so poisoning
/// can only happen if an unrelated bug already panicked; refusing to unwind
/// the whole process a second time over stale poisoning is preferable to
/// deadlocking future calls. Shared by `translator` and `state` so both
/// modules lock their interior `HashMap`/`HashSet` fields the same way.
pub