About mcpRAG
mcpRAG is a document question-answering pipeline built around local Ollama embeddings, FAISS retrieval, and Gemini generation. It is intended for users who want to index text files, query their contents, and add documents incrementally.
Key features
- 01Create embeddings with Ollama
- 02Index vectors with FAISS
- 03Answer questions using Gemini
- 04Add documents without rebuilding the index
- 05Store chunk metadata locally
Use cases
- 01Query a collection of TXT documents
- 02Build a RAG system for SAP documentation
- 03Extend an existing index with new files
MCP support
The project describes an MCP server for agentic use, but the provided README does not specify its MCP tools or interface.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
We haven’t collected a package or remote endpoint for this server. Follow the instructions in the project README.
Tools
Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.
Details
From the project READMEmcpRAG - Retrieval-Augmented Generation System
A production-ready RAG (Retrieval-Augmented Generation) implementation combining open-source embeddings, FAISS vector database, and Gemini LLM for accurate document-based question answering.
Overview
This project implements a complete RAG pipeline for querying text documents using:
- Embeddings: Nomic embeddings via Ollama (local, open-source)
- Vector Database: FAISS (Facebook AI Similarity Search)
- LLM: Gemini 2.0 Flash (fast, cost-effective)
- Document Processing: Chunking with overlap for context preservation
- Incremental Updates: Add new documents without rebuilding entire index
Architecture
Documents (TXT) → Chunking (60 words, 10-word overlap)
↓
Nomic Embeddings (Ollama, 768 dimensions)
↓
FAISS Vector Index (L2 distance similarity)
↓
User Query → Query Embedding → FAISS Search → Top-K Chunks
↓
Context + Query → Gemini LLM
↓
Answer
Key Features
- Local Embeddings: Uses Ollama with Nomic model - no external API calls for embeddings
- Efficient Search: FAISS IndexFlatL2 for fast similarity search
- Chunking Strategy: 60-word chunks with 10-word overlap to preserve context
- Incremental Updates:
appendEmbeds.pyadds new documents without rebuilding - Metadata Tracking: Stores document name, chunk ID, and chunk text for traceability
- Persistent Storage: Index and metadata saved locally for reuse
Installation
Prerequisites
- Python 3.11+
- Ollama installed and running locally
- Gemini API key
- Nomic embedding model downloaded in Ollama
Setup
- Install Ollama (if not already installed):
# macOS/Linux
curl -fsSL https://ollama.ai/install.sh | sh
# Windows
# Download from https://ollama.ai/download
- Pull the Nomic embedding model:
ollama pull nomic-embed-text
Verify Ollama is running:
curl http://localhost:11434/api/embeddings -d '{"model":"nomic-embed-text","prompt":"test"}'
- Clone and navigate to the project:
cd mcpRAG
- Create virtual environment and install dependencies:
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
pip install -r requirements.txt
- Configure API credentials:
Create a .env file:
GEMINI_API_KEY=your_gemini_api_key_here
Get your API key from Google AI Studio
- Prepare your documents:
Place your .txt files in the docfiles/ or sapfiles/ directory.
Usage
1. Create Initial Embeddings and Index
Run ollamaEmbed.py to process your first document and create the FAISS index:
# Edit ollamaEmbed.py to set your document path
file_path = Path("C:\\path\\to\\your\\document.txt")
# Then run
python ollamaEmbed.py
What it does:
- Reads the specified text file
- Chunks text into 60-word segments with 10-word overlap
- Generates embeddings using Ollama's Nomic model
- Creates FAISS index with L2 distance metric
- Saves index to
my_faiss_index.faiss - Saves metadata to
my_metadata.json
Output:
Processing document.txt...
✅ Indexed 142 chunks
✅ Saved FAISS index to my_faiss_index.faiss
✅ Saved metadata to my_metadata.json
2. Query the RAG System
Run ragModel.py to search the index and generate answers:
# Edit ragModel.py to set your query
query = "how does SAP integrate with other systems?"
top_k = 5 # Number of relevant chunks to retrieve
# Then run
python ragModel.py
What it does:
- Loads the FAISS index and metadata
- Converts your query to embeddings using Nomic
- Searches FAISS for the top-K most similar chunks
- Passes retrieved context + query to Gemini 2.0 Flash
- Returns a contextual answer
Example Output:
Query: how does SAP integrate with other systems?
Result 1:
Document: SAP_Joule.txt
Chunk: SAP integrates with various external systems through APIs...
Chunk ID: SAP_Joule_42
--------------------
Gemini Response: SAP integrates with other systems primarily through REST APIs
and OData services. The integration layer supports real-time data synchronization...
3. Add New Documents to Existing Index
Run appendEmbeds.py to incrementally add documents without rebuilding:
# Edit appendEmbeds.py to set your new document path
new_file_path = "C:\\path\\to\\new_document.txt"
# Then run
python appendEmbeds.py
What it does:
- Loads existing FAISS index and metadata
- Processes the new document (chunking + embeddings)
- Appends new embeddings to existing FAISS index
- Updates metadata with new chunks
- Saves updated index and metadata
Output:
Processing SAP_GENAI.txt...
✅ Added 87 new chunks to the index
✅ Saved updated FAISS index to my_faiss_index.faiss
✅ Saved updated metadata to my_metadata.json
Project Files
| File | Purpose |
|---|---|
ollamaEmbed.py | Initial document processing, embedding generation, FAISS index creation |
ragModel.py | Query interface - search index and generate answers with Gemini |
appendEmbeds.py | Incrementally add new documents to existing index |
requirements.txt | Python dependencies |
.env | API credentials (create this file) |
my_faiss_index.faiss | Saved FAISS vector index (auto-generated) |
my_metadata.json | Chunk metadata with document names and text (auto-generated) |
docfiles/ | Directory for source documents |
sapfiles/ | Directory for SAP-related documents |
Configuration
Chunking Parameters
Adjust in any of the Python files:
CHUNK_SIZE = 60 # Number of words per chunk
CHUNK_OVERLAP = 10 # Overlapping words between chunks
Recommendations:
- Smaller chunks (30-50 words): Better precision, more chunks, slower indexing
- Larger chunks (80-100 words): More context, fewer chunks, faster indexing
- Overlap (10-20 words): Prevents context loss at chunk boundaries
Retrieval Settings
In ragModel.py, adjust top_k:
top_k = 5 # Number of chunks to retrieve
Recommendations:
- top_k=3: Fast, focused answers
- top_k=5: Balanced (default)
- top_k=10: More comprehensive context, slower
LLM Model
Change Gemini model in ragModel.py:
model="gemini-2.0-flash" # Fast, cost-effective (default)
model="gemini-1.5-pro" # More capable, higher cost
model="gemini-1.5-flash" # Balanced
Embedding Model
Change Ollama embedding model (requires model pull):
"model": "nomic-embed-text" # 768 dimensions (default)
"model": "mxbai-embed-large" # 1024 dimensions
If changing models, delete existing .faiss and .json files and rebuild.
How It Works
1. Document Chunking
Text is split into overlapping chunks:
Original: "The quick brown fox jumps over the lazy dog..."
Chunk 1: "The quick brown fox jumps..." (words 0-60)
Chunk 2: "...fox jumps over the lazy..." (words 50-110, 10-word overlap)
Chunk 3: "...lazy dog runs through..." (words 100-160, 10-word overlap)
2. Embedding Generation
Each chunk is converted to a 768-dimensional vector via Ollama:
response = requests.post(
"http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": chunk}
)
embedding = np.array(response.json()["embedding"]) # Shape: (768,)
3. FAISS Indexing
Embeddings are stored in a FAISS IndexFlatL2 (L2 distance):
dimension = 768
index = faiss.IndexFlatL2(dimension)
index.add(np.stack(all_chunks)) # Add all embeddings
FAISS IndexFlatL2 uses Euclidean distance:
- Closer vectors (smaller L2 distance) = more similar
- Exhaustive search guarantees best matches
- Fast for up to ~100K vectors
4. Semantic Search
Query is embedded and searched against index:
query_embedding = get_embedding(query) # Shape: (768,)
D, I = index.search(query_embedding.reshape(1, -1), top_k=5)
# D: distances, I: indices of most similar chunks
5. Answer Generation
Retrieved chunks are passed to Gemini as context:
context = "\n".join([metadata[i]["chunk"] for i in I[0]])
prompt = f"Query: {query}\n\nContext: {context}\n\nAnswer:"
response = client.models.generate_content(model="gemini-2.0-flash", contents=prompt)
Example Workflow
Scenario: Building a SAP Documentation RAG System
- Initial setup with SAP Joule documentation:
# Edit ollamaEmbed.py
file_path = Path("sapfiles/SAP_Joule.txt")
python ollamaEmbed.py
# Output: ✅ Indexed 142 chunks
- Query the system:
# Edit ragModel.py
query = "What is SAP Joule?"
python ragModel.py
# Output: SAP Joule is an AI assistant that helps users...
- Add more documentation:
# Edit appendEmbeds.py
new_file_path = "sapfiles/SAP_GENAI.txt"
python appendEmbeds.py
# Output: ✅ Added 87 new chunks to the index
- Query with expanded knowledge base:
# Query now searches across both documents
python ragModel.py
Advanced Usage
Batch Processing Multiple Documents
Modify ollamaEmbed.py to process a directory:
from pathlib import Path
all_chunks = []
metadata = []
for file_path in Path("docfiles").glob("*.txt"):
chunks, meta = process_document(file_path)
all_chunks.extend(chunks)
metadata.extend(meta)
# Then create index as usual
Custom Similarity Metrics
Replace IndexFlatL2 with other FAISS indices:
# Inner product (for normalized embeddings)
index = faiss.IndexFlatIP(dimension)
# Approximate search (faster for large datasets)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist=100)
Multi-Query RAG
Query multiple times with refined prompts:
queries = [
"What is SAP Joule?",
"How does SAP Joule integrate with SAP systems?",
"What are the key features of SAP Joule?"
]
for query in queries:
results = search_index(index, metadata, query, top_k=5)
# Process results...
Performance Optimization
For Large Document Collections (>10K chunks)
- Use IVFPQ index (Inverted File with Product Quantization):
nlist = 100 # Number of clusters
m = 8 # Number of subquantizers
quantizer = faiss.IndexFlatL2(dimension)
index = faiss.IndexIVFPQ(quantizer, dimension, nlist, m, 8)
index.train(embeddings) # Training required
index.add(embeddings)
- Batch embedding generation:
# Process chunks in batches
batch_size = 50
for i in range(0, len(chunks), batch_size):
batch = chunks[i:i+batch_size]
# Generate embeddings for batch...
For Faster Queries
- Reduce top_k: Retrieve fewer chunks (3 instead of 5)
- Use Gemini Flash: Faster than Pro models
- Cache embeddings: Store query embeddings for repeated queries
Troubleshooting
Ollama Connection Error
Error: requests.exceptions.ConnectionError: Failed to establish a new connection
Solution:
# Check if Ollama is running
curl http://localhost:11434/api/tags
# If not, start Ollama
ollama serve
Model Not Found
Error: model 'nomic-embed-text' not found
Solution:
ollama pull nomic-embed-text
ollama list # Verify model is downloaded
FAISS Dimension Mismatch
Error: RuntimeError: Error in void faiss::IndexFlat::add_with_ids()
Solution: Delete existing .faiss and .json files and rebuild:
rm my_faiss_index.faiss my_metadata.json
python ollamaEmbed.py
Gemini API Error
Error: google.api_core.exceptions.PermissionDenied: 403 API key not valid
Solution: Verify your .env file:
cat .env # Should show: GEMINI_API_KEY=AIza...
Get a new key from Google AI Studio
Memory Issues (Large Documents)
Error: MemoryError: Unable to allocate array
Solution: Process documents in smaller batches or use dimensionality reduction:
# Use PCA to reduce dimensions
import faiss
pca_matrix = faiss.PCAMatrix(768, 256) # Reduce to 256 dimensions
index = faiss.IndexFlatL2(256)
Technical Details
FAISS IndexFlatL2
- Algorithm: Exhaustive search (brute force)
- Metric: L2 (Euclidean) distance
- Accuracy: 100% (finds exact nearest neighbors)
- Speed: O(N*D) where N=number of vectors, D=dimension
- Best for: Up to 1M vectors with 768 dimensions
Nomic Embeddings
- Model:
nomic-embed-text - Dimensions: 768
- Context Length: 8192 tokens
- Trained on: Billions of text pairs for semantic similarity
- License: Open-source (Apache 2.0)
Chunking Strategy
Why 60 words with 10-word overlap?
- 60 words: ~300-400 characters, fits typical semantic units (paragraphs)
- 10-word overlap: Prevents context loss at boundaries
- Result: Each chunk has some context from neighbors
Trade-offs:
- More overlap → Better context, more storage
- Less overlap → Faster processing, potential context gaps
Comparison with Alternatives
| Component | This Project | Alternative |
|---|---|---|
| Embeddings | Ollama (local, free) | OpenAI ($0.0001/1K tokens) |
| Vector DB | FAISS (local, fast) | Pinecone/Weaviate (cloud, $$) |
| LLM | Gemini Flash (cheap) | GPT-4 (expensive) |
| Storage | Local files | Cloud databases |
| Privacy | Embeddings local | Data sent to APIs |
Advantages:
- Lower cost (only LLM API calls)
- Privacy (embeddings stay local)
- Fast (local FAISS search)
- No vendor lock-in
Disadvantages:
- Requires Ollama setup
- Local storage needed
- No built-in scaling
Extending the Project
Add Support for PDF Documents
pip install PyPDF2
import PyPDF2
def extract_text_from_pdf(pdf_path):
with open(pdf_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text()
return text
Add Conversational Memory
Store conversation history and include in context:
conversation_history = []
conversation_history.append({"role": "user", "content": query})
conversation_history.append({"role": "assistant", "content": response.text})
Add Web UI with Streamlit
pip install streamlit
import streamlit as st
st.title("RAG Document Q&A")
query = st.text_input("Ask a question:")
if st.button("Search"):
results = search_index(index, metadata, query, top_k=5)
answer = generate_answer(results, query)
st.write(answer)
Deploy as MCP Server
Integrate with the MCP (Model Context Protocol) pattern:
- Expose RAG search as an MCP tool
- Add resources for document management
- See
../agenticMCP_rag/for reference implementation
References
License
Part of the experimental AI/ML monorepo. See root repository for license details.
Related Projects
For more advanced RAG implementations, see:
../agenticMCP_rag/- RAG with MCP integration and agent architecture../R_RAG/- Alternative RAG implementation../perceptionAgent/- Agent architecture with perception-decision-action pattern
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.