remembrall_server/tools/
ingest.rs1use std::sync::Arc;
11
12use rmcp::{ErrorData as McpError, model::*, schemars};
13use serde_json::json;
14
15use remembrall_core::{embed::Embedder, ingest, memory::store::MemoryStore};
16
17#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
22pub struct IngestGithubParams {
23 #[schemars(description = "GitHub repo in owner/repo format (e.g. 'owner/repo')")]
24 pub repo: String,
25 #[schemars(description = "Maximum number of recent merged PRs to ingest (default 50, max 200)")]
26 pub limit: Option<u32>,
27 #[schemars(description = "Project name to tag memories with (defaults to the repo name)")]
28 pub project: Option<String>,
29}
30
31#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
32pub struct IngestDocsParams {
33 #[schemars(description = "Path to project directory to scan for markdown files")]
34 pub path: String,
35 #[schemars(description = "Project name to tag memories with")]
36 pub project: Option<String>,
37}
38
39pub async fn ingest_github_impl(
44 memory: &Arc<MemoryStore>,
45 embedder: &Arc<dyn Embedder>,
46 params: IngestGithubParams,
47) -> Result<CallToolResult, McpError> {
48 let IngestGithubParams { repo, limit, project } = params;
49
50 let effective_project = project.clone().unwrap_or_else(|| {
53 repo.split('/').last().unwrap_or("unknown").to_string()
54 });
55
56 let result = ingest::ingest_github_prs(
57 &repo,
58 limit.map(|l| l as i32),
59 project.as_deref(),
60 memory.as_ref(),
61 Arc::clone(embedder),
62 )
63 .await
64 .map_err(|e| McpError::internal_error(e.to_string(), None))?;
65
66 let text = json!({
67 "repo": repo,
68 "project": effective_project,
69 "memories_stored": result.memories_stored,
70 "memories_skipped": result.memories_skipped,
71 "errors": result.errors,
72 })
73 .to_string();
74
75 Ok(CallToolResult::success(vec![Content::text(text)]))
76}
77
78pub async fn ingest_docs_impl(
79 memory: &Arc<MemoryStore>,
80 embedder: &Arc<dyn Embedder>,
81 params: IngestDocsParams,
82) -> Result<CallToolResult, McpError> {
83 let IngestDocsParams { path, project } = params;
84
85 let effective_project = project.clone().unwrap_or_else(|| {
88 std::path::Path::new(&path)
89 .file_name()
90 .and_then(|n| n.to_str())
91 .unwrap_or("unknown")
92 .to_string()
93 });
94
95 let result = ingest::ingest_docs(
96 &path,
97 project.as_deref(),
98 memory.as_ref(),
99 Arc::clone(embedder),
100 )
101 .await
102 .map_err(|e| McpError::internal_error(e.to_string(), None))?;
103
104 let text = json!({
105 "path": path,
106 "project": effective_project,
107 "memories_stored": result.memories_stored,
108 "memories_skipped": result.memories_skipped,
109 "errors": result.errors,
110 })
111 .to_string();
112
113 Ok(CallToolResult::success(vec![Content::text(text)]))
114}