Skip to main content

lean_ctx/core/context_kernel/
mcp_coverage.rs

1//! MCP-specific client coverage detection and efficiency profiles.
2
3use super::client_profile::{ClientEfficiencyProfile, ProfileBuilder};
4use super::client_wiring::OptimizationLevel;
5use super::coverage_class::CoverageClass;
6
7/// Detects the context coverage available for an MCP client and its capabilities.
8#[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/// Builds the kernel efficiency profile for an MCP client.
24#[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/// Returns the optimization level supported by an MCP client.
39#[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/// Returns the maximum schema-token budget for an MCP client.
50#[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}