Skip to main content

lean_ctx/tools/registered/
ctx_artifacts.rs

1use std::path::Path;
2
3use rmcp::ErrorData;
4use rmcp::model::Tool;
5use serde_json::{Map, Value, json};
6
7use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput, get_str, get_usize};
8use crate::tool_defs::tool_def;
9
10pub struct CtxArtifactsTool;
11
12impl McpTool for CtxArtifactsTool {
13    fn name(&self) -> &'static str {
14        "ctx_artifacts"
15    }
16
17    fn tool_def(&self) -> Tool {
18        tool_def(
19            "ctx_artifacts",
20            "Context artifact registry with BM25 search — manage and query indexed code artifacts.\n\
21            WORKFLOW: index artifacts first (index/reindex), then search with query for semantic retrieval.\n\
22            Actions: list|status|index|reindex|search|remove.\n\
23            ANTIPATTERN: NOT for general code search — use ctx_semantic_search for codebase queries.",
24            json!({
25                "type": "object",
26                "properties": {
27                    "action": {
28                        "type": "string",
29                        "enum": ["list", "status", "index", "reindex", "search", "remove"],
30                        "description": "list|status|index|reindex|search|remove"
31                    },
32                    "project_root": {
33                        "type": "string",
34                        "description": "Project root"
35                    },
36                    "query": {
37                        "type": "string",
38                        "description": "Search query"
39                    },
40                    "name": {
41                        "type": "string",
42                        "description": "Artifact name"
43                    },
44                    "top_k": {
45                        "type": "integer",
46                        "description": "Max results (default depends on action, capped at 1000)"
47                    },
48                    "format": {
49                        "type": "string",
50                        "enum": ["json", "markdown"],
51                        "description": "json|markdown"
52                    }
53                },
54                "required": ["action"]
55            }),
56        )
57    }
58
59    fn handle(
60        &self,
61        args: &Map<String, Value>,
62        ctx: &ToolContext,
63    ) -> Result<ToolOutput, ErrorData> {
64        let action = get_str(args, "action")
65            .ok_or_else(|| ErrorData::invalid_params("action is required", None))?;
66        let format = get_str(args, "format");
67        let query = get_str(args, "query");
68        let name = get_str(args, "name");
69        let top_k = get_usize(args, "top_k").map(|d| d.min(1000));
70        let root = if let Some(p) = ctx
71            .resolved_path("project_root")
72            .or(ctx.resolved_path("root"))
73        {
74            p
75        } else if let Some(err) = ctx.path_error("project_root").or(ctx.path_error("root")) {
76            return Err(ErrorData::invalid_params(format!("root: {err}"), None));
77        } else {
78            &ctx.project_root
79        };
80
81        let result = crate::tools::ctx_artifacts::handle(
82            &action,
83            Path::new(root),
84            name.as_deref().or(query.as_deref()),
85            top_k,
86            format.as_deref(),
87        );
88
89        Ok(ToolOutput::simple(result))
90    }
91}