1use crate::bear::BearDatabase;
2use rmcp::{
3 ErrorData as McpError,
4 handler::server::tool::{Parameters, ToolRouter},
5 model::*,
6 tool, tool_handler, tool_router,
7};
8use std::sync::Arc;
9use tokio::sync::Mutex;
10
11#[derive(Clone)]
12pub struct BearMcpServer {
13 bear_database: Arc<Mutex<BearDatabase>>,
14 tool_router: ToolRouter<Self>,
15}
16
17#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
18pub struct SearchRequest {
19 #[schemars(description = "search query")]
20 pub query: Option<String>,
21 #[schemars(description = "tag")]
22 pub tag: Option<String>,
23}
24
25#[tool_router]
26impl BearMcpServer {
27 pub async fn new(db_path: std::path::PathBuf) -> crate::Result<Self> {
28 let bear_database = Arc::new(Mutex::new(BearDatabase::new(db_path).await?));
29 Ok(Self {
30 bear_database,
31 tool_router: Self::tool_router(),
32 })
33 }
34
35 #[tool(description = "Search all notes")]
36 async fn list_notes(
37 &self,
38 Parameters(SearchRequest { query, tag }): Parameters<SearchRequest>,
39 ) -> std::result::Result<CallToolResult, McpError> {
40 #[derive(serde::Serialize)]
41 struct NotesResponse<T> {
42 notes: T,
43 }
44
45 let tag_noramlized = tag.as_deref().map(|tag| tag.trim_start_matches('#'));
46
47 let database = self.bear_database.lock().await;
48 let notes = database
49 .list_notes(query.as_deref(), tag_noramlized)
50 .await?;
51 let response = NotesResponse { notes };
52 Ok(CallToolResult::success(vec![Content::json(&response)?]))
53 }
54
55 #[tool(description = "List tags")]
56 async fn list_tags(&self) -> std::result::Result<CallToolResult, McpError> {
57 #[derive(serde::Serialize)]
58 struct TagsResponse<T> {
59 tags: T,
60 }
61
62 let database = self.bear_database.lock().await;
63 let tags = database.list_tags().await?;
64 let response = TagsResponse { tags };
65 Ok(CallToolResult::success(vec![Content::json(&response)?]))
66 }
67}
68
69#[tool_handler]
70impl rmcp::ServerHandler for BearMcpServer {
71 fn get_info(&self) -> ServerInfo {
72 ServerInfo {
73 protocol_version: ProtocolVersion::V_2025_03_26,
74 capabilities: ServerCapabilities::builder().enable_tools().build(),
75 server_info: Implementation::from_build_env(),
76 instructions: Some("A server for notes stored in the Bear app".into()),
77 }
78 }
79}