RAGSync MCP Server
将文档、文件和网站索引到向量存储中,为AI智能体提供实时语义搜索,并在内容变更时自动同步,无需编写代码。
文档
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).