Skip to main content
Glama
konradxmalinowski

notes-rag-mcp

README.md
<h1 align="center">Notes RAG MCP Server</h1>

<p align="center">
  <img src="https://img.shields.io/badge/Python-3.12+-3776AB?logo=python&logoColor=white" alt="Python 3.12+">
  <img src="https://img.shields.io/badge/LangChain-LCEL-1C3C3C?logo=langchain&logoColor=white" alt="LangChain LCEL">
  <img src="https://img.shields.io/badge/Google_Gemini-2.5_flash-4285F4?logo=google&logoColor=white" alt="Google Gemini">
  <img src="https://img.shields.io/badge/PostgreSQL-15-336791?logo=postgresql&logoColor=white" alt="PostgreSQL">
  <img src="https://img.shields.io/badge/ChromaDB-vector_store-FF6F00" alt="ChromaDB">
  <img src="https://img.shields.io/badge/MCP-FastMCP-000000" alt="MCP">
  <img src="https://img.shields.io/badge/license-MIT-blue" alt="License MIT">
</p>

<p align="center">
  A Python MCP server that exposes RAG-powered note management as AI agent tools — combining PostgreSQL, ChromaDB, and Google Gemini via a LangChain LCEL pipeline.
</p>

---

## Table of Contents

- [Overview](#overview)
- [Features](#features)
- [Architecture](#architecture)
- [Tech Stack](#tech-stack)
- [Getting Started](#getting-started)
- [Configuration](#configuration)
- [MCP Tools](#mcp-tools)
- [Connecting to MCP Clients](#connecting-to-mcp-clients)
- [Project Structure](#project-structure)
- [License](#license)

---

## Overview

**Notes RAG MCP Server** is a [Model Context Protocol](https://modelcontextprotocol.io/) server that turns a personal note collection into a knowledge base an AI agent can query. Notes are persisted in **PostgreSQL** (source of truth), their embeddings stored in **ChromaDB**, and answers generated by **Google Gemini** through a declarative **LangChain LCEL** chain.

Any MCP-compatible client — Claude Desktop, Claude Code, the MCP Inspector, or a custom agent — can call the six exposed tools to add, search, ask, summarize, list, and delete notes.

---

## Features

- **Semantic search** — vector similarity search via ChromaDB returns the most relevant notes for any query
- **RAG Q&A** — a full LCEL pipeline (`retriever | prompt | llm | parser`) grounds answers strictly in stored notes, never hallucinating beyond context
- **Consistent dual store** — every write and delete keeps PostgreSQL and ChromaDB in sync automatically
- **Note summarization** — dedicated LCEL chain condenses any note to 2–3 sentences on demand
- **MCP resource** — notes are also exposed as `notes://<id>` resources for direct content access
- **Lazy initialization** — Gemini and ChromaDB clients are created only on first use; importing the module requires no API key
- **Idempotent setup** — the database schema is applied at startup with `IF NOT EXISTS`; safe to restart at any time

---

## Architecture

```
MCP Client (Claude Desktop / Inspector / Claude Code)
        │  stdio transport
        ▼
┌─────────────────────────────┐
│       server.py             │
│  FastMCP — 6 tools +        │
│  notes://{id} resource      │
└────────┬────────────────────┘
         │
    ┌────┴────┐
    ▼         ▼
┌────────┐  ┌──────────────────────────────────────┐
│  db.py │  │           rag_chain.py               │
│        │  │  GoogleGenerativeAIEmbeddings         │
│ psycopg│  │  ChatGoogleGenerativeAI (Gemini)      │
│   3    │  │  Chroma (langchain-chroma)            │
│        │  │                                      │
│ Source │  │  LCEL: retriever | prompt | llm |    │
│ of     │  │         StrOutputParser              │
│ truth  │  └──────────────────────────────────────┘
└───┬────┘           │
    │                │
    ▼                ▼
┌──────────┐   ┌───────────┐
│PostgreSQL│   │ ChromaDB  │
│ (notes   │   │ (vector   │
│  table)  │   │  index)   │
└──────────┘   └───────────┘
```

**RAG question flow (`ask` tool):**

```python
{"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt | llm | StrOutputParser()
```

The ChromaDB retriever fetches the most similar notes → `format_docs` merges them into a context block → Gemini answers **only from that context**.

---

## Tech Stack

| Layer | Technology |
|---|---|
| MCP server | [FastMCP](https://github.com/jlowin/fastmcp) (`mcp[cli]`) |
| LLM | Google Gemini 2.5 Flash (`langchain-google-genai`) |
| Embeddings | Gemini Embedding 001 (`gemini-embedding-001`) |
| RAG pipeline | LangChain LCEL (`langchain-core`) |
| Vector store | ChromaDB (`langchain-chroma`) |
| Relational DB | PostgreSQL 15 (`psycopg` 3) |
| Config | `python-dotenv` |

---

## Getting Started

### Prerequisites

- Python ≥ 3.12
- PostgreSQL running locally (e.g. `postgresql@15` via Homebrew)
- A [Google AI Studio](https://aistudio.google.com/apikey) API key

### Installation

```bash
# 1. Clone the repository
git clone https://github.com/konradxmalinowski/project-rag-mcp.git
cd project-rag-mcp

# 2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

# 3. Install dependencies
pip install -e .

# 4. Create the database
createdb notes_mcp

# 5. Configure environment variables
cp .env.example .env
# Edit .env — set GEMINI_API_KEY and verify DATABASE_URL
```

The `notes` table is created automatically on first server startup. You can also apply the schema manually:

```bash
psql -d notes_mcp -f schema.sql
```

---

## Configuration

| Variable | Required | Default | Description |
|---|---|---|---|
| `GEMINI_API_KEY` | Yes | — | Google AI Studio API key |
| `DATABASE_URL` | Yes | — | PostgreSQL connection string |
| `CHROMA_PATH` | No | `./chroma_db` | Persistent ChromaDB directory |
| `GEMINI_CHAT_MODEL` | No | `gemini-2.5-flash` | Chat model for generation |
| `GEMINI_EMBED_MODEL` | No | `gemini-embedding-001` | Embedding model |

Copy `.env.example` to `.env` and fill in the required values. Never commit `.env`.

---

## MCP Tools

| Tool | Description |
|---|---|
| `add_note(title, content)` | Persist a note to PostgreSQL and index its embedding in ChromaDB |
| `search_notes(query, top_k=3)` | Semantic similarity search — returns title, snippet, and distance |
| `ask(question, top_k=3)` | Full RAG: retrieve relevant notes → generate a grounded answer via Gemini |
| `list_notes()` | Return all notes (id, title, created_at), newest first |
| `summarize_note(note_id)` | Summarize a single note in 2–3 sentences using Gemini |
| `delete_note(note_id)` | Remove a note from both PostgreSQL and ChromaDB |

**MCP resource:** `notes://{note_id}` — exposes raw note content for direct read access.

---

## Connecting to MCP Clients

### MCP Inspector (browser UI — easiest for testing)

```bash
mcp dev server.py
```

Open the Inspector in your browser. In the **Tools** tab:

1. `add_note("Python GIL", "The GIL is a mutex that protects access to Python objects...")`
2. Add 2–3 more notes on different topics.
3. `search_notes("what is the GIL")` → the GIL note should rank first.
4. `ask("explain the GIL based on my notes")` → grounded answer citing note titles.
5. `summarize_note(1)`, then `delete_note(1)` → verify PostgreSQL ↔ ChromaDB consistency.

### Claude Desktop

Add the following entry to your Claude Desktop MCP config (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "notes-rag": {
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}
```

### Claude Code (CLI)

```bash
claude mcp add notes-rag /absolute/path/to/.venv/bin/python -- /absolute/path/to/server.py
```

---

## Project Structure

```
project-rag-mcp/
├── server.py        # FastMCP entry point — 6 tools + notes resource
├── rag_chain.py     # LangChain LCEL: Gemini + ChromaDB + RAG chain
├── db.py            # PostgreSQL data layer (psycopg3)
├── schema.sql       # notes table DDL
├── pyproject.toml   # project metadata and dependencies
├── .env.example     # environment variable template
└── LICENSE
```

---

## License

[MIT](LICENSE) © 2025 Konrad Malinowski