A comprehensive, hands-on tutorial for building production-ready AI agents using LangChain, Model Context Protocol (MCP), and Retrieval-Augmented Generation (RAG) with both Elasticsearch and ChromaDB.
This repository demonstrates:
- RAG Patterns - From simple to production-grade retrieval
- Vector vs Full-Text Search - ChromaDB vs Elasticsearch comparison
- MCP Integration - Official protocol for AI tool standardization
- Agent Architectures - When to use agents vs direct retrieval
- Production Patterns - Microservices, debugging, and best practices
yarn installCreate a .env file:
# Google API Key (get from https://makersuite.google.com/app/apikey)
GOOGLE_API_KEY=your-actual-api-key-here
# Elasticsearch Configuration
ELASTICSEARCH_URL=http://localhost:9200
# ChromaDB Configuration (optional)
CHROMA_URL=http://localhost:8000
CHROMA_EMBEDDING_FUNCTION=default📖 See SETUP.md for detailed setup instructions
MCP servers are pre-configured for Cursor IDE!
After setup, restart Cursor and you'll have access to:
- Elasticsearch tools: Search, index, and manage documents
- ChromaDB tools: Vector search and collection management
- Auto-discovery: Tools appear automatically in Cursor
- No additional setup: Just restart Cursor after running the tutorial
Path A: Elasticsearch (Full-Text Search)
# Terminal 1: Start Elasticsearch + Kibana
yarn elasticsearch:start
# Terminal 2: Run examples
yarn rag:case1 # Simple RAG
yarn rag:case2 # Agent-based
yarn rag:case3 # Production MCPPath B: ChromaDB (Vector Search)
# Terminal 1: Start ChromaDB
yarn chroma:start
# Terminal 2: Run example
# Explore data
yarn chroma:console.
├── src/
│ ├── mcp/ # MCP server examples (stdio)
│ ├── rag/ # RAG with Elasticsearch
│ │ ├── elasticsearch-mcp-http-server.ts # MCP server (HTTP)
│ │ ├── setup-data.ts # Data initialization
│ │ └── README.md # 📖 Elasticsearch RAG guide
│ └── chroma/ # RAG with ChromaDB
│ ├── setup-data.ts # Data initialization
│ ├── query-console.ts # Interactive query tool
│ └── README.md # 📖 ChromaDB RAG guide
│
├── test/
│ └── rag/ # RAG examples (⭐ START HERE)
│ ├── case1-direct-rag-example.ts # Direct RAG
│ ├── case2-agent-rag-example.ts # Agent + ES
│ ├── case3-agent-mcp-example.ts # Agent + MCP + ES
│ └── README.md # Examples comparison
│
├── data/
│ └── products.json # Shared product catalog
│
├── scripts/
│ ├── es-mcp/ # Elasticsearch Docker setup
│ │ ├── docker-compose.yml # ES + Kibana + init
│ │ └── Dockerfile
│ └── chroma-mcp/ # ChromaDB Docker setup
│ ├── docker-compose.yml # Chroma + init
│ └── Dockerfile
│
├── resources/
│ ├── MCP-Weather-Server.postman_collection.json
│ └── README.md
│
├── .env # ⚠️ CREATE THIS (see SETUP.md)
├── .gitignore # Excludes .env
├── SETUP.md # 📖 Setup guide
└── README.md # 👈 You are here
Best for: Understanding RAG fundamentals
yarn elasticsearch:start
yarn rag:case1Architecture:
User Query → Search ES → Pass results to AI → Generate Answer
Features:
- ✅ No agents, no complexity
- ✅ Debug logging shows ES queries
- ✅ See relevance scores
- ✅ Linear, predictable flow
When to use: Simple Q&A, document lookup, learning RAG
Best for: Smart assistants that need reasoning
yarn elasticsearch:start
yarn rag:case2Architecture:
User Query → Agent decides → Search ES → Agent reasons → Answer
Features:
- ✅ Agent intelligence (when/how to search)
- ✅ Custom LangChain tools
- ✅ Multi-step reasoning
- ✅ Debug logging
When to use: Chatbots, assistants, complex queries
Best for: Production apps, microservices, scalability
yarn elasticsearch:start
yarn mcp:elasticsearch-http
yarn rag:case3Architecture:
User Query → Agent → MCP Adapter → HTTP MCP Server → ES → Response
Features:
- ✅ Microservices architecture
- ✅ HTTP-based MCP (standard protocol)
- ✅ Process isolation
- ✅ Tool auto-discovery
- ✅ Decoupled components
When to use: Production systems, multiple services, team projects
Best for: Semantic search, AI-first applications
yarn chroma:startArchitecture:
User Query → Agent → MCP Adapter → chroma-mcp (Python) → ChromaDB → Response
Features:
- ✅ Official chroma-mcp Python server (via pip)
- ✅ Semantic similarity search
- ✅ Built-in embeddings (MiniLM-L6-v2)
- ✅ Configurable embedding models
- ✅ stdio-based MCP
When to use: Semantic search, embeddings, similarity matching
| Feature | Elasticsearch (Cases 1-3) | ChromaDB (Case 4) |
|---|---|---|
| Search Type | Full-text + filters | Vector similarity |
| Embeddings | Not built-in | Native support |
| Best For | Traditional search | AI/semantic search |
| Query Language | JSON DSL | Semantic queries |
| Setup | Docker + Kibana UI | Docker only |
| Console | Kibana Dev Tools | Custom CLI tool |
| Data Model | Documents + indexes | Collections + vectors |
| Use Case | Search engines, logs | AI apps, recommendations |
yarn elasticsearch:start # Start ES + Kibana + auto-load data
yarn elasticsearch:stop # Stop all servicesWhat you get:
- ✅ Elasticsearch at http://localhost:9200
- ✅ Kibana UI at http://localhost:5601
- ✅ Sample data auto-loaded (5 products)
yarn chroma:start # Start ChromaDB + auto-load data
yarn chroma:stop # Stop service
yarn chroma:console # Interactive query console ⭐What you get:
- ✅ ChromaDB at http://localhost:8000
- ✅ Sample data auto-loaded (same 5 products)
- ✅ Interactive console for queries
yarn rag:case1 # Direct RAG (Elasticsearch)
yarn rag:case2 # Agent + ES tools
yarn rag:case3 # Agent + MCP + ES (production)
# Agent + MCP + ChromaDB (vectors)yarn mcp:elasticsearch # Elasticsearch MCP (stdio)
yarn mcp:elasticsearch-http # Elasticsearch MCP (HTTP) ⭐# MCP servers are pre-configured for Cursor IDE!
# Configuration files:
# - ~/.cursor/mcp.json (global)
# - .cursor/mcp.json (project-specific)
# Available MCP servers in Cursor:
# - elasticsearch-mcp: Search and index documents
# - chromadb-mcp: Vector search and collectionsWhat you get in Cursor:
- ✅ Auto-discovery: Tools appear automatically in Cursor
- ✅ No setup needed: Just restart Cursor after configuration
- ✅ Both servers: Elasticsearch + ChromaDB MCP servers
- ✅ Relative paths: Portable configuration
- ✅ Environment ready: All env vars pre-configured
yarn data:setup # ES data initialization
yarn chroma:data:setup # Chroma data initialization- Elasticsearch API: http://localhost:9200
- Kibana UI: http://localhost:5601
- Kibana Dev Tools: http://localhost:5601/app/dev_tools#/console
- ChromaDB API: http://localhost:8000
- Interactive Console:
yarn chroma:console
# 1. Create .env with your Google API key
cat > .env << 'EOF'
GOOGLE_API_KEY=your-key-here
ELASTICSEARCH_URL=http://localhost:9200
EOF
# 2. Start Elasticsearch
yarn elasticsearch:start
# 3. Run the simplest example
yarn rag:case1What to observe:
- 📤 How queries are structured
- 📥 What data comes back
- 🎯 Relevance scores
- 🤖 How AI uses the context
# Same Elasticsearch is running
yarn rag:case2What to observe:
- 🤖 Agent decides when to search
- 🔧 Tool calling in action
- 💭 Agent reasoning process
# Terminal 1: Elasticsearch (already running)
# Terminal 2: Start MCP HTTP server
yarn mcp:elasticsearch-http
# Terminal 3: Run agent
yarn rag:case3What to observe:
- 📡 Process isolation
- 🔌 HTTP communication
- 🛠️ Tool auto-discovery
- 🏗️ Microservices architecture
# Terminal 1: Start ChromaDB
yarn chroma:start
# Terminal 2: Explore data
yarn chroma:console
# Try: list, peek products, query products laptop
# Terminal 3: Run agentWhat to observe:
- 🧮 Semantic similarity
- 📐 Embedding vectors
- 🎯 Distance scores
- 🐍 Python MCP server via pip (chroma-mcp)
All examples use the same product catalog from data/products.json:
- Consistent across Elasticsearch and ChromaDB
- Easy to modify and experiment
- Real-world product data structure
Every RAG example shows:
📤 Elasticsearch Query:
{
"index": "products",
"query": { "multi_match": { "query": "laptop" } }
}
📥 Results:
Total: 3, Max Score: 2.45
1. [2.45] Dell XPS 15 ($1299)
2. [1.87] MacBook Pro ($1999)
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
const mcpClient = new MultiServerMCPClient({
elasticsearch: {
type: "http",
url: "http://localhost:8001/mcp"
}
});
// Magic! No manual tool definitions needed
const tools = await mcpClient.getTools();Elasticsearch (Kibana):
- Full UI at http://localhost:5601
- Dev Tools for query testing
- Visual index management
ChromaDB (Custom CLI):
$ yarn chroma:console
chroma> list
📚 Available Collections:
📦 products (5 documents)
chroma> query products laptop 2
🔍 Searching "products" for: "laptop"
1. [0.3421] MacBook Pro 16"
2. [0.4122] Dell XPS 13Pre-configured MCP servers ready to use in Cursor:
// ~/.cursor/mcp.json (automatically created)
{
"mcpServers": {
"elasticsearch-mcp": {
"command": "node",
"args": ["--import", "tsx/esm", "./src/rag/elasticsearch-mcp-http-server.ts"],
"env": { "ELASTICSEARCH_URL": "http://localhost:9200" }
},
"chromadb-mcp": {
"command": "node",
"args": ["--import", "tsx/esm", "./src/chroma/chroma-mcp-http-server.ts"],
"env": { "CHROMA_URL": "http://localhost:8000" }
}
}
}Available tools in Cursor:
elasticsearch_search- Search documentselasticsearch_index_document- Index new documentselasticsearch_get_indices- List all indiceschroma_query_collection- Semantic searchchroma_list_collections- List collectionschroma_get_collection_info- Collection details
ChromaDB example shows how to configure embeddings:
# In .env
CHROMA_EMBEDDING_FUNCTION=default # or openai, cohere, jina
# Default: MiniLM-L6-v2 (free, local, 384 dims)
# OpenAI: text-embedding-ada-002 (paid, 1536 dims)| Category | Technology | Purpose |
|---|---|---|
| Language | TypeScript | Type-safe development |
| AI Framework | LangChain | Agent orchestration |
| AI Model | Google Gemini | Text generation |
| Search (Text) | Elasticsearch 8.11 | Full-text search |
| Search (Vector) | ChromaDB 0.4.24 | Semantic search |
| Protocol | MCP (Model Context Protocol) | Tool standardization |
| MCP Integration | @langchain/mcp-adapters | Auto tool discovery |
| MCP SDK | @modelcontextprotocol/sdk | Server implementation |
| Runtime | tsx | TypeScript execution |
| Environment | dotenv | Config management |
| Validation | Zod | Schema validation |
| Containers | Docker Compose | Service orchestration |
{
"langchain": "^1.0.0-alpha.9",
"@langchain/core": "^1.0.0-alpha.7",
"@langchain/google-genai": "^1.0.0",
"@langchain/mcp-adapters": "^1.0.0",
"@elastic/elasticsearch": "^8.11.0",
"chromadb": "^3.0.17",
"@modelcontextprotocol/sdk": "^1.20.1",
"dotenv": "^17.2.3",
"tsx": "^4.19.2"
}# Error: "Please set an API key for Google GenerativeAI"
# Solution: Create .env file
cat > .env << 'EOF'
GOOGLE_API_KEY=your-actual-key
ELASTICSEARCH_URL=http://localhost:9200
CHROMA_URL=http://localhost:8000
EOF# Check if port is in use
lsof -i :9200
# Clean up and restart
yarn elasticsearch:stop
docker system prune -f
yarn elasticsearch:start# Error: "The v1 API is deprecated. Please use /v2 apis"
# Solution: The docker-compose.yml pins to compatible version
yarn chroma:stop
yarn chroma:start# Test HTTP server directly
curl http://localhost:8001/health
# Should return: {"status":"ok","mcp_server":"running"}# Install chroma-mcp Python package
pip3 install chroma-mcp
# Verify installation
python3 -m chroma_mcp --help- ✅ Always use
.envfor API keys - ✅ Never commit
.envto git - ✅ Use
dotenv/configat import time
- Simple Q&A? → Case 1 (direct RAG)
- Need reasoning? → Case 2 (agent + tools)
- Production system? → Case 3/4 (agent + MCP)
- Semantic search? → Case 4 (ChromaDB)
- Structured data + filters → Elasticsearch
- Semantic similarity → ChromaDB
- Complex queries → Elasticsearch
- AI-first apps → ChromaDB
- ✅ Enable debug logging (already done!)
- ✅ Use web consoles (Kibana/chroma:console)
- ✅ Check MCP server health endpoints
- ✅ View Docker logs when needed
- ✅ Use MCP for service decoupling
- ✅ Implement proper error handling
- ✅ Add authentication to MCP servers
- ✅ Monitor with proper logging
- ✅ Use environment-specific configs
- SETUP.md - Environment setup and API keys
- Elasticsearch RAG - Elasticsearch integration guide
- ChromaDB RAG - ChromaDB integration guide
- RAG Examples - All examples comparison
- Postman Resources - API testing
- LangChain Documentation
- MCP Specification
- Elasticsearch Guide
- ChromaDB Docs
- Google AI Studio - Get API key
- ✅
.envin.gitignore- never committed - ✅ No API keys in source code
- ✅
dotenvauto-loads from.env - ✅ Docker services isolated in networks
⚠️ Add authentication for production MCP servers⚠️ Use HTTPS in production⚠️ Rotate API keys regularly
When you run yarn elasticsearch:start or yarn chroma:start:
- Service starts and waits until healthy
- Data initialization runs automatically
- Sample data loaded (products from
data/products.json) - Ready to use - no manual steps!
All initialization is in Docker Compose - zero configuration needed! 🎉
MIT
This is a tutorial project for learning. Feel free to:
- Fork and experiment
- Submit improvements
- Share with others
- Use in your projects
echo "GOOGLE_API_KEY=your-key" > .env
yarn elasticsearch:start
yarn rag:case1 # Watch the magic happen! ✨echo "GOOGLE_API_KEY=your-key" > .env
yarn chroma:start
# Explore vector search! 🚀# Elasticsearch console
open http://localhost:5601/app/dev_tools#/console
# ChromaDB console
yarn chroma:consoleHappy learning! 🎓