Skip to main content
Glama
VectorStoreImpl.ts5 kB
import { CreateIndexConfig, LocalIndex } from 'vectra'; import { ServerInfo, ToolWithMetadata, SearchResult } from '../../domain/types.js'; import { IVectorStore } from '../../interfaces/IVectorStore.js'; import { EmbeddingsManager } from './EmbeddingsManager.js'; import * as fs from 'fs/promises'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; // Get __dirname equivalent in ES modules const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * VectorStoreImpl provides vector-based semantic search for tools * Implements dependency injection pattern for better testability */ export class VectorStoreImpl implements IVectorStore { private index: LocalIndex; private embeddingsManager: EmbeddingsManager; private toolsMap: Map<string, ToolWithMetadata> = new Map(); private initialized: boolean = false; private indexPath: string; constructor(embeddingsManager: EmbeddingsManager, indexPath: string = '.vector-index') { // Resolve path relative to the build directory (one level up to project root) // This ensures the index is created in the project directory regardless of cwd this.indexPath = path.resolve(__dirname, '..', '..', '..', indexPath); this.index = new LocalIndex(this.indexPath); this.embeddingsManager = embeddingsManager; } /** * Initialize the vector store */ async initialize(): Promise<void> { if (this.initialized) return; // Initialize embeddings manager await this.embeddingsManager.initialize(); const createIndexConfig: CreateIndexConfig = {version: 1, deleteIfExists: true} // Create indexing await this.index.createIndex(createIndexConfig); console.error('✓ Created new vector index'); this.initialized = true; } /** * Index all tools from registered servers */ async indexTools(servers: Map<string, ServerInfo>): Promise<void> { if (!this.initialized) { throw new Error('VectorStore not initialized. Call initialize() first.'); } console.error('Indexing tools...'); let indexed = 0; for (const [serverName, serverInfo] of servers) { for (const tool of serverInfo.tools) { // Use description or name as fallback const description = tool.description || tool.name; // Generate embedding const embedding = await this.embeddingsManager.embed(description); // Create unique ID const id = `${serverName}/${tool.name}`; // Store metadata this.toolsMap.set(id, { serverName, toolName: tool.title || tool.name, description, tool, }); // Add to vector index await this.index.upsertItem({ id, vector: embedding, metadata: { serverName, toolName: tool.name, description, }, }); indexed++; } } console.error(`✓ Indexed ${indexed} tools`); } /** * Search for tools using semantic similarity */ async search(query: string, topK: number = 5): Promise<SearchResult[]> { if (!this.initialized) { throw new Error('VectorStore not initialized. Call initialize() first.'); } // Generate query embedding const queryEmbedding = await this.embeddingsManager.embed(query); // Search vector index const results = await this.index.queryItems(queryEmbedding, topK); // Map results to SearchResult format return results.map((result) => { const toolMeta = this.toolsMap.get(result.item.id); if (!toolMeta) { throw new Error(`Tool metadata not found for ${result.item.id}`); } return { serverName: toolMeta.serverName, toolName: toolMeta.toolName, description: toolMeta.description, score: result.score, tool: toolMeta.tool, }; }); } /** * Re-index a specific server's tools */ async reindexServer(serverName: string, serverInfo: ServerInfo): Promise<void> { // Remove old entries for this server for (const [id, meta] of this.toolsMap) { if (meta.serverName === serverName) { await this.index.deleteItem(id); this.toolsMap.delete(id); } } // Index new tools for (const tool of serverInfo.tools) { const description = tool.description || tool.name; const embedding = await this.embeddingsManager.embed(description); const id = `${serverName}/${tool.name}`; this.toolsMap.set(id, { serverName, toolName: tool.name, description, tool, }); await this.index.upsertItem({ id, vector: embedding, metadata: { serverName, toolName: tool.name, description }, }); } } /** * Get statistics about the vector store */ getStats(): { totalTools: number; } { return { totalTools: this.toolsMap.size, }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eliavamar/mcp-of-mcps'

If you have feedback or need assistance with the MCP directory API, please join our Discord server