RAGSync MCP Server
Lập chỉ mục tài liệu, tệp tin và trang web của bạn vào kho vector, cung cấp cho tác nhân AI khả năng tìm kiếm ngữ nghĩa trực tiếp với tính năng tự động đồng bộ khi có thay đổi, không cần viết code.
Tài liệu
RAGSync
Configuration-driven RAG MCP server — ingest, watch, and search arbitrary knowledge sources behind a stable tool surface.
Features
- Broad source support — index local folders (text, PDF, Markdown) and web pages; not limited to a single file type or format
- Config-driven — one YAML file defines sources, chunking strategy, embedding model, and vector store; no code required
- Live reload — filesystem watching and polling keep the index current as sources change; editing the config itself applies changes without a restart
- Flexible embeddings — local
fastembedworks out of the box with no API key; swap in OpenAI or Voyage per source - Stable MCP tool surface — five source-agnostic tools (
search,list_sources,get_document,get_index_status,reindex) that never change as sources are added
Installation
The fastest way is with uvx — no clone or install step:
{
"mcpServers": {
"ragsync": {
"command": "uvx",
"args": ["ragsync", "--config", "/abs/path/to/config.yaml"]
}
}
}
Add this to your MCP client config:
| Client | Config file |
|---|---|
| Cursor | .cursor/mcp.json (project) or ~/.cursor/mcp.json (global) |
| Claude Desktop | claude_desktop_config.json |
| Claude Code | .mcp.json (or claude mcp add) |
| Windsurf / others | their mcpServers config |
Paths inside the config are resolved against the config file's directory (not the client's working directory), so a config can live in the repo and reference repo content with relative paths like
path: ./docs. Give--configitself an absolute path, though — the client chooses where it launches the server from, so that's the one path it must be able to find unambiguously.
Pin a version with "[email protected]" if you want reproducible launches. (If the
client can't find uvx on its PATH, use the absolute path to the uvx binary
— which uvx.)
Other install options
Option B — run in place with uv (no install, from a clone)
uv run --directory runs the server from the cloned repo without installing it:
{
"mcpServers": {
"ragsync": {
"command": "uv",
"args": [
"run",
"--directory",
"/abs/path/to/ragsync-mcp",
"ragsync",
"--config",
"/abs/path/to/ragsync-mcp/examples/config.example.yaml"
]
}
}
}
Option C — install the CLI globally
uv tool install ragsync # from PyPI; or a local path to a clone
{
"mcpServers": {
"ragsync": {
"command": "ragsync",
"args": ["--config", "/abs/path/to/config.yaml"]
}
}
}
(Equivalently, "command": "python", "args": ["-m", "ragsync_mcp", "--config", "…"]
if the package is installed in the active environment.)
Hosted embedding keys
For openai/voyage sources, the config names an env var (api_key_env) rather
than the key itself. Provide that variable to the subprocess via env:
{
"mcpServers": {
"ragsync": {
"command": "ragsync",
"args": ["--config", "/abs/path/to/config.yaml"],
"env": { "OPENAI_API_KEY": "sk-..." }
}
}
}
After saving, restart/reload the client. It will list the five tools (search,
list_sources, get_document, get_index_status, reindex); the agent calls
search to answer questions from your indexed sources. First launch downloads
the local embedding model, so initial startup can take a little longer.
Configuration
A single YAML file defines global defaults and a list of sources. Each
source becomes one searchable collection with its own loader, chunking,
embedding model, vector-store collection, and watcher. Per-source isolation lets
different sources use different embedding models safely.
defaults:
chunking:
strategy: recursive_character
chunk_size: 800
chunk_overlap: 100
embedding:
provider: fastembed
model: BAAI/bge-small-en-v1.5 }
vector_store:
backend: chroma
persist_directory: ./vector_db
sources:
- name: product-docs
type: folder
description: Product documentation and how-to guides.
connection:
path: ./docs # relative to the config file's directory
include: ["**/*.md"]
exclude: ["**/internal/**"]
watch:
enabled: true
mode: filesystem
chunking:
strategy: markdown
chunk_size: 1000
chunk_overlap: 150
vector_store:
collection: product_docs
metadata:
product: example
audience: public
The examples/ directory has runnable configs:
config.example.yaml— a complete multi-source example (pointed at the sample content underexamples/docsandexamples/playbooks).folder.yaml— a singlefoldersource.website.yaml— a singlewebsitesource.
Source types
| type | description | watch modes |
|---|---|---|
folder | local/mounted directory of files (text, PDF) | filesystem, poll |
website | fixed list of web pages (fetched, not crawled) | poll |
Include/exclude globs use gitignore-style matching (e.g. **/internal/**).
Embedding providers
fastembed (local, default), openai, and voyage (hosted). Hosted providers
read their API key from the environment variable named by api_key_env — keys
are never written into config.
MCP tools
Five tools, deliberately small and source-agnostic. They never change as sources are added:
search— semantic search across one or all sources, with optional metadata filtering. Returns results with normalized[0, 1]scores.list_sources— discover available sources and their health/metadata.get_document— fetch a full document aftersearchsurfaces a chunk.get_index_status— indexing freshness/health for one source or all.reindex— force a full re-scan of a source.
Tools return structured {"error": "..."} objects rather than raising, so the
calling agent can recover conversationally.
Access scoping
Per-source isolation is a security boundary: scope access by running separate server instances with separate configs. There is no cross-instance "search everything" path.
Development
uv sync --extra dev # install test dependencies
uv run pytest
Tests run fully offline by injecting a deterministic embedder in place of
fastembed (see tests/conftest.py). The architecture and extension contract —
how to add a new source type — are documented in AGENTS.md.
Releasing
Releases are automated from Conventional Commits.
CI (.github/workflows/ci.yml) runs the test suite on every pull request. On
merge to main, the release workflow (.github/workflows/release.yml) runs the
tests again, then python-semantic-release
inspects the commits since the last tag and decides the next version:
| Commit type | Example | Version bump |
|---|---|---|
fix: | fix: handle empty PDF pages | patch — 0.1.0 → 0.1.1 |
feat: | feat: add notion loader | minor — 0.1.0 → 0.2.0 |
feat!: / BREAKING CHANGE: | feat!: drop python 3.9 | major — 0.1.0 → 1.0.0 |
docs: / chore: / test: / ci: / refactor: | — | no release |
When there is a releasable change it bumps version in pyproject.toml, updates
CHANGELOG.md, tags the commit, creates a GitHub release, and publishes the
package to PyPI. Once published, anyone can run it with
uvx ragsync --config <path> (or pip install ragsync).