lean-ctx 3.6.2

Context Runtime for AI Agents with CCP. 51 MCP tools, 10 read modes, 60+ compression patterns, cross-session memory (CCP), persistent AI knowledge with temporal facts + contradiction detection, multi-agent context sharing, LITM-aware positioning, AAAK compact format, adaptive compression with Thompson Sampling bandits. Supports 24+ AI tools. Reduces LLM token consumption by up to 99%.
Documentation
use rmcp::model::Tool;
use rmcp::ErrorData;
use serde_json::{json, Map, Value};

use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput};
use crate::tool_defs::tool_def;

pub struct CtxRadarTool;

impl McpTool for CtxRadarTool {
    fn name(&self) -> &'static str {
        "ctx_radar"
    }

    fn tool_def(&self) -> Tool {
        tool_def(
            "ctx_radar",
            "Full context budget breakdown: system prompt, messages, tools, reads, shell — all tracked token usage.",
            json!({
                "type": "object",
                "properties": {
                    "format": {
                        "type": "string",
                        "description": "Output format: 'display' (human-readable) or 'json' (structured)",
                        "enum": ["display", "json"],
                        "default": "display"
                    }
                }
            }),
        )
    }

    fn handle(
        &self,
        args: &Map<String, Value>,
        ctx: &ToolContext,
    ) -> Result<ToolOutput, ErrorData> {
        let format = args
            .get("format")
            .and_then(|v| v.as_str())
            .unwrap_or("display");

        let data_dir = crate::core::data_dir::lean_ctx_data_dir().unwrap_or_else(|_| {
            std::path::PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()))
                .join(".lean-ctx")
        });

        let client_name = ctx
            .client_name
            .as_ref()
            .and_then(|cn| tokio::task::block_in_place(|| cn.blocking_read().clone()).into())
            .unwrap_or_else(|| "cursor".to_string());
        let window_size = crate::core::context_radar::default_window_for_client(&client_name);

        let radar = crate::core::context_radar::ContextRadar::load(&data_dir, window_size);

        let output = match format {
            "json" => {
                let breakdown = radar.budget_breakdown();
                serde_json::to_string_pretty(&breakdown).unwrap_or_default()
            }
            _ => radar.format_display(),
        };

        Ok(ToolOutput::simple(output))
    }
}