RAG Documentation MCP Server

  • src
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; import { QdrantClient } from "@qdrant/js-client-rest"; import { chromium } from "playwright"; import { EmbeddingService } from "./services/embeddings.js"; // Environment variables for configuration const EMBEDDING_PROVIDER = process.env.EMBEDDING_PROVIDER || 'ollama'; const EMBEDDING_MODEL = process.env.EMBEDDING_MODEL; const OPENAI_API_KEY = process.env.OPENAI_API_KEY; const FALLBACK_PROVIDER = process.env.FALLBACK_PROVIDER; const FALLBACK_MODEL = process.env.FALLBACK_MODEL; const QDRANT_URL = process.env.QDRANT_URL || 'http://localhost:6333'; const QDRANT_API_KEY = process.env.QDRANT_API_KEY; if (!QDRANT_URL) { throw new Error( "QDRANT_URL environment variable is required for cloud storage" ); } if ((EMBEDDING_PROVIDER === 'openai' || FALLBACK_PROVIDER === 'openai') && !OPENAI_API_KEY) { throw new Error( "OPENAI_API_KEY environment variable is required when using OpenAI as either primary or fallback provider" ); } if (EMBEDDING_PROVIDER === 'ollama') { console.warn('Using Ollama as primary provider. Make sure Ollama is running locally.'); } export class ApiClient { qdrantClient: QdrantClient; embeddingService: EmbeddingService; browser: any; vectorSize: number; constructor() { // Initialize Qdrant client with cloud configuration this.qdrantClient = new QdrantClient({ url: QDRANT_URL, apiKey: QDRANT_API_KEY, }); // Initialize embedding service with configured provider this.embeddingService = EmbeddingService.createFromConfig({ provider: EMBEDDING_PROVIDER as 'ollama' | 'openai', apiKey: EMBEDDING_PROVIDER === 'openai' ? OPENAI_API_KEY : undefined, model: EMBEDDING_MODEL, fallbackProvider: FALLBACK_PROVIDER as 'ollama' | 'openai' | undefined, fallbackApiKey: FALLBACK_PROVIDER === 'openai' ? OPENAI_API_KEY : undefined, fallbackModel: FALLBACK_MODEL }); this.vectorSize = this.embeddingService.getVectorSize(); } async initBrowser() { if (!this.browser) { this.browser = await chromium.launch(); } } async cleanup() { if (this.browser) { await this.browser.close(); } } async getEmbeddings(text: string): Promise<number[]> { try { return await this.embeddingService.generateEmbeddings(text); } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Failed to generate embeddings: ${error}` ); } } async initCollection(COLLECTION_NAME: string) { try { const collections = await this.qdrantClient.getCollections(); const exists = collections.collections.some( (c) => c.name === COLLECTION_NAME ); if (!exists) { await this.qdrantClient.createCollection(COLLECTION_NAME, { vectors: { size: this.vectorSize, // Dynamic size based on provider distance: "Cosine", }, optimizers_config: { default_segment_number: 2, memmap_threshold: 20000, }, replication_factor: 2, }); } } catch (error) { if (error instanceof Error) { if (error.message.includes("unauthorized")) { throw new McpError( ErrorCode.InvalidRequest, "Failed to authenticate with Qdrant cloud. Please check your API key." ); } else if ( error.message.includes("ECONNREFUSED") || error.message.includes("ETIMEDOUT") ) { throw new McpError( ErrorCode.InternalError, "Failed to connect to Qdrant cloud. Please check your QDRANT_URL." ); } } throw new McpError( ErrorCode.InternalError, `Failed to initialize Qdrant cloud collection: ${error}` ); } } }