//! MCP ツールの一覧定義。ツール追加時はここと dispatch_tool の match の両方に追加すること。
/// tools/list で返すツール定義の配列。
pub fn tool_list() -> Vec<serde_json::Value> {
vec![
serde_json::json!({
"name": "get_item_by_id",
"description": "Get a specific document chunk by ID",
"inputSchema": {
"type": "object",
"properties": { "id": { "type": "integer" } },
"required": ["id"]
}
}),
serde_json::json!({
"name": "add_item_text",
"description": "Add or overwrite a document by path. Chunks are generated automatically.",
"inputSchema": {
"type": "object",
"properties": {
"content": { "type": "string" },
"path": { "type": "string" },
"mime": { "type": "string" },
"category": { "type": "string", "description": "Optional. Category label for the document." }
},
"required": ["content", "path"]
}
}),
serde_json::json!({
"name": "search_text",
"description": "Search document chunks using Hybrid Hybrid/Vector search (BM25 + LSA/HNSW)",
"inputSchema": {
"type": "object",
"properties": {
"content": { "type": "string" },
"limit": { "type": "integer", "default": 5 },
"min_score": { "type": "number", "default": 0.3, "description": "Minimum similarity (0-1). Results below this are dropped. Default 0.3." },
"category": { "type": "string", "description": "Optional. Filter results to a specific category." }
},
"required": ["content"]
}
}),
serde_json::json!({
"name": "update_item",
"description": "Update an existing chunk's content",
"inputSchema": {
"type": "object",
"properties": { "id": { "type": "integer" }, "content": { "type": "string" } },
"required": ["id", "content"]
}
}),
serde_json::json!({
"name": "delete_item",
"description": "Delete a chunk by ID",
"inputSchema": {
"type": "object",
"properties": { "id": { "type": "integer" } },
"required": ["id"]
}
}),
serde_json::json!({
"name": "list_documents",
"description": "List documents with paging (path, mime, chunk count, category). Returns items, total_count, total_pages, page.",
"inputSchema": {
"type": "object",
"properties": {
"limit": { "type": "integer", "default": 20, "description": "Items per page (1–100). Default 20." },
"page": { "type": "integer", "default": 1, "description": "1-based page number. Out-of-range returns empty items." }
}
}
}),
serde_json::json!({
"name": "list_categories",
"description": "List distinct category names assigned to documents (for search_text category filter)",
"inputSchema": { "type": "object", "properties": {} }
}),
serde_json::json!({
"name": "get_document_count",
"description": "Get the total count of documents stored in the database",
"inputSchema": { "type": "object", "properties": {} }
}),
serde_json::json!({
"name": "get_document",
"description": "Get full document content by document ID",
"inputSchema": {
"type": "object",
"properties": { "document_id": { "type": "integer" }, "id": { "type": "integer" } }
}
}),
serde_json::json!({
"name": "delete_document",
"description": "Delete a document and all its chunks",
"inputSchema": {
"type": "object",
"properties": { "document_id": { "type": "integer" }, "id": { "type": "integer" } }
}
}),
serde_json::json!({
"name": "lsa_retrain",
"description": "Manually trigger LSA model retraining and vector rebuild",
"inputSchema": { "type": "object", "properties": {} }
}),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tool_list_has_expected_tools() {
let list = tool_list();
let names: Vec<String> = list
.iter()
.filter_map(|v| v.get("name").and_then(|n| n.as_str()).map(String::from))
.collect();
let expected = [
"get_item_by_id",
"add_item_text",
"search_text",
"update_item",
"delete_item",
"list_documents",
"list_categories",
"get_document_count",
"get_document",
"delete_document",
"lsa_retrain",
];
assert_eq!(list.len(), expected.len(), "tool_list length");
for name in &expected {
assert!(names.contains(&name.to_string()), "tool_list contains {}", name);
}
}
}