use rmcp::ErrorData as McpError;
use rmcp::handler::server::wrapper::Parameters;
use rmcp::model::CallToolResult;
use rmcp::tool;
use serde_json::Value;
use super::BasemindServer;
use super::helpers::record_call;
use super::types::{
MemoryDeleteParams, MemoryGetParams, MemoryListParams, MemoryPutParams, MemorySearchParams,
SearchDocumentsParams,
};
fn not_enabled(feature: &'static str) -> Result<CallToolResult, McpError> {
Err(McpError::internal_error(
format!("{feature} feature not enabled"),
None,
))
}
#[rmcp::tool_router(vis = "pub(super)", router = "tool_router_memory")]
impl BasemindServer {
#[tool(
description = "Persist key-value in scoped memory (scope = git remote URL). \
embed=true stores in LanceDB for memory_search. Upsert semantics. \
Needs --features memory."
)]
async fn memory_put(
&self,
Parameters(p): Parameters<MemoryPutParams>,
) -> Result<CallToolResult, McpError> {
let __started = std::time::Instant::now();
let __params_json = serde_json::to_value(&p).unwrap_or(Value::Null);
let __result: Result<CallToolResult, McpError> = async {
#[cfg(feature = "memory")]
{
return super::memory::run_memory_put(&self.state, p).await;
}
#[cfg(not(feature = "memory"))]
{
let _ = p;
return not_enabled("memory");
}
#[allow(unreachable_code)]
not_enabled("memory")
}
.await;
record_call(
&self.state,
"memory_put",
&__params_json,
__started,
&__result,
);
__result
}
#[tool(description = "Exact-key lookup in scoped memory. Returns entry \
(key,value,tags,timestamps) or null. Fjall only, no vector touch. \
Needs --features memory.")]
async fn memory_get(
&self,
Parameters(p): Parameters<MemoryGetParams>,
) -> Result<CallToolResult, McpError> {
let __started = std::time::Instant::now();
let __params_json = serde_json::to_value(&p).unwrap_or(Value::Null);
let __result: Result<CallToolResult, McpError> = async {
#[cfg(feature = "memory")]
{
return super::memory::run_memory_get(&self.state, p).await;
}
#[cfg(not(feature = "memory"))]
{
let _ = p;
return not_enabled("memory");
}
#[allow(unreachable_code)]
not_enabled("memory")
}
.await;
record_call(
&self.state,
"memory_get",
&__params_json,
__started,
&__result,
);
__result
}
#[tool(
description = "List scoped memory entries. prefix is key-prefix filter \
(not substring). tag is exact. Values truncated ~200 chars. \
Default 100 max 1000. Pass `cursor` from a previous response to fetch the \
next page; absent means no more results. Needs --features memory."
)]
async fn memory_list(
&self,
Parameters(p): Parameters<MemoryListParams>,
) -> Result<CallToolResult, McpError> {
let __started = std::time::Instant::now();
let __params_json = serde_json::to_value(&p).unwrap_or(Value::Null);
let __result: Result<CallToolResult, McpError> = async {
#[cfg(feature = "memory")]
{
return super::memory::run_memory_list(&self.state, p).await;
}
#[cfg(not(feature = "memory"))]
{
let _ = p;
return not_enabled("memory");
}
#[allow(unreachable_code)]
not_enabled("memory")
}
.await;
record_call(
&self.state,
"memory_list",
&__params_json,
__started,
&__result,
);
__result
}
#[tool(
description = "Vector KNN over stored memory. Embeds query, KNN in LanceDB \
memory table (scope-filtered). tag is post-KNN exact filter. \
Default 10 max 100 by L2 distance. Needs --features memory."
)]
async fn memory_search(
&self,
Parameters(p): Parameters<MemorySearchParams>,
) -> Result<CallToolResult, McpError> {
let __started = std::time::Instant::now();
let __params_json = serde_json::to_value(&p).unwrap_or(Value::Null);
let __result: Result<CallToolResult, McpError> = async {
#[cfg(feature = "memory")]
{
return super::memory::run_memory_search(&self.state, p).await;
}
#[cfg(not(feature = "memory"))]
{
let _ = p;
return not_enabled("memory");
}
#[allow(unreachable_code)]
not_enabled("memory")
}
.await;
record_call(
&self.state,
"memory_search",
&__params_json,
__started,
&__result,
);
__result
}
#[tool(
description = "Delete memory entry by exact key from Fjall and LanceDB. \
Returns {deleted:true} when found. Needs --features memory."
)]
async fn memory_delete(
&self,
Parameters(p): Parameters<MemoryDeleteParams>,
) -> Result<CallToolResult, McpError> {
let __started = std::time::Instant::now();
let __params_json = serde_json::to_value(&p).unwrap_or(Value::Null);
let __result: Result<CallToolResult, McpError> = async {
#[cfg(feature = "memory")]
{
return super::memory::run_memory_delete(&self.state, p).await;
}
#[cfg(not(feature = "memory"))]
{
let _ = p;
return not_enabled("memory");
}
#[allow(unreachable_code)]
not_enabled("memory")
}
.await;
record_call(
&self.state,
"memory_delete",
&__params_json,
__started,
&__result,
);
__result
}
#[tool(
description = "Semantic search over indexed document chunks (PDF/Office/HTML). \
Embeds query, KNN in LanceDB documents table (scope-filtered). \
mime_type is exact filter. Default 10 max 100. Needs --features documents."
)]
async fn search_documents(
&self,
Parameters(p): Parameters<SearchDocumentsParams>,
) -> Result<CallToolResult, McpError> {
let __started = std::time::Instant::now();
let __params_json = serde_json::to_value(&p).unwrap_or(Value::Null);
let __result: Result<CallToolResult, McpError> = async {
#[cfg(feature = "documents")]
{
return super::memory::run_search_documents(&self.state, p).await;
}
#[cfg(not(feature = "documents"))]
{
let _ = p;
return not_enabled("documents");
}
#[allow(unreachable_code)]
not_enabled("documents")
}
.await;
record_call(
&self.state,
"search_documents",
&__params_json,
__started,
&__result,
);
__result
}
}