mcp-pgvector-search

mcp-pgvector-search

An MCP server that enables hybrid semantic and keyword retrieval over your documents, using PostgreSQL and pgvector as the backend. It fuses rankings from both methods to provide high-quality search results to the language model.

Category
Visit Server

README

mcp-pgvector-search

An MCP server that gives a language model retrieval over your own documents, using PostgreSQL and pgvector as the whole backend.

It runs two searches for every query — semantic and keyword — and fuses the rankings. No separate vector database, no extra service to operate.

Why hybrid, and not just embeddings

Vector search and keyword search fail in opposite directions.

Embeddings match meaning and miss exact tokens. Ask for error code E4021 and a semantic search returns passages about error handling. The one paragraph that names the code may not rank at all, because a rare alphanumeric string barely moves an embedding.

Full-text search nails those and misses paraphrase entirely. Ask "how do I cancel my plan" against a document that says "terminating a subscription" and you get nothing.

Running both recovers most of what either misses. The interesting part is how you combine them.

Why Reciprocal Rank Fusion

The obvious approach is to normalize both scores and add them. It does not work well: a cosine similarity sits around 0.7–0.9, a PostgreSQL ts_rank is often below 0.1, and the mapping between them shifts as the corpus grows. Any weighting you tune today is wrong next month.

RRF ignores the scores and combines by position. Each ranking contributes 1 / (k + rank), summed across rankings. Nothing to tune, and it has a property worth having: a passage both retrievers found ranks above one that topped only a single list. Two independent methods agreeing is a stronger signal than either alone, and the fused order says so.

Every result reports which retrievers found it, so the model can weigh it:

{
  "document": "docs/billing.md",
  "text": "Subscriptions can be terminated from…",
  "relevance": 0.03252,
  "matched_by": ["keyword", "semantic"],
  "high_confidence": true
}

Setup

pip install mcp-pgvector-search
psql "$DATABASE_URL" -f schema.sql
export DATABASE_URL="postgresql://user:pass@host/db"
export EMBEDDING_API_KEY="sk-..."

Index a file or a directory:

mcp-pgvector-search index ./docs

Run the server:

mcp-pgvector-search

Or point an MCP client at it:

{
  "mcpServers": {
    "pgvector-search": {
      "command": "mcp-pgvector-search",
      "env": {
        "DATABASE_URL": "postgresql://user:pass@host/db",
        "EMBEDDING_API_KEY": "sk-..."
      }
    }
  }
}

Design decisions

Chunking splits on the largest natural boundary that fits. Paragraphs first, then sentences, and a hard character cut only when a single sentence exceeds the budget — which happens with tables, code blocks and minified text. A chunk that cuts mid-sentence embeds badly; a chunk spanning three unrelated topics embeds into an average of all of them and matches none well.

Chunks overlap by default. A fact stated across a boundary is otherwise retrievable from neither side. The cost is storing the overlapping text twice, which is cheap next to silently losing an answer.

The embedding provider sits behind an interface. The wire format is the OpenAI-compatible /v1/embeddings shape, which OpenAI, Azure, LiteLLM, Ollama and most local inference servers speak. Switching provider is two environment variables, not a rewrite. Dimension is verified at startup, because a mismatch found during a query is a confusing error at the worst possible moment.

Indexing is checksum-guarded. Re-running the indexer over unchanged files re-embeds nothing. Replacing a document happens in one transaction, so a failure halfway leaves the previous version intact rather than a half-indexed document that returns partial answers.

The tsvector is a generated column. It cannot drift out of sync with the content it indexes, because the application never writes it.

HNSW rather than IVFFlat. Slower to build, and worth it: no training pass, so accuracy holds as the corpus grows instead of degrading until someone remembers to reindex.

Results are capped by character budget. Retrieval quality is not the only constraint — everything returned lands in a context window someone pays for by the token. When results are dropped, the response says so, so the model narrows its query instead of assuming it saw everything.

Empty results say so explicitly. The response tells the model to report that nothing matched rather than answering from general knowledge. Silent empty retrieval is how a RAG system starts confidently making things up.

Configuration

Variable Required Default Notes
DATABASE_URL yes — PostgreSQL DSN with the vector extension
EMBEDDING_API_KEY usually — Omit for local servers that need no auth
EMBEDDING_BASE_URL no https://api.openai.com/v1 Any OpenAI-compatible endpoint
EMBEDDING_MODEL no text-embedding-3-small
EMBEDDING_DIMENSION no 1536 Must match both the model and schema.sql

Changing the embedding model means changing the dimension and re-embedding the corpus. That is a property of embeddings, not a limitation of this tool.

Development

pip install -e ".[dev]"
pytest
ruff check .

Chunking and fusion have no database or network dependencies, so the retrieval logic is tested without a running PostgreSQL.

License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured