use crate::ir::loader;
use crate::mcp::json::scrap::ScrapRefJson;
use crate::usecase::frontmatter::usecase::FrontmatterUsecase;
use rmcp::model::ErrorCode;
use rmcp::model::{CallToolResult, ContentBlock};
use rmcp::service::RequestContext;
use rmcp::{ErrorData, RoleServer};
use serde::Serialize;
use serde_json::Value;
use std::path::Path;
#[derive(Debug, Serialize)]
pub struct FrontmatterItemJson {
pub scrap: ScrapRefJson,
pub frontmatter: Value,
}
#[derive(Debug, Serialize)]
pub struct ListFrontmatterResponse {
pub results: Vec<FrontmatterItemJson>,
pub count: usize,
pub next: String,
}
pub async fn list_frontmatter(
scraps_dir: &Path,
exclude_dirs: &[std::path::PathBuf],
_context: RequestContext<RoleServer>,
) -> Result<CallToolResult, ErrorData> {
let scraps = loader::load_scraps(scraps_dir, exclude_dirs).map_err(|e| {
ErrorData::new(
ErrorCode(-32003),
format!("Failed to load scraps: {e}"),
None,
)
})?;
let frontmatter_usecase = FrontmatterUsecase::new();
let results = frontmatter_usecase.execute(&scraps).map_err(|e| {
ErrorData::new(
ErrorCode(-32004),
format!("List frontmatter failed: {e}"),
None,
)
})?;
let items: Vec<FrontmatterItemJson> = results
.into_iter()
.map(|result| FrontmatterItemJson {
scrap: (&result.scrap).into(),
frontmatter: result.frontmatter,
})
.collect();
let count = items.len();
let next = if count == 0 {
"No scrap carries frontmatter. The wiki's tags are all #[[tag]] in scrap bodies — start from list_tags."
} else {
"Keys are the author's own; scraps gives them no meaning, except that the strings of a `tags` list are tags (list_tags) and a `type` string is the scrap's kind (list_types). Read the scrap behind an entry with get_scrap {key}."
};
let response = ListFrontmatterResponse {
results: items,
count,
next: next.to_string(),
};
Ok(CallToolResult::success(vec![ContentBlock::text(
serde_json::to_string(&response).map_err(|e| {
ErrorData::new(
ErrorCode(-32005),
format!("JSON serialization failed: {e}"),
None,
)
})?,
)]))
}