ebook 0.1.2

A CLI tool for reading, writing, and operating on various ebook formats
Documentation
use super::args::*;
use super::tool_impl;
use rmcp::{
    ErrorData as McpError, ServerHandler, ServiceExt,
    handler::server::{tool::ToolRouter, wrapper::Parameters},
    model::{CallToolResult, Implementation, ServerCapabilities, ServerInfo},
    tool, tool_handler, tool_router,
    transport::stdio,
};

#[derive(Clone)]
pub struct EbookMcp {
    tool_router: ToolRouter<Self>,
}

#[tool_router]
impl EbookMcp {
    pub fn new() -> Self {
        Self {
            tool_router: Self::tool_router(),
        }
    }

    #[tool(
        name = "read_ebook",
        description = "Read an ebook file and extract content/metadata/TOC (supports: epub, pdf, txt, mobi, fb2, azw, cbz)"
    )]
    async fn read_ebook(
        &self,
        Parameters(args): Parameters<ReadEbookArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::read_ebook(args).await)
    }

    #[tool(
        name = "write_ebook",
        description = "Create a new ebook file from text content (supports: epub, pdf, txt, mobi, fb2, azw)"
    )]
    async fn write_ebook(
        &self,
        Parameters(args): Parameters<WriteEbookArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::write_ebook(args).await)
    }

    #[tool(name = "extract_images", description = "Extract images from an ebook (EPUB, CBZ)")]
    async fn extract_images(
        &self,
        Parameters(args): Parameters<ExtractImagesArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::extract_images(args).await)
    }

    #[tool(
        name = "validate_ebook",
        description = "Validate an ebook file structure (supports: epub, pdf, txt, mobi, fb2, azw, cbz)"
    )]
    async fn validate_ebook(
        &self,
        Parameters(args): Parameters<ValidateEbookArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::validate_ebook(args).await)
    }

    #[tool(
        name = "get_ebook_info",
        description = "Get detailed information about an ebook file"
    )]
    async fn get_ebook_info(
        &self,
        Parameters(args): Parameters<GetEbookInfoArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::get_ebook_info(args).await)
    }

    #[tool(
        name = "convert_ebook",
        description = "Convert an ebook from one format to another"
    )]
    async fn convert_ebook(
        &self,
        Parameters(args): Parameters<ConvertEbookArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::convert_ebook(args).await)
    }

    #[tool(
        name = "optimize_images",
        description = "Optimize images in EPUB or CBZ by resizing and compressing"
    )]
    async fn optimize_images(
        &self,
        Parameters(args): Parameters<OptimizeImagesArgs>,
    ) -> Result<CallToolResult, McpError> {
        Ok(tool_impl::optimize_images(args).await)
    }
}

#[tool_handler]
impl ServerHandler for EbookMcp {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            server_info: Implementation {
                name: "ebook-mcp".to_string(),
                title: Some("ebook MCP".to_string()),
                version: env!("CARGO_PKG_VERSION").to_string(),
                ..Default::default()
            },
            capabilities: ServerCapabilities::builder().enable_tools().build(),
            instructions: Some(
                "Read, validate, convert, and author ebook files (EPUB, MOBI, PDF, CBZ, FB2, TXT, AZW). \
                 Use local absolute paths the host can access."
                    .to_string(),
            ),
            ..Default::default()
        }
    }
}

impl Default for EbookMcp {
    fn default() -> Self {
        Self::new()
    }
}

pub struct McpServer;

impl McpServer {
    pub fn new() -> Self {
        Self
    }

    pub async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let service = EbookMcp::new().serve(stdio()).await?;
        service.waiting().await?;
        Ok(())
    }
}

impl Default for McpServer {
    fn default() -> Self {
        Self::new()
    }
}