lean_ctx/core/context_kernel/
mcp_coverage.rs1use super::client_profile::{ClientEfficiencyProfile, ProfileBuilder};
4use super::client_wiring::OptimizationLevel;
5use super::coverage_class::CoverageClass;
6
7#[must_use]
9pub fn detect_mcp_coverage(client: &str, has_roots: bool, has_sampling: bool) -> CoverageClass {
10 let base = match client.to_ascii_lowercase().as_str() {
11 "cursor" | "cursor-ide" => CoverageClass::FullInline,
12 "vscode" | "visual-studio-code" | "zed" => CoverageClass::ContextControlled,
13 _ => CoverageClass::ObserveOnly,
14 };
15
16 if base == CoverageClass::ObserveOnly && has_roots && has_sampling {
17 CoverageClass::ContextControlled
18 } else {
19 base
20 }
21}
22
23#[must_use]
25pub fn mcp_client_profile(client: &str) -> ClientEfficiencyProfile {
26 let context_window = match client.to_ascii_lowercase().as_str() {
27 "cursor" | "cursor-ide" => 200_000,
28 "vscode" | "visual-studio-code" => 128_000,
29 _ => 64_000,
30 };
31
32 ProfileBuilder::new(client)
33 .coverage(detect_mcp_coverage(client, false, false))
34 .context_window(context_window)
35 .build()
36}
37
38#[must_use]
40pub fn mcp_optimization_level(client: &str) -> OptimizationLevel {
41 match detect_mcp_coverage(client, false, false) {
42 CoverageClass::FullInline => OptimizationLevel::Full,
43 CoverageClass::ContextControlled => OptimizationLevel::Partial,
44 CoverageClass::ObserveOnly => OptimizationLevel::ObserveOnly,
45 CoverageClass::Unmanaged => OptimizationLevel::None,
46 }
47}
48
49#[must_use]
51pub fn client_schema_budget(client: &str) -> usize {
52 mcp_client_profile(client).tool_budget.max_schema_tokens
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn cursor_is_full_inline() {
61 assert_eq!(
62 detect_mcp_coverage("cursor", false, false),
63 CoverageClass::FullInline
64 );
65 }
66
67 #[test]
68 fn vscode_is_context_controlled() {
69 assert_eq!(
70 detect_mcp_coverage("vscode", false, false),
71 CoverageClass::ContextControlled
72 );
73 }
74
75 #[test]
76 fn claude_is_observe_only() {
77 assert_eq!(
78 detect_mcp_coverage("claude-code", false, false),
79 CoverageClass::ObserveOnly
80 );
81 }
82
83 #[test]
84 fn unknown_is_observe_only() {
85 assert_eq!(
86 detect_mcp_coverage("random-client", false, false),
87 CoverageClass::ObserveOnly
88 );
89 }
90
91 #[test]
92 fn roots_sampling_upgrades() {
93 assert_eq!(
94 detect_mcp_coverage("random-client", true, true),
95 CoverageClass::ContextControlled
96 );
97 }
98
99 #[test]
100 fn profile_context_window_varies() {
101 assert_eq!(mcp_client_profile("cursor").context_window, 200_000);
102 assert_eq!(mcp_client_profile("vscode").context_window, 128_000);
103 assert_eq!(mcp_client_profile("random-client").context_window, 64_000);
104 }
105
106 #[test]
107 fn optimization_matches_coverage() {
108 assert_eq!(mcp_optimization_level("cursor"), OptimizationLevel::Full);
109 assert_eq!(mcp_optimization_level("vscode"), OptimizationLevel::Partial);
110 assert_eq!(
111 mcp_optimization_level("claude"),
112 OptimizationLevel::ObserveOnly
113 );
114 }
115
116 #[test]
117 fn schema_budget_comes_from_profile() {
118 assert_eq!(
119 client_schema_budget("cursor"),
120 mcp_client_profile("cursor").tool_budget.max_schema_tokens
121 );
122 }
123}