Metadata-Version: 2.4
Name: mcp-server-pgvector
Version: 0.1.0
Summary: MCP server exposing pgvector similarity search, hybrid search, and index management as tools for LLM agents
Keywords: mcp,model-context-protocol,pgvector,postgres,vector-search,rag
Author: Praveen Mittal
Author-email: Praveen Mittal <pkmittal28@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Database
Requires-Dist: mcp>=1.12.0
Requires-Dist: asyncpg>=0.30.0
Requires-Dist: pydantic>=2.9.0
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/mittalpk/mcp-server-pgvector
Project-URL: Repository, https://github.com/mittalpk/mcp-server-pgvector
Project-URL: Issues, https://github.com/mittalpk/mcp-server-pgvector/issues
Description-Content-Type: text/markdown

# mcp-server-pgvector

[![CI](https://github.com/mittalpk/mcp-server-pgvector/actions/workflows/ci.yml/badge.svg)](https://github.com/mittalpk/mcp-server-pgvector/actions/workflows/ci.yml)

<!-- mcp-name: io.github.mittalpk/pgvector -->

An [MCP](https://modelcontextprotocol.io/) server that gives LLM agents first-class access to
[pgvector](https://github.com/pgvector/pgvector)-backed embedding tables in PostgreSQL: similarity
search, hybrid (vector + full-text) search, upserts, and HNSW/IVFFlat index management.

Generic Postgres MCP servers expose raw SQL or schema introspection; this one speaks pgvector
specifically — nearest-neighbor search, distance metrics, and ANN index tuning are first-class
tools, not something the model has to hand-write SQL for.

## Tools

| Tool | Description |
|---|---|
| `list_vector_tables` | Discover every `vector` column in the database, with its dimensionality |
| `describe_vector_table` | Columns, indexes, and approximate row count for a table |
| `similarity_search` | k-NN search over a vector column (cosine / L2 / inner product), with structured metadata filters |
| `hybrid_search` | Weighted blend of vector similarity and Postgres full-text search (`ts_rank_cd`) |
| `upsert_embedding` | Insert or update a row's embedding + metadata |
| `create_vector_index` | Create an HNSW or IVFFlat index with tunable parameters |
| `explain_similarity_query` | `EXPLAIN ANALYZE` a similarity query to confirm the ANN index is used |

## Safety

- Every table/column name is validated against `information_schema` / `pg_catalog` before being
  interpolated into SQL — an LLM can only ever reference identifiers that already exist. Values are
  always bound parameters.
- Metadata filters are a closed `{column, op, value}` allowlist, not a raw SQL fragment.
- Set `MCP_PGVECTOR_READ_ONLY=true` to disable `upsert_embedding` and `create_vector_index`,
  leaving only read/search tools available — useful when pointing the server at a production
  database.
- Every query runs with a per-command timeout (`MCP_PGVECTOR_COMMAND_TIMEOUT_SECONDS`, default
  30s) so one expensive query can't occupy a pool connection — and stall every other caller —
  indefinitely. Set it to `0` to disable.

## Installation

```bash
uvx mcp-server-pgvector
```

Or with pip:

```bash
pip install mcp-server-pgvector
python -m mcp_server_pgvector
```

## Configuration

The server reads its connection string from `DATABASE_URL` (or `PGVECTOR_DATABASE_URL`):

```json
{
  "mcpServers": {
    "pgvector": {
      "command": "uvx",
      "args": ["mcp-server-pgvector"],
      "env": {
        "DATABASE_URL": "postgresql://user:password@localhost:5432/mydb",
        "MCP_PGVECTOR_READ_ONLY": "false",
        "MCP_PGVECTOR_COMMAND_TIMEOUT_SECONDS": "30"
      }
    }
  }
}
```

## Production readiness

**Covered:**
- Identifier-safe SQL (every table/column checked against `pg_catalog` before use) and a closed
  filter-operator allowlist — no path from tool arguments to raw SQL.
- Per-query timeout, so one runaway query can't monopolize the (small, 5-connection) pool.
- 60+ tests, including dimension-mismatch and injection-attempt regressions, run in CI on every
  push/PR against a real pgvector container across Python 3.10–3.13. A separate CI job builds the
  package and runs `twine check` on the result.
- Connection failures surface as plain `ConnectionRefusedError`/`asyncpg` exceptions — verified
  these don't leak the DSN's credentials into error text.

**Known limitations, honestly:**
- No per-tool authorization — access control is whatever the Postgres role in `DATABASE_URL` can
  do. If you need different agents to have different permissions, give them different
  connection strings backed by different Postgres roles, not different server instances of this
  same DSN.
- `hybrid_search`'s full-text side is hardcoded to Postgres's `'english'` text search
  configuration; there's no parameter to change it yet.
- No structured logging — failures are exceptions surfaced through the MCP error channel, not
  written to a log you can tail. Fine for a single-user desktop MCP client, a real gap if you're
  running this as a shared service.
- The connection pool is fixed at 1–5 connections and isn't configurable via environment variable
  yet.

## Development

```bash
uv sync --dev

# Bring up an isolated pgvector instance for local testing
docker compose -f docker-compose.dev.yml up -d

export DATABASE_URL=postgresql://postgres:postgres@localhost:5434/postgres
uv run pytest

uv run ruff check .
uv run pyright
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md). See [CHANGELOG.md](CHANGELOG.md) for release history.

## License

MIT — see [LICENSE](LICENSE).
