Citewell
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Citewell
Citation-grounded document memory for AI assistants. Offline · MCP-native · Turkish-ready
Türkçe README · Documentation · Architecture decisions
Citewell is a headless RAG server that gives Claude Desktop, Claude Code, Cursor and any other MCP client access to your documents — and every passage it returns comes with file, page, chunk id and a verbatim quote, so answers can be checked.
It has no LLM of its own and needs no API key. It retrieves; your assistant writes the answer.
You ▸ Ankara'da otelde bir gece için en fazla ne kadar harcayabilirim?
Claude ▸ (calls citewell.search_documents)
Ankara, İstanbul ve İzmir'de gecelik konaklama üst sınırı 3.500 TL'dir [1].
Kaynaklar
[1] calisan-el-kitabi.pdf, s. 4 — "İş seyahatlerinde konaklama için gecelik üst sınır
İstanbul, Ankara ve İzmir'de 3.500 TL, diğer şehirlerde 2.500 TL'dir."Why Citewell?
Cited, verifiable passages — page-aware chunking that never crosses page boundaries, verbatim quotes, stable chunk ids (ADR-0006).
MCP-native — stdio for desktop clients, Streamable HTTP at
/mcp, structured tool output, resources and a citation prompt. Read-only by design (ADR-0007).Hybrid search — pgvector (HNSW) + PostgreSQL full-text search fused with Reciprocal Rank Fusion, optional
bge-reranker-v2-m3cross-encoder (ADR-0004).Turkish first — Turkish stemming (
tsvectorturkishconfig), correctI/ı–İ/icasing, cp1254 decoding, multilingualbge-m3embeddings, a Turkish eval set.Offline & private — local models; after a one-time download nothing leaves your machine.
Measured — built-in eval: Recall@k, MRR, hit rate, page hit rate, p50/p95 latency.
One command —
docker compose up. PostgreSQL is the only dependency.
Related MCP server: loci-mcp
Quickstart (Docker)
git clone https://github.com/omerozkilic/citewell && cd citewell
docker compose up -d # PostgreSQL + Citewell on localhost:8000
cp -r ~/Documents/handbooks/* data/ # PDF, Markdown, TXT, HTML, DOCX
docker compose run --rm citewell models download # once (~2–3 GB), needs internet
docker compose run --rm citewell ingest /data
docker compose run --rm citewell search "yıllık izin kaç gün?"REST API & OpenAPI docs: http://localhost:8000/docs
MCP endpoint (Streamable HTTP): http://localhost:8000/mcp
No documents at hand? Use the bundled sample corpus (a fictional Turkish company handbook):
docker compose run --rm -v ./examples:/examples:ro citewell ingest /examples/corpus
docker compose run --rm citewell search "Ankara'da otelde bir gece için en fazla ne kadar harcayabilirim?"A database is tied to the embedding model it was initialised with (
citewell doctorshows it). To switch models, use a fresh database volume and re-ingest.
Quickstart (local, uv)
uv sync --extra models
docker compose up -d db
uv run citewell init
uv run citewell ingest ./my-docs --collection handbook
uv run citewell search "parola en az kaç karakter olmalı?" -c handbookConnect your assistant
# HTTP (server running via docker compose or `citewell serve`)
claude mcp add --transport http citewell http://localhost:8000/mcp
# or stdio, from a local checkout
claude mcp add citewell -- uv --directory /path/to/citewell run citewell mcp{
"mcpServers": {
"citewell": {
"command": "docker",
"args": ["exec", "-i", "citewell-citewell-1", "citewell", "mcp"]
}
}
}Local checkout instead of Docker:
{
"mcpServers": {
"citewell": {
"command": "uv",
"args": ["--directory", "/path/to/citewell", "run", "citewell", "mcp"],
"env": { "CITEWELL_DATABASE_URL": "postgresql://citewell:citewell@localhost:5432/citewell" }
}
}
}{ "mcpServers": { "citewell": { "url": "http://localhost:8000/mcp" } } }MCP surface
Kind | Name | Purpose |
tool |
| Cited passages ( |
tool |
| A chunk plus neighbouring chunks, to read around a quote |
tool |
| What is indexed |
resource |
| Same data as resources |
prompt |
| "Answer only from passages, cite with [n]" |
CLI
citewell init create extension + schema (idempotent)
citewell ingest PATH... [-c COL] [-l turkish|english|simple] [--force]
citewell search "query" [-k 5] [-m hybrid|vector|keyword] [-c COL|*] [--rerank] [--json]
citewell serve [--init] [--host] [--port] REST API + MCP at /mcp
citewell mcp [--transport stdio|http] MCP server only
citewell eval DATASET.jsonl [-k 5] [-r vector,keyword,hybrid,hybrid+rerank] [-o report.json]
citewell collections list|create|delete · documents list|delete · models download · doctorREST API
curl -s localhost:8000/v1/search -H 'content-type: application/json' \
-d '{"query": "uzaktan çalışma kaç gün?", "top_k": 3, "mode": "hybrid"}'
curl -s -F file=@politika.pdf -F collection=handbook localhost:8000/v1/documentsGET /health · POST|GET /v1/search · GET /v1/chunks/{id}?context=1 · GET|POST|DELETE /v1/collections ·
GET|POST|DELETE /v1/documents — full schema at /docs. Set CITEWELL_API_TOKEN to require a bearer token.
How it works
flowchart LR
subgraph Ingest
F[PDF / MD / TXT / HTML / DOCX] --> L[Loaders<br/>pages & sections]
L --> C[Page-aware chunker<br/>verbatim slices]
C --> E[bge-m3 embeddings<br/>+ title/section header]
end
E --> PG[(PostgreSQL<br/>pgvector HNSW<br/>tsvector GIN)]
subgraph Query
Q[query] --> V[vector top-40]
Q --> K[keyword top-40<br/>turkish stemmer]
V --> R[RRF k=60]
K --> R
R --> X[optional rerank<br/>bge-reranker-v2-m3]
X --> O[passages + citations<br/>+ best quote]
end
PG --- V
PG --- K
O --> M[MCP · REST · CLI]Evaluation
citewell eval examples/eval/tr-sample.jsonl -k 5 -r vector,keyword,hybrid,hybrid+rerank --markdownDatasets are JSON Lines: {"id", "query", "relevant": [{"source", "page"?}], "collection"?}.
Metrics: Recall@k, MRR@k, Hit@k, Page hit@k (exact page), p50/p95 latency.
CI runs the sample set end-to-end on every push (with the lexical test backends) and fails below a
recall floor; see docs/evaluation.md.
First results: Turkish Labour Law (İş Kanunu, 4857)
20 natural-language questions from examples/eval/mevzuat-questions.jsonl
against the official PDF from mevzuat.gov.tr (121 articles). A hit counts only if it lands on a page
of the right article. BAAI/bge-m3 + bge-reranker-v2-m3, default chunking, Apple Silicon (MPS),
models already loaded:
Run | Recall@5 | MRR@5 | Hit@5 | Page hit@5 | p50 ms | p95 ms |
vector | 1.000 | 0.885 | 1.000 | 1.000 | 45 | 95 |
keyword | 0.850 | 0.614 | 0.850 | 0.850 | 8 | 11 |
hybrid (default) | 1.000 | 0.893 | 1.000 | 1.000 | 48 | 54 |
hybrid + rerank | 1.000 | 0.950 | 1.000 | 1.000 | 3259 | 3771 |
Every question finds the right article and page in the top 5 with hybrid search; the right passage is usually ranked first or second (MRR 0.89).
Dense retrieval does the heavy lifting on paraphrased questions; keyword search alone misses 15 %.
The cross-encoder lifts MRR to 0.95 but costs ~3 s per query on a laptop, so it is opt-in (
--rerank/rerank: true).
Small and single-domain (20 questions, one law, written by the maintainers) — a first data point, not a leaderboard. Reproduce it with docs/evaluation.md; larger independent Turkish eval sets are very welcome.
Configuration
All settings are environment variables prefixed with CITEWELL_ (or a .env file) — see
.env.example and docs/configuration.md.
Variable | Default |
|
|
|
|
|
|
|
|
|
|
| unset (no auth; bind to localhost) |
Roadmap
v0.1 — ingest (PDF/MD/TXT/HTML/DOCX), vector search, cited passages, CLI, REST, MCP, Docker
v0.2 — hybrid search (pgvector + tsvector, RRF), cross-encoder rerank, Turkish eval harness
v0.3 — MCP: completions, progress notifications, resource subscriptions; per-collection access, collection-scoped tokens (collections, resources and prompts already ship)
Later — OpenAI-compatible endpoint (Open WebUI), OCR for scanned PDFs, sparse/BM25 retriever, larger public Turkish benchmark, SQLite single-file mode
Contributing
See CONTRIBUTING.md. Security reports: SECURITY.md. Architecture decisions live in docs/adr.
License
MIT © Ömer Özkılıç
This server cannot be deployed
Maintenance
Related MCP Connectors
Your personal library, readable by AI agents: durable snapshots, hybrid search, 46 MCP tools.
Query your own PDFs and documents from any MCP client. Every answer cites the page it came from.
Self-hosted AI workspace: semantic RAG document search, long-term memory, and AI chat via MCP.
Agentic search over your Dewey document collections from any MCP-compatible client.
Related MCP Servers
- FlicenseNot gradedqualityAmaintenanceEnables AI agents and MCP-compatible hosts to search, ask, and retrieve section-level citations from scattered local notes, docs, PDFs, Word files, web pages, org files, and chat logs using hybrid vector and BM25 retrieval.-
- FlicenseNot gradedqualityAmaintenanceEnables AI agents and MCP hosts to query a local, machine-resident index of scattered notes, project docs, PDFs, Office files, and chat exports using hybrid vector + BM25 retrieval with section-level citations. Exposes tools, resources, and prompts so hosts like Claude Desktop, Cursor, and Cline can search, ask, and explore linked knowledge through natural language.-
- FlicenseNot gradedqualityAmaintenanceEnables AI agents and MCP hosts to search and ask questions across a local hybrid-retrieval index of scattered notes, documents, PDFs, and chat logs, returning section-level citations and agent-usable tools, resources, and prompts.-
- FlicenseNot gradedqualityAmaintenanceEnables AI agents and MCP hosts to search and query a local hybrid vector/BM25 index of scattered notes, docs, PDFs, Office files, web pages, org files, and chat exports. It returns section-level citations so agents can ground answers in the user's own files.-