Skip to main content

rab/extensions/mcp/
cache.rs

1//! Persistent metadata cache — caches MCP tool definitions to disk for fast startup.
2//! Mirrors pi-mcp-adapter's mcp-cache.json pattern.
3
4use crate::extensions::mcp::types::{CachedTool, MetadataCache, ServerCacheEntry};
5use std::collections::HashMap;
6use std::path::PathBuf;
7
8const CACHE_VERSION: u32 = 1;
9
10/// Get the cache file path.
11fn cache_path() -> PathBuf {
12    directories::BaseDirs::new()
13        .map(|d| {
14            d.home_dir()
15                .join(".rab")
16                .join("agent")
17                .join("mcp-cache.json")
18        })
19        .unwrap_or_else(|| PathBuf::from("/tmp/.rab/agent/mcp-cache.json"))
20}
21
22/// Load the metadata cache from disk.
23pub fn load_cache() -> MetadataCache {
24    let path = cache_path();
25    if !path.exists() {
26        return MetadataCache {
27            version: CACHE_VERSION,
28            servers: HashMap::new(),
29        };
30    }
31    let content = match std::fs::read_to_string(&path) {
32        Ok(c) => c,
33        Err(_) => {
34            return MetadataCache {
35                version: CACHE_VERSION,
36                servers: HashMap::new(),
37            };
38        }
39    };
40    let cache: MetadataCache = match serde_json::from_str(&content) {
41        Ok(c) => c,
42        Err(_) => MetadataCache {
43            version: CACHE_VERSION,
44            servers: HashMap::new(),
45        },
46    };
47    // Ignore old version caches
48    if cache.version != CACHE_VERSION {
49        return MetadataCache {
50            version: CACHE_VERSION,
51            servers: HashMap::new(),
52        };
53    }
54    cache
55}
56
57/// Save the metadata cache to disk.
58pub fn save_cache(cache: &MetadataCache) {
59    let path = cache_path();
60    if let Some(parent) = path.parent() {
61        let _ = std::fs::create_dir_all(parent);
62    }
63    if let Ok(content) = serde_json::to_string(cache) {
64        let _ = std::fs::write(&path, &content);
65    }
66}
67
68/// Update cache entry for a specific server after successful connection.
69pub fn update_cache_entry(
70    server_name: &str,
71    config_hash: u64,
72    yo_tools: &[yoagent::mcp::types::McpToolInfo],
73) {
74    let mut cache = load_cache();
75    let tools: Vec<CachedTool> = yo_tools
76        .iter()
77        .map(|t| CachedTool {
78            name: t.name.clone(),
79            description: t.description.clone(),
80            input_schema: if t.input_schema.is_null() {
81                serde_json::json!({"type": "object", "properties": {}})
82            } else {
83                t.input_schema.clone()
84            },
85        })
86        .collect();
87
88    cache.servers.insert(
89        server_name.to_string(),
90        ServerCacheEntry {
91            config_hash,
92            tools,
93            cached_at: std::time::SystemTime::now()
94                .duration_since(std::time::UNIX_EPOCH)
95                .map(|d| d.as_millis() as u64)
96                .unwrap_or(0),
97        },
98    );
99    save_cache(&cache);
100}
101
102/// Check if a valid cache entry exists for a server.
103pub fn has_valid_cache(server_name: &str, config_hash: u64) -> bool {
104    let cache = load_cache();
105    cache
106        .servers
107        .get(server_name)
108        .is_some_and(|e| e.config_hash == config_hash)
109}