hanzo-mcp 1.1.23

Hanzo MCP server — a hanzo-mcp binary serving 15 hand-written tools (fs, exec, code, git, fetch, workspace, computer, browser, think, memory, plan, tasks, mode, hanzo, search) over JSON-RPC
Documentation
/// Vector store implementation (stub - LanceDB temporarily disabled)
///
/// This module provides the interface for vector embeddings and similarity search.
/// The LanceDB backend is temporarily disabled due to arrow version conflicts.
/// When re-enabled, this will support:
/// - Document storage with embeddings
/// - Symbol indexing for code search
/// - Memory/knowledge base storage
/// - Semantic similarity search

use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::collections::HashMap;

/// Document structure for vector store
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
    pub id: String,
    pub content: String,
    pub metadata: serde_json::Value,
    pub embedding: Vec<f32>,
    pub score: f32,
}

/// Symbol structure for code symbols
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Symbol {
    pub name: String,
    pub symbol_type: String,
    pub file_path: String,
    pub line_number: usize,
    pub signature: Option<String>,
    pub docstring: Option<String>,
    pub embedding: Vec<f32>,
}

/// Memory structure for conversation and knowledge
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Memory {
    pub id: String,
    pub memory_type: String,
    pub content: String,
    pub timestamp: i64,
    pub metadata: serde_json::Value,
    pub embedding: Vec<f32>,
}

/// Vector store configuration
#[derive(Debug, Clone)]
pub struct VectorStoreConfig {
    pub data_dir: PathBuf,
    pub embedding_model: String,
    pub dimensions: usize,
    pub index_name: String,
}

impl Default for VectorStoreConfig {
    fn default() -> Self {
        Self {
            data_dir: dirs::home_dir()
                .unwrap_or_else(|| PathBuf::from("."))
                .join(".hanzo")
                .join("lancedb"),
            embedding_model: "all-MiniLM-L6-v2".to_string(),
            dimensions: 384,
            index_name: "default".to_string(),
        }
    }
}

/// Vector store (stub implementation - LanceDB disabled)
pub struct VectorStore {
    config: VectorStoreConfig,
    // In-memory store for basic functionality
    documents: HashMap<String, Document>,
    symbols: HashMap<String, Symbol>,
    memories: HashMap<String, Memory>,
}

impl VectorStore {
    /// Create new vector store
    pub async fn new(config: Option<VectorStoreConfig>) -> Result<Self, Box<dyn std::error::Error>> {
        let config = config.unwrap_or_default();

        Ok(Self {
            config,
            documents: HashMap::new(),
            symbols: HashMap::new(),
            memories: HashMap::new(),
        })
    }

    /// Initialize tables (no-op in stub)
    pub async fn initialize(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        // LanceDB initialization disabled
        Ok(())
    }

    /// Add document to vector store
    pub async fn add_document(&mut self, doc: Document) -> Result<(), Box<dyn std::error::Error>> {
        self.documents.insert(doc.id.clone(), doc);
        Ok(())
    }

    /// Add symbol to vector store
    pub async fn add_symbol(&mut self, symbol: Symbol) -> Result<(), Box<dyn std::error::Error>> {
        self.symbols.insert(symbol.name.clone(), symbol);
        Ok(())
    }

    /// Add memory to vector store
    pub async fn add_memory(&mut self, memory: Memory) -> Result<(), Box<dyn std::error::Error>> {
        self.memories.insert(memory.id.clone(), memory);
        Ok(())
    }

    /// Search for similar documents (stub - returns empty)
    pub async fn search(
        &self,
        _query: &str,
        _table: &str,
        _limit: usize,
        _threshold: f32,
    ) -> Result<Vec<Document>, Box<dyn std::error::Error>> {
        // Vector search disabled - would use embeddings
        Ok(vec![])
    }

    /// Search for similar documents
    pub async fn search_documents(
        &self,
        _query: &str,
        _limit: usize,
        _threshold: f32,
    ) -> Result<Vec<Document>, Box<dyn std::error::Error>> {
        // Vector search disabled - would use embeddings
        Ok(vec![])
    }

    /// Search for similar symbols
    pub async fn search_symbols(
        &self,
        _query: &str,
        _symbol_type: Option<&str>,
        _limit: usize,
    ) -> Result<Vec<Symbol>, Box<dyn std::error::Error>> {
        // Vector search disabled - would use embeddings
        Ok(vec![])
    }

    /// Search for similar memories
    pub async fn search_memories(
        &self,
        _query: &str,
        _memory_type: Option<&str>,
        _limit: usize,
    ) -> Result<Vec<Memory>, Box<dyn std::error::Error>> {
        // Vector search disabled - would use embeddings
        Ok(vec![])
    }

    /// Generate embedding for text (stub - returns zero vector)
    pub async fn generate_embedding(&self, _text: &str) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
        // This would use sentence-transformers or OpenAI API
        Ok(vec![0.0; self.config.dimensions])
    }

    /// Index codebase (stub - no-op)
    pub async fn index_codebase(&mut self, _directory: &Path) -> Result<(), Box<dyn std::error::Error>> {
        // Codebase indexing disabled without vector store
        Ok(())
    }

    /// Calculate cosine similarity
    pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
        if a.len() != b.len() {
            return 0.0;
        }

        let dot_product: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
        let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
        let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();

        if norm_a == 0.0 || norm_b == 0.0 {
            return 0.0;
        }

        dot_product / (norm_a * norm_b)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_vector_store() {
        let store = VectorStore::new(None).await;
        assert!(store.is_ok());
    }

    #[test]
    fn test_cosine_similarity() {
        let a = vec![1.0, 0.0, 0.0];
        let b = vec![1.0, 0.0, 0.0];
        assert_eq!(VectorStore::cosine_similarity(&a, &b), 1.0);

        let c = vec![0.0, 1.0, 0.0];
        assert_eq!(VectorStore::cosine_similarity(&a, &c), 0.0);
    }
}