Skip to main content
Glama
00aimlds00

notes-rag-pg

by 00aimlds00
# Notes RAG on PostgreSQL + pgvector + MCP

Migrates the retrieval layer of [AskMyNotes](https://github.com/00aimlds00/class-notes-rag) from flat files (NumPy + pickle) to a real PostgreSQL database with the [pgvector](https://github.com/pgvector/pgvector) extension, running in Docker. The database is exposed to Claude through a custom [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server, so questions can be answered via genuine SQL-backed semantic search instead of an in-memory NumPy comparison.

## Architecture

既存のノートインデックス (embeddings.npy, chunks.pkl, metadata.json) │ ▼ migrate.py ──────────────► PostgreSQL + pgvector (Docker) │ ▼ mcp_server.py (MCP ツール) │ ▼ mcp-client ──────────► Claude


## Prerequisites

- **Docker Desktop** — running and verified working (`docker run hello-world`)
- **[uv](https://docs.astral.sh/uv/)** — Python package/environment manager
- **An Anthropic API key** — [console.anthropic.com](https://console.anthropic.com)
- **A pre-built notes index from [class-notes-rag](https://github.com/00aimlds00/class-notes-rag)** — this project migrates that data, it doesn't generate it. Clone that repo, add your own notes to `notes/`, and run its `ingest.py` first to produce `index/embeddings.npy`, `index/chunks.pkl`, and `index/metadata.json`.
- **[mcp-client](https://github.com/00aimlds00/mcp-client)** — used to actually connect to and query the MCP server built here.

This repo, `class-notes-rag`, and `mcp-client` are designed to sit as **sibling folders** on disk, e.g.:

C:\Users\you\class-notes-rag
C:\Users\you\notes-rag-pg\ ← このリポジトリ C:\Users\you\mcp-client\


## Setup

1. Clone this repo and start the database:

git clone https://github.com/00aimlds00/notes-rag-pg.git cd notes-rag-pg docker compose up -d

This runs PostgreSQL 16 with pgvector pre-installed, on host port **5433** (not 5432 — chosen to avoid clashing with any existing native PostgreSQL install on your machine; change it in `docker-compose.yml` if 5433 is also taken).

2. Install Python dependencies:

uv sync


3. Create a `.env` file in this folder:

DATABASE_URL=postgresql://notesuser:notespass@localhost:5433/notesdb ANTHROPIC_API_KEY=your-api-key-here


4. Migrate your notes index (requires `class-notes-rag/index/` to already exist, as a sibling directory — see Prerequisites):

uv run migrate.py

This creates a `notes_chunks` table and loads every chunk and embedding. Safe to re-run — it drops and recreates the table each time.

## Usage

**Test retrieval directly**, without MCP or Claude, to confirm the database is working:

uv run test_search.py


**Connect via MCP**, from your `mcp-client` project:

cd ../mcp-client uv add psycopg2-binary sentence-transformers uv run client.py ../notes-rag-pg/mcp_server.py

(The `uv add` step is needed because `mcp-client` launches this server as a subprocess using its own Python environment — see Troubleshooting below.)

Then ask a question, e.g. `What is a patent?` — Claude will call the `search_notes` tool, which runs a live cosine-distance SQL query against Postgres.

## Tools exposed by the MCP server

| Tool | Description |
|---|---|
| `search_notes(query, top_k=4, class_filter="")` | Semantic search over indexed notes. `class_filter` can restrict to `"class-xi"` or `"class-xii"`. |
| `list_note_sources(class_filter="")` | Lists all distinct indexed source files. |

## Design notes

- **No approximate-nearest-neighbor index (ivfflat/hnsw) is used.** At this data scale (a few thousand chunks), exact brute-force cosine search is both fast enough and fully accurate. An `ivfflat` index was tested and found to *reduce* retrieval accuracy at this size — see full write-up in the project documentation.
- **Uses the public `pgvector/pgvector:pg16` image unmodified** — no custom Docker image to build or pull separately.
- **`mcp_server.py` disables Hugging Face/Transformers stdout output** (`HF_HUB_DISABLE_PROGRESS_BARS`, etc.) and lazy-loads `sentence-transformers`, since MCP's stdio transport uses stdout exclusively for JSON-RPC — any stray print statement would corrupt the protocol stream.

## Troubleshooting

- **`password authentication failed`** — likely a port conflict with an existing native PostgreSQL install. Change the host-side port in `docker-compose.yml` (`"5433:5432"` → e.g. `"5434:5432"`) and update `DATABASE_URL` to match.
- **MCP client shows `Connection closed` / handshake timeout** — the server process almost certainly crashed on import. Make sure `mcp-client`'s own environment has `psycopg2-binary` and `sentence-transformers` installed (see Usage above) — it launches the server using its own Python interpreter, not this project's.
- **Search returns irrelevant results** — if you've added a vector index, try dropping it (`DROP INDEX ...;`) and re-testing with `test_search.py`. Approximate indexes need tuning (`probes`) that isn't worth it below tens of thousands of rows.

## License

MIT

Available Tools

2 tools
list_note_sourcesA

List all distinct source files currently indexed in the notes database.

Args: class_filter: Optional — restrict to "class-xi" or "class-xii". Leave empty for both.

ParametersJSON Schema
NameRequiredDescriptionDefault
class_filterNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. The verb 'list' clearly implies a read-only operation, and the description specifies that it enumerates distinct sources. For a simple query tool, this adequately conveys the behavioral profile, though it does not go into detail about limitations (e.g., sorting, limits) — which are not critical here.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences: purpose first, then parameter details. It is front-loaded, has zero redundancy, and every word earns its place. Structure is well-organized with a clear separation of purpose and argument explanation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple, has an output schema (so return format is covered), and the only parameter is fully explained. The description provides all necessary information for correct invocation. While it doesn't contrast with the sibling, that is a usage-guidelines gap rather than a completeness gap for this simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description compensates fully. It explains that class_filter is optional, restricts to 'class-xi' or 'class-xii', and empty means both. This adds meaning beyond the schema's bare type and default, and even gives allowed values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('list') and resource ('all distinct source files currently indexed in the notes database'), which is precise and clearly differentiates from the sibling search_notes, which would search within notes rather than enumerate sources. This is a clear, non-tautological purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not explicitly contrast this tool with search_notes or state when to use it versus the alternative. The purpose implies a listing use case, but there is no explicit when-to-use or when-not-to-use guidance, leaving it to the agent to infer from context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_notesA

Search the indexed class notes (Class XI & XII) for content relevant to a question.

Args: query: The natural-language question or topic to search for. top_k: Number of top matching chunks to return (default 4). class_filter: Optional — restrict results to "class-xi" or "class-xii". Leave empty to search both.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
top_kNo
class_filterNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the burden. It discloses the read-only nature implicitly via 'Search' and states it returns 'top matching chunks', which covers the primary behavior. However, it does not explicitly state side effects, auth requirements, or any constraints, leaving the agent to infer the operational profile.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured: a one-line purpose statement followed by a clear 'Args:' section with bullet points for each parameter. Every sentence adds value, and the main purpose is front-loaded before parameter details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is relatively simple with an output schema present, so return format details are covered elsewhere. The description sufficiently explains parameters and the search scope. The only gap is explicit routing to the sibling 'list_note_sources', but given the distinct verb and resource, the context is nearly complete for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, yet the description thoroughly explains all three parameters: query as a natural-language question, top_k as the number of chunks with a default, and class_filter with allowed values and semantics. It fully compensates for the schema's lack of descriptions, adding meaning beyond raw types and defaults.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb ('Search'), a specific resource ('indexed class notes (Class XI & XII)'), and the scope ('Class XI & XII'). It distinguishes itself from the sibling 'list_note_sources' by focusing on content search rather than listing sources.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies use for searching content by question/topic but provides no explicit guidance on when to choose this tool over the sibling 'list_note_sources'. It does not mention when not to use it or give alternative routing, so usage is inferred rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.3/5.0
Disambiguation5/5

Each tool has a completely distinct purpose: one searches content, the other lists source files. There is no overlap in functionality or arguments, so an agent would never confuse them.

Naming Consistency5/5

Both tools follow a clear verb_noun pattern: 'search_notes' and 'list_note_sources'. They use snake_case consistently and start with an imperative verb, making the naming predictable and intuitive.

Tool Count3/5

With only two tools, the server feels minimal. While the scope (searching and listing notes) is narrow and could justify a lean surface, it is at the borderline where agents might benefit from additional operations like fetching a specific chunk or retrieving full notes.

Completeness5/5

For a read-only RAG server over class notes, the two tools cover the essential workflows: searching relevant content and listing available sources. There are no obvious missing operations that would cause an agent to get stuck in a typical use case.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    Enables indexing and retrieving notes with full-text search using SQLite, plus building knowledge graphs to find relationships between concepts. Supports natural language note management, tagging, and semantic connections.
    16
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides semantic search and keyword search over Obsidian notes, along with direct note retrieval, allowing external AI agents to query and access the vault.
    18
    BSD Zero Clause

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/00aimlds00/notes-rag-pg'

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