use super::definitions::VerifyPermissionsArgs;
use crate::mcp::permissions::get_available_tools;
use crate::mcp::{is_path_allowed, McpContext};
use anyhow::Result;
use serde_json::{json, Value};
use std::path::PathBuf;
use std::sync::Arc;
pub async fn server_info(_args: Value, ctx: Arc<McpContext>) -> Result<Value> {
let cache_stats = ctx.cache.stats().await;
use chrono::{Local, Utc};
let now_local = Local::now();
let now_utc = Utc::now();
let omni_quotes = [
"Waves remember what structure forgets.",
"Compression is rhythm; meaning is melody.",
"Directories are forests; walk softly and listen.",
"Entropy is just unexplained context.",
];
let omni_quote = {
use rand::seq::SliceRandom;
let mut rng = rand::thread_rng();
omni_quotes
.choose(&mut rng)
.copied()
.unwrap_or(omni_quotes[0])
};
let info = json!({
"server": {
"name": "Smart Tree MCP Server",
"version": env!("CARGO_PKG_VERSION"),
"current_time": {
"local": now_local.format("%Y-%m-%d %H:%M:%S %Z").to_string(),
"utc": now_utc.format("%Y-%m-%d %H:%M:%S UTC").to_string(),
"date_format_hint": "Use YYYY-MM-DD format for date filters (e.g., 2025-07-11)"
},
"build": {
"name": env!("CARGO_PKG_NAME"),
"description": env!("CARGO_PKG_DESCRIPTION"),
"authors": env!("CARGO_PKG_AUTHORS"),
"repository": env!("CARGO_PKG_REPOSITORY"),
"rust_version": env!("CARGO_PKG_RUST_VERSION"),
},
"protocol": {
"version": "1.0",
"features": ["tools", "resources", "prompts", "notifications"],
},
},
"omni": {
"quote": omni_quote,
},
"capabilities": {
"output_formats": [
"classic", "hex", "json", "ai", "stats", "csv", "tsv", "digest",
"quantum", "semantic", "quantum-semantic", "summary", "summary-ai"
],
"compression": {
"supported": true,
"formats": ["zlib", "quantum", "base64"],
},
"streaming": {
"supported": true,
"formats": ["hex", "ai", "quantum", "quantum-semantic"],
},
"search": {
"content_search": true,
"pattern_matching": true,
"regex_support": true,
},
},
"configuration": {
"cache": {
"enabled": ctx.config.cache_enabled,
"ttl_seconds": ctx.config.cache_ttl,
"max_size_bytes": ctx.config.max_cache_size,
"current_entries": cache_stats.entries,
"current_size_bytes": cache_stats.size,
"hit_rate": format!("{:.1}%", cache_stats.hit_rate * 100.0),
},
"security": {
"allowed_paths": ctx.config.allowed_paths.len(),
"blocked_paths": ctx.config.blocked_paths.len(),
"default_blocks": ["/etc", "/sys", "/proc"],
},
"mcp_optimization": {
"compression_enabled": !std::env::var("MCP_NO_COMPRESS")
.is_ok_and(|v| v == "1" || v.to_lowercase() == "true"),
"emoji_disabled": true,
"auto_ai_modes": true,
"env_vars": {
"MCP_NO_COMPRESS": "Set to '1' or 'true' to disable compression for AIs that can't handle it",
},
},
},
"features": {
"quantum_compression": {
"description": "Ultra-compressed binary format with 90%+ compression",
"status": "active",
"notes": "Base64-encoded for JSON transport in MCP",
},
"mcp_optimization": {
"description": "Automatic API optimization for any output mode",
"status": "active",
"features": ["compression (disable with MCP_NO_COMPRESS=1)", "no emoji", "AI mode selection"],
"recommended_for": ["MCP servers", "LLM APIs", "AI assistants"],
},
"tokenization": {
"description": "Semantic tokenization for common patterns",
"tokens": {
"directories": ["node_modules=0x80", ".git=0x81", "src=0x82"],
"extensions": [".js=0x90", ".rs=0x91", ".py=0x92"],
},
},
},
"recommended_workflow": {
"step_1": {
"tool": "quick_tree",
"why": "Always start here! Gets you a 3-level overview with 10x compression. Perfect for understanding the basic structure.",
"example": "quick_tree(path='.')",
},
"step_2": {
"tool": "project_overview or analyze_workspace",
"why": "For deeper understanding of project type, dependencies, and structure. Use project_overview for single projects, analyze_workspace for complex/multi-language codebases.",
"example": "project_overview(path='.')",
},
"step_3_options": {
"for_specific_files": {
"tools": ["find_code_files", "find_config_files", "find_documentation", "find_tests"],
"why": "Use these targeted searches to locate specific file types quickly",
},
"for_code_analysis": {
"tool": "analyze_directory with mode='quantum-semantic'",
"why": "Best mode for understanding code structure with semantic compression and tokenization",
},
"for_search": {
"tool": "search_in_files",
"why": "Find specific functions, TODOs, or any text pattern across the codebase",
},
"for_statistics": {
"tool": "get_statistics",
"why": "Understand file distribution, sizes, and project composition",
},
},
"pro_tips": [
"Always use quick_tree first - it's optimized for initial exploration",
"For large codebases, use mode='summary-ai' for 10x compression",
"quantum-semantic mode is AMAZING for code understanding - try it!",
"Cache is enabled by default - repeated calls are instant",
"Use search_in_files to find specific implementations quickly",
"semantic_analysis groups files by purpose - great for large projects",
],
},
"statistics": {
"uptime_seconds": 0,
"requests_handled": 0,
"cache_hits": cache_stats.hits,
"cache_misses": cache_stats.misses,
},
"tips": [
"🌟 ALWAYS start with 'quick_tree' for any new directory!",
"Use 'summary-ai' format for optimal LLM API transmission (10x compression!)",
"Enable compression with compress=true for large directories",
"Use 'quantum-semantic' mode for the best code analysis experience",
"Stream mode available for very large directories",
"Content search supported with 'search_in_files' tool",
"The cache makes repeated queries instant - don't worry about calling tools multiple times!",
],
});
let json_string = serde_json::to_string_pretty(&info)?;
Ok(json!({
"content": [{
"type": "text",
"text": json_string
}]
}))
}
pub async fn verify_permissions(args: Value, ctx: Arc<McpContext>) -> Result<Value> {
let args: VerifyPermissionsArgs = serde_json::from_value(args)?;
let path = PathBuf::from(&args.path);
if !is_path_allowed(&path, &ctx.config) {
return Ok(json!({
"content": [{
"type": "text",
"text": format!("🚫 Access Denied: Path '{}' is not in allowed paths list.\n\nAllowed paths:\n{}",
path.display(),
ctx.config.allowed_paths.iter()
.map(|p| format!(" - {}", p.display()))
.collect::<Vec<_>>()
.join("\n")
)
}]
}));
}
let mut perm_cache = ctx.permissions.lock().await;
let perms = perm_cache.verify(&path)?;
let tools = get_available_tools(&perms);
let mut output = format!("🔐 Permission Check for: {}\n\n", path.display());
output.push_str("📊 Permission Status:\n");
output.push_str(&format!(
" • Exists: {}\n",
if perms.exists { "✅" } else { "❌" }
));
output.push_str(&format!(
" • Readable: {}\n",
if perms.readable { "✅" } else { "❌" }
));
output.push_str(&format!(
" • Writable: {}\n",
if perms.writable { "✅" } else { "❌" }
));
output.push_str(&format!(
" • Type: {}\n",
if perms.is_directory {
"📁 Directory"
} else if perms.is_file {
"📄 File"
} else {
"❓ Unknown"
}
));
if let Some(error) = &perms.error {
output.push_str(&format!(" • Error: {}\n", error));
}
output.push_str("\n🛠️ Available Tools:\n");
let mut available = vec![];
let mut unavailable = vec![];
for tool in &tools {
if tool.available {
available.push(tool);
} else {
unavailable.push(tool);
}
}
if !available.is_empty() {
output.push_str("\n✅ Ready to Use:\n");
for tool in available {
output.push_str(&format!(" • {} - Call with this path\n", tool.name));
}
}
if !unavailable.is_empty() {
output.push_str("\n❌ Not Available (with reasons):\n");
for tool in unavailable {
output.push_str(&format!(
" • {} - {}\n",
tool.name,
tool.reason
.as_ref()
.unwrap_or(&"Unknown reason".to_string())
));
}
}
output.push_str("\n💡 Tips:\n");
if !perms.exists {
output
.push_str(" • The path doesn't exist. Check your spelling or use a different path.\n");
} else if !perms.readable {
output.push_str(" • No read permission. You may need to run with elevated privileges.\n");
} else if !perms.writable && perms.is_file {
output.push_str(" • File is read-only. You can analyze but not edit.\n");
}
output.push('\n');
output.push_str("Trisha from Accounting says: \"It's like checking if you have the keys ");
output.push_str("before bringing the whole toolbox! Smart thinking!\" 🔑\n");
Ok(json!({
"content": [{
"type": "text",
"text": output
}],
"metadata": {
"permissions": perms,
"available_tools": tools.iter().filter(|t| t.available).map(|t| &t.name).collect::<Vec<_>>(),
"unavailable_tools": tools.iter().filter(|t| !t.available).map(|t| &t.name).collect::<Vec<_>>(),
}
}))
}