Skip to main content
Glama

Binal Digital Twin MCP Server

by binal182
rag-search.ts•7.28 kB
import { z } from "zod" import { Index } from "@upstash/vector" // Shared Zod schema for RAG search query validation export const ragSearchSchema = z.string().min(1, "Query cannot be empty").max(500, "Query too long") // Initialize Upstash Vector client function getVectorClient() { const url = process.env.UPSTASH_VECTOR_REST_URL const token = process.env.UPSTASH_VECTOR_REST_TOKEN if (!url || !token) { throw new Error("Missing Upstash Vector configuration. Please set UPSTASH_VECTOR_REST_URL and UPSTASH_VECTOR_REST_TOKEN in your environment variables.") } return new Index({ url, token, }) } // Shared RAG search logic used by both MCP handler and server actions export async function searchBinalKnowledge(query: string) { // Validate input using the shared schema const validatedQuery = ragSearchSchema.parse(query) try { const index = getVectorClient() // Extract key terms and context from query for better search const processedQuery = preprocessQuery(validatedQuery) // Perform vector search with metadata const results = await index.query({ data: processedQuery, topK: 5, // Get top 5 most relevant results includeMetadata: true, }) console.log(`šŸ” Query: "${validatedQuery}" - Found ${results?.length || 0} results`) // Format results for consistent output if (!results || results.length === 0) { console.log('āŒ No results returned from database') return { type: 'text' as const, text: `šŸ” No relevant information found about "${validatedQuery}". Please try rephrasing your question or asking about Binal's professional background, skills, or experience.` } } // Log raw results for debugging results.forEach((result, i) => { const content = result.metadata?.content || result.data || 'No content' console.log(`Result ${i + 1}: Score=${result.score}, Content length=${content.length}, Preview="${content.substring(0, 100)}..."`) }) // Extract and format the search results const validResults = results .filter(result => { // Filter out results with no content const metadata = result.metadata || {} const content = metadata.content || metadata.text || result.data || result.content || metadata.data // Only include results that have actual content and good relevance const isValid = content && content !== "No content available" && content.length > 20 && result.score && result.score > 0.7 if (!isValid) { console.log(`āŒ Filtered out result: score=${result.score}, content length=${content?.length || 0}`) } return isValid }) .slice(0, 3) // Take only top 3 valid results console.log(`āœ… Valid results after filtering: ${validResults.length}`) const formattedResults = validResults .map((result, index) => { const metadata = result.metadata || {} // Prioritize metadata.content since that's what works in Upstash const content = metadata.content || metadata.text || result.data || result.content || metadata.data || "No content available - please check database" const source = metadata.source || metadata.category || "Unknown source" const category = metadata.category || "" return `${index + 1}. **${source}**${category ? ` (${category})` : ""} ${content} *Relevance: ${(result.score * 100).toFixed(1)}%*` }) if (formattedResults.length === 0) { return { type: 'text' as const, text: `šŸ” Found ${results.length} results for "${validatedQuery}", but none were highly relevant. Please try being more specific about what you'd like to know about Binal.` } } // Create a more conversational response based on query type const responseText = formatConversationalResponse(validatedQuery, formattedResults) return { type: 'text' as const, text: responseText } } catch (error) { console.error('RAG search error:', error) return { type: 'text' as const, text: `āŒ Error searching Binal's knowledge base: ${error instanceof Error ? error.message : 'Unknown error occurred'}. Please check your Upstash Vector configuration.` } } } // Helper function to preprocess queries for better search function preprocessQuery(query: string): string { // Handle follow-up questions and pronouns const lowerQuery = query.toLowerCase() // Replace pronouns with "Binal" for better search let processedQuery = query .replace(/\b(she|her|hers)\b/gi, 'Binal') .replace(/\b(he|his|him)\b/gi, 'Binal') .replace(/\b(they|their|them)\b/gi, 'Binal') // Handle common follow-up patterns if (lowerQuery.includes('tell me more') || lowerQuery.includes('more details')) { processedQuery = processedQuery + ' detailed information experience' } if (lowerQuery.includes('when') || lowerQuery.includes('what year')) { processedQuery = processedQuery + ' dates timeline duration' } if (lowerQuery.includes('where')) { processedQuery = processedQuery + ' location company address' } return processedQuery } // Helper function to format conversational responses function formatConversationalResponse(query: string, formattedResults: string[]): string { const lowerQuery = query.toLowerCase() // Determine response style based on query let responsePrefix = "Here's what I found:" if (lowerQuery.includes('tell me about') || lowerQuery.includes('what did')) { responsePrefix = "Based on Binal's background:" } else if (lowerQuery.includes('how') || lowerQuery.includes('what experience')) { responsePrefix = "Regarding Binal's experience:" } else if (lowerQuery.includes('when') || lowerQuery.includes('what year')) { responsePrefix = "Here are the relevant dates:" } else if (lowerQuery.includes('where')) { responsePrefix = "Location information:" } return `${responsePrefix} ${formattedResults.join('\n\n')} --- *Found ${formattedResults.length} relevant result(s). Feel free to ask follow-up questions!*` } // Tool definition that can be reused export const searchBinalTool = { name: 'search_binal_knowledge', description: 'Search through Binal\'s professional knowledge base using RAG (Retrieval-Augmented Generation). Ask questions about Binal\'s background, skills, experience, projects, or expertise.', schema: { query: ragSearchSchema, } } as const // Health check function export async function checkVectorConnection() { try { const index = getVectorClient() // Try a simple query to test connection await index.query({ data: "test", topK: 1, includeMetadata: false, }) return { connected: true, error: null } } catch (error) { return { connected: false, error: error instanceof Error ? error.message : 'Unknown connection error' } } }

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/binal182/binal-mcpserver'

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