wandering-rag-mcp

wandering-rag-mcp

A local RAG knowledge base MCP server that exposes semantic document search as tools using zvec for vector storage and Qwen3-Embedding for text embedding.

Category
访问服务器

README

English | 中文

wandering-rag-mcp

A local RAG (Retrieval-Augmented Generation) knowledge base MCP server that exposes semantic document search as tools. Uses zvec (Alibaba's embedded vector database) for vector storage and Qwen3-Embedding-0.6B for text embedding.

No external LLM required — the MCP server handles retrieval, and the client (QoderWork, Claude Desktop, etc.) provides generation.

Features

  • Multi-format support: Plain text files (40+ types: md, txt, py, js, ts, go, rs, etc.) and binary documents (PDF, DOCX, PPTX, XLSX)
  • Embedded vector DB: zvec — zero-config, no Docker, WAL-persistent, HNSW-indexed
  • Local embedding: Qwen3-Embedding-0.6B (0.6B params, 1024-dim, 32K context, bilingual CN/EN)
  • Optional reranker: bge-reranker-v2-m3 cross-encoder for higher retrieval accuracy
  • REST API: HTTP endpoints for document management (upload/search/delete), runs alongside MCP on the same port
  • Three transport modes: stdio, SSE, Streamable HTTP
  • Multi-collection: Isolate documents into separate knowledge bases

Quick Start

Prerequisites

  • Python >= 3.10

Install

git clone <repo-url>
cd wandering-rag-mcp
pip install -e .

Run

# stdio mode (default, for QoderWork / Claude Desktop)
python server.py

# SSE mode
python server.py --mode sse --port 8000

# Streamable HTTP mode
python server.py --mode streamable-http --host 0.0.0.0 --port 8000

# Disable REST API (MCP only)
python server.py --mode sse --no-api

Environment variables are also supported:

Variable Description Default
RAG_MCP_MODE Transport mode stdio
RAG_MCP_HOST Bind host 127.0.0.1
RAG_MCP_PORT Bind port 8000
RAG_EMBEDDING_MODEL Embedding model name Qwen/Qwen3-Embedding-0.6B
RAG_RERANKER_MODEL Reranker model name BAAI/bge-reranker-v2-m3
RAG_DATA_DIR Vector data directory ./data
RAG_CORS_ORIGINS Allowed CORS origins (comma-separated) *

Client Configuration

stdio Mode (QoderWork / Claude Desktop)

{
  "mcpServers": {
    "wandering-rag-mcp": {
      "command": "python",
      "args": ["D:\\repos\\rag-mcp\\server.py"]
    }
  }
}

SSE Mode

{
  "mcpServers": {
    "wandering-rag-mcp": {
      "url": "http://your-server:8000/sse"
    }
  }
}

Streamable HTTP Mode

{
  "mcpServers": {
    "wandering-rag-mcp": {
      "url": "http://your-server:8000/mcp"
    }
  }
}

MCP Tools

search

Search the knowledge base with natural language queries.

Parameter Type Default Description
query string (required) Natural language search query
top_k int 5 Number of results to return
collection string "default" Collection to search
rerank bool false Use cross-encoder reranker for higher accuracy
filter string "" Glob pattern to filter by source file (e.g. *.md, **/docs/*)
expand_context int 0 Number of neighboring chunks to include before/after each result for broader context

ingest_file

Import a single file into the knowledge base.

Parameter Type Default Description
filepath string (required) Path to the file
collection string "default" Target collection
chunk_size int 500 Max characters per chunk
force bool false Re-import even if file hasn't changed
chunk_mode string "recursive" Chunking strategy: recursive (character-based splitting), semantic (embedding similarity-based splitting), or structural (document structure-aware splitting by headings, code blocks, tables)

Change detection: By default, files that haven't changed since last import are skipped. Use force=true to re-import anyway.

Supported formats: .md, .txt, .py, .js, .ts, .pdf, .docx, .pptx, .xlsx, and 40+ more.

ingest_directory

Batch import all files in a directory.

Parameter Type Default Description
dirpath string (required) Directory path
collection string "default" Target collection
recursive bool true Scan subdirectories
extensions string "" Comma-separated extensions filter (empty = all supported)
chunk_size int 500 Max characters per chunk
force bool false Re-import even if files haven't changed
chunk_mode string "recursive" Chunking strategy: recursive, semantic, or structural

list_collections

List all knowledge base collections.

list_documents

List all documents in a collection.

Parameter Type Default Description
collection string "default" Collection name

delete_document

Remove a document and all its chunks from the knowledge base.

Parameter Type Default Description
filepath string (required) Path used during import
collection string "default" Collection name

delete_collection

Delete an entire knowledge base collection and all its documents, vectors, and configuration. This cannot be undone.

Parameter Type Default Description
collection string "default" Collection name to delete

configure_collection

Set default parameters for a knowledge base collection. Future import and search operations will use these defaults when parameters are not explicitly specified.

Parameter Type Default Description
collection string "default" Collection name
chunk_mode string "" Default chunking strategy. Empty = keep current. recursive, semantic, or structural
chunk_size int 0 Default max characters per chunk. 0 = keep current
chunk_overlap int -1 Default overlap characters. -1 = keep current
rerank bool None Default whether to use reranker for search. None = keep current
description string None Collection description. None = keep current

get_collection_config

View the current configuration for a collection.

Parameter Type Default Description
collection string "default" Collection name

REST API

When running in SSE or Streamable HTTP mode, a REST API is automatically available at /api/ alongside the MCP endpoint. This enables web frontends (e.g., CodingHub) to manage documents via HTTP while AI clients use MCP for search.

Disable with --no-api if you only need MCP.

GET /api/health

Health check endpoint.

GET /api/collections

List all knowledge base collections.

Response:

[{"name": "default", "doc_count": 5}]

GET /api/collections/{name}/documents

List all documents in a collection.

Response:

[{"source": "/path/to/file.md", "chunk_count": 12}]

POST /api/collections/{name}/documents

Upload a file to the knowledge base. Accepts multipart/form-data with a file field.

curl -F "file=@document.pdf" http://localhost:8000/api/collections/default/documents

Optional query parameters: chunk_size (default: 500), chunk_mode (recursive, semantic, or structural, default: recursive).

Response:

{"status": "ok", "filename": "document.pdf", "chunks": 24}

DELETE /api/collections/{name}/documents

Delete a document and all its chunks.

curl -X DELETE http://localhost:8000/api/collections/default/documents \
  -H "Content-Type: application/json" \
  -d '{"filepath": "/path/to/file.md"}'

Response:

{"status": "ok", "filepath": "/path/to/file.md", "deleted": 12}

DELETE /api/collections/{name}

Delete an entire collection and all its data.

curl -X DELETE http://localhost:8000/api/collections/my_collection

Response:

{"status": "ok", "collection": "my_collection", "deleted": true}

POST /api/collections/{name}/search

Semantic search across the knowledge base.

curl -X POST http://localhost:8000/api/collections/default/search \
  -H "Content-Type: application/json" \
  -d '{"query": "how to install", "top_k": 5, "rerank": false, "filter": "*.md", "expand_context": 1}'

Request body:

Field Type Default Description
query string (required) Search query
top_k int 5 Number of results
rerank bool false Use cross-encoder reranker
filter string "" Glob pattern to filter by source file path
expand_context int 0 Number of neighboring chunks to include before/after each result

Response:

[
  {"id": "...", "score": 0.85, "text": "...", "source": "file.md", "chunk_index": 3}
]

GET /api/collections/{name}/config

Get the configuration for a collection.

Response:

{"chunk_mode": "semantic", "chunk_size": 500, "chunk_overlap": 50, "rerank": false, "description": "Technical docs"}

PUT /api/collections/{name}/config

Update collection configuration. Only include fields you want to change.

curl -X PUT http://localhost:8000/api/collections/default/config \
  -H "Content-Type: application/json" \
  -d '{"chunk_mode": "semantic", "description": "Technical documentation"}'

Response: Returns the full updated configuration.

CORS

The REST API includes CORS headers by default (allows all origins). Restrict with the RAG_CORS_ORIGINS environment variable:

RAG_CORS_ORIGINS=http://localhost:5173,http://localhost:8080 python server.py --mode sse

Architecture

flowchart TB
    subgraph Client["MCP Client (QoderWork, etc.)"]
        direction LR
        C1["User question"] --> C2["Call MCP tools"] --> C3["LLM answer"]
    end

    Client <-->|"stdio / SSE / Streamable HTTP"| Server

    subgraph Server["RAG MCP Server (FastMCP)"]
        direction LR
        subgraph Tools[" "]
            direction TB
            T1["Ingest Pipeline"] ~~~ T2["Search Pipeline"] ~~~ T3["Collection Manager"]
        end
        Tools --> Embed & Rerank & Vec
        Embed["sentence-transformers<br/>Qwen3-Embedding-0.6B"]
        Rerank["Cross-Encoder<br/>bge-reranker-v2-m3"]
        Vec["zvec<br/>./data/"]
    end

    style Client fill:#e8f4f8,stroke:#2196F3
    style Server fill:#f5f5f5,stroke:#333
    style Tools fill:#fff3e0,stroke:#FF9800
    style Embed fill:#fce4ec,stroke:#E91E63
    style Rerank fill:#e8eaf6,stroke:#3F51B5
    style Vec fill:#f3e5f5,stroke:#9C27B0

Project Structure

wandering-rag-mcp/
├── pyproject.toml          # Dependencies and entry point
├── server.py               # MCP server entry + 6 tool definitions + combined ASGI
├── api/
│   ├── __init__.py
│   └── app.py              # REST API routes (starlette)
├── core/
│   ├── chunker.py          # Text chunking (recursive + semantic)
│   ├── embeddings.py       # sentence-transformers wrapper (lazy load)
│   ├── reranker.py         # Cross-encoder reranker (lazy load)
│   ├── service.py          # Shared business logic (MCP + REST)
│   └── vector_store.py     # zvec wrapper (CRUD + search)
├── data/                   # zvec storage (auto-created at runtime)
│   └── default/
└── .gitignore

How It Works

  1. Ingest: File is read (plain text or converted via markitdown) → split into overlapping chunks → each chunk embedded into a 1024-dim vector → stored in zvec with metadata (text, source path, chunk index)

  2. Search: Query text → embedded into vector → zvec ANN search returns top-k nearest chunks with similarity scores → optionally reranked by cross-encoder for higher accuracy → returned as formatted text with source references

  3. Document ID: SHA256 hash of the file path (first 16 chars) is used as a stable document ID, enabling idempotent re-imports and deletion by file path.

Dependencies

Package Purpose
mcp MCP protocol SDK (FastMCP)
zvec Embedded vector database by Alibaba
sentence-transformers Load and run embedding models
markitdown[all] Convert PDF/DOCX/PPTX/XLSX to Markdown
python-multipart Multipart form parsing for REST API file uploads

Technical Documentation

For detailed architecture and technical stack explanation, see Architecture Document.

Deployment

Quick Install (Online)

For a clean Linux server with internet access:

curl -sSL https://raw.githubusercontent.com/mambo-wang/wandering-rag-mcp/main/deploy/setup.sh | bash

This installs everything: Python venv, dependencies, embedding model, and generates start scripts.

Offline Install

For air-gapped servers, use the offline packaging scripts in deploy/:

# On a machine with internet: prepare the bundle (~3GB with models)
cd deploy && bash prepare.sh x86_64

# Transfer wandering-rag-mcp-offline.tar.gz to the target server, then:
tar xzf wandering-rag-mcp-offline.tar.gz
cd bundle && bash install.sh

See deploy/README.md for full deployment guide.

Roadmap

The following improvements are planned for future releases:

  • Hybrid search: Combine BM25 keyword retrieval with semantic search using Reciprocal Rank Fusion (RRF) for better precision on exact-match queries (function names, error codes, technical terms)
  • SQLite metadata layer: Replace _registry.json with SQLite for document metadata, enabling server-side metadata filtering (WHERE clauses) and reliable batch deletion instead of the current ID-probing approach
  • Evaluation framework: Built-in recall@k and MRR benchmarks with a CLI evaluation script, enabling quantitative measurement of retrieval quality when tuning chunking strategies or swapping models
  • Token-based chunk sizing: Replace character-based chunk_size with token-based sizing for consistent chunk lengths across different languages (CJK vs. Latin scripts)
  • Embedding batch control: Configurable batch size for encode() to prevent memory spikes when ingesting large documents with hundreds of chunks
  • Concurrent access safety: File-level locking for _registry.json and thread-safe VectorStore operations to prevent corruption under concurrent REST API requests

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选