bear-mcp-server 0.1.1

A Model Context Protocol server for Bear.app
Documentation
#![deny(unused_crate_dependencies)]

use anyhow as _;
use async_trait as _;
use chrono as _;
use dirs as _;
use futures as _;
use rmcp_macros as _;
use schemars as _;
use serde as _;
use sqlx as _;
use structopt as _;
use thiserror as _;

use bear_mcp_server::Result;
use bear_mcp_server::mcp::BearMcpServer;
use dotenv::dotenv;
use rmcp::ServiceExt;
use rmcp::transport::stdio;
use structopt::StructOpt;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::writer::MakeWriterExt;

const DEFAULT_DB_PATH: &str =
    "./Library/Group Containers/9K33E3U3T4.net.shinyfrog.bear/Application Data/database.sqlite";

fn default_db_path() -> std::path::PathBuf {
    dirs::home_dir()
        .expect("must succeed")
        .join(DEFAULT_DB_PATH)
        .canonicalize()
        .expect("must succeed")
}

#[derive(Debug, StructOpt)]
#[structopt(
    name = "bear-mcp-server",
    about = "A Model Context Protocol server for Bear.app"
)]
struct Cli {
    #[structopt(long, env = "BEAR_DB_PATH", help = "Path to the Bear.app database")]
    bear_db_path: Option<std::path::PathBuf>,
}

#[tokio::main]
async fn main() -> Result<()> {
    dotenv().ok();

    let cli = Cli::from_args();
    let bear_db_path = cli
        .bear_db_path
        .unwrap_or_else(default_db_path)
        .canonicalize()?;

    let rolling_log_file_directory_path = dirs::data_local_dir()
        .expect("must succeed")
        .join(env!("CARGO_PKG_NAME"))
        .join("logs");
    tokio::fs::create_dir_all(&rolling_log_file_directory_path).await?;

    let rolling_file_appender =
        tracing_appender::rolling::daily(rolling_log_file_directory_path, "");

    tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::from_default_env().add_directive(tracing::Level::DEBUG.into()))
        .with_writer(std::io::stderr.and(rolling_file_appender))
        .with_ansi(false)
        .init();

    tracing::info!("Starting the MCP server…");

    let server = BearMcpServer::new(bear_db_path).await?;

    tracing::info!("Initialised the server.");

    let service = server
        .serve(stdio())
        .await
        .map_err(|e| bear_mcp_server::Error::Other(e.to_string()))?;

    tracing::info!("Set up the service.");

    service
        .waiting()
        .await
        .map_err(|e| bear_mcp_server::Error::Other(e.to_string()))?;

    Ok(())
}