search-vectors
Search files and code snippets using natural language queries, filter by path, similarity threshold, and result limit, and generate embeddings with customizable AI providers.
Instructions
Search for files and code snippets using natural language queries
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filesOnly | No | Return only file paths without chunks | |
| limit | No | Maximum number of results | |
| path | No | Project path to search (defaults to current directory) | |
| provider | No | Embedding provider to use (defaults to configured provider) | |
| query | Yes | Natural language search query | |
| similarityThreshold | No | Minimum similarity score (0-1) |
Implementation Reference
- src/server.ts:467-489 (registration)Registers the 'search-vectors' MCP tool with title, description, input schema, and execution handler that calls handleSearchVectorsserver.registerTool("search-vectors", { title: "Search Vectors", description: "Search for files and code snippets using natural language queries", inputSchema: SearchVectorsSchema.shape, }, async (args) => { const { handleSearchVectors } = await import("./handlers/vector"); const result = await handleSearchVectors({ query: args.query, path: args.path || process.cwd(), provider: args.provider, limit: args.limit || 10, similarityThreshold: args.similarityThreshold || 0.7, filesOnly: args.filesOnly || false, }); return { content: [ { type: "text", text: result } ] }; });
- src/server.ts:134-141 (schema)Zod schema defining input parameters for the search-vectors tool: query, path, provider, limit, similarityThreshold, filesOnlyconst SearchVectorsSchema = z.object({ query: z.string().describe("Natural language search query"), path: z.string().optional().describe("Project path to search (defaults to current directory)"), provider: z.enum(["openai", "azure", "gemini"]).optional().describe("Embedding provider to use (defaults to configured provider)"), limit: z.number().min(1).max(50).optional().describe("Maximum number of results"), similarityThreshold: z.number().min(0).max(1).optional().describe("Minimum similarity score (0-1)"), filesOnly: z.boolean().optional().describe("Return only file paths without chunks"), });
- src/vector/search.ts:20-107 (handler)Core handler function performing semantic search on code vectors using embeddings. Supports native VSS or fallback cosine similarity computation.export async function searchVectors(options: SearchOptions): Promise<SearchResult[]> { const { projectPath, query, provider, limit = 10, similarityThreshold = 0.7 } = options; // Get query embedding const queryEmbedding = await provider.getEmbedding(query); // Get database const { db, client } = await getVectorDB(projectPath); try { // Try to use native vector search if available let results: Array<{ id: string; relpath: string; chunk: string; embedding: Buffer; distance: number; }>; try { // Try VSS virtual table first const vectorResult = await client.execute({ sql: `SELECT vc.id, vc.relpath, vc.chunk, vs.distance FROM vss_vectors vs JOIN vector_chunks vc ON vc.rowid = vs.rowid WHERE vss_search(vs.embedding, ?) ORDER BY vs.distance LIMIT ?`, args: [new Float32Array(queryEmbedding).buffer, limit] }); results = vectorResult.rows.map(row => ({ id: row[0] as string, relpath: row[1] as string, chunk: row[2] as string, embedding: Buffer.alloc(0), // Not needed for VSS results distance: row[3] as number, })); } catch (error) { // Fallback to manual cosine similarity if vector extension not available logger.warn('Native vector search failed, using fallback:', error); // Try to get embeddings from main table first (fallback storage) let fallbackResult = await client.execute({ sql: 'SELECT id, relpath, chunk, embedding FROM vector_chunks WHERE embedding IS NOT NULL', args: [] }); const allChunks = fallbackResult.rows.map(row => ({ id: row[0] as string, relpath: row[1] as string, chunk: row[2] as string, embedding: row[3] as Buffer, })); // Calculate cosine similarity for each chunk const withSimilarity = allChunks.map(chunk => { // Fix: libsql returns Uint8Array, need to access .buffer for Float32Array const embedBuffer = chunk.embedding instanceof Buffer ? chunk.embedding : Buffer.from((chunk.embedding as Uint8Array).buffer); const chunkEmbedding = bufferToFloat32Array(embedBuffer); const similarity = cosineSimilarity(queryEmbedding, Array.from(chunkEmbedding)); return { ...chunk, distance: 1 - similarity, // Convert similarity to distance }; }); // Sort by similarity and limit results = withSimilarity .sort((a, b) => a.distance - b.distance) .slice(0, limit); } // Convert distance to similarity and filter by threshold return results .map(result => ({ relpath: result.relpath, chunk: result.chunk, similarity: 1 - result.distance, chunkId: result.id, })) .filter(result => result.similarity >= similarityThreshold); } catch (error) { throw new Error(`Vector search failed: ${error instanceof Error ? error.message : String(error)}`); } }
- src/vector/search.ts:109-119 (helper)Helper function to get unique file paths from search results (used when filesOnly=true)export async function getRelatedFiles(options: SearchOptions): Promise<string[]> { const results = await searchVectors(options); // Get unique file paths const filePaths = new Set<string>(); for (const result of results) { filePaths.add(result.relpath); } return Array.from(filePaths); }