lean_ctx/tools/registered/
ctx_plan.rs1use rmcp::ErrorData;
2use rmcp::model::Tool;
3use serde_json::{Map, Value, json};
4
5use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput};
6use crate::tool_defs::tool_def;
7
8pub struct CtxPlanTool;
9
10impl McpTool for CtxPlanTool {
11 fn name(&self) -> &'static str {
12 "ctx_plan"
13 }
14
15 fn tool_def(&self) -> Tool {
16 tool_def(
17 "ctx_plan",
18 "WORKFLOW: set task+profile -> ctx_plan -> use results with ctx_read/ctx_compose.\n\
19 ANTIPATTERN: NOT for compressing already-selected files (use ctx_fill).\n\
20 Selects files for context via Phi scoring + budget + policy.\n\
21 task=short English; budget=token limit (default 12000);\n\
22 profile=ultra_lean|balanced|forensic. Saves tokens by prioritizing relevant files.",
23 json!({
24 "type": "object",
25 "properties": {
26 "task": { "type": "string", "description": "Task description (short English preferred)" },
27 "budget": { "type": "integer", "description": "Token budget limit (default: 12000)" },
28 "profile": { "type": "string", "description": "ultra_lean (minimal)|balanced (default)|forensic (exhaustive)" }
29 },
30 "required": ["task"]
31 }),
32 )
33 }
34
35 fn handle(
36 &self,
37 args: &Map<String, Value>,
38 ctx: &ToolContext,
39 ) -> Result<ToolOutput, ErrorData> {
40 let ledger = crate::core::context_ledger::ContextLedger::load();
41
42 let root = if let Some(ref session_lock) = ctx.session {
43 crate::server::bounded_lock::read(session_lock, "ctx_plan:session")
44 .as_ref()
45 .and_then(|s| s.project_root.clone())
46 .unwrap_or_else(|| ctx.project_root.clone())
47 } else {
48 ctx.project_root.clone()
49 };
50
51 let policies = crate::core::context_policies::PolicySet::load_project(
52 &std::path::PathBuf::from(&root),
53 );
54 let result = crate::tools::ctx_plan::handle(Some(args), &ledger, &policies);
55
56 Ok(ToolOutput::simple(result))
57 }
58}