1. 项目概述为你的AI助手打造一个私有的、本地的知识大脑如果你正在使用Claude Desktop、Cursor或者本地运行的Ollama模型有没有想过为什么它们不能像ChatGPT那样“记住”你的个人文档或者实时搜索网页答案很简单它们缺少“工具”。这些AI助手本身是强大的语言模型但它们没有访问你本地文件系统或互联网的“手”和“眼睛”。这就是MCP模型上下文协议和RAG检索增强生成技术大显身手的地方。mcp-rag-server这个项目本质上是一个“工具提供者”。它通过MCP协议为任何兼容的AI客户端无论是云端Claude还是本地Ollama提供了三把关键的钥匙一把能对你的本地文档Markdown、TXT、PDF进行语义搜索一把能进行实时网页搜索还有一把能让你在运行时动态添加新文档。最核心的承诺是你的文档数据永远不会离开你的机器。所有嵌入向量Embeddings的计算都在本地完成使用Nomic v1.5模型无需向OpenAI或Cohere等云端API发送任何数据。想象一下这个场景你有一个存放了团队所有技术决策文档ADR、API接口说明、运维手册和新员工入职指南的文件夹。以前你需要手动打开文件搜索或者依赖不精确的全局文本搜索。现在你只需要在Claude Desktop里问一句“我们关于认证中间件的最终决策是什么”或者“API的速率限制是多少”AI助手就能立刻从你的私有知识库中找到最相关的片段并给出精准回答。对于本地运行的Ollama模型来说这更是雪中送炭让它瞬间拥有了文档检索和网页搜索这两项原本不具备的核心能力。这个项目非常适合开发者、技术团队负责人、独立研究者以及任何希望将AI助手深度集成到个人或团队工作流中同时又对数据隐私有严格要求的人。它不是一个庞大的企业级系统而是一个开箱即用、易于理解和扩展的“瑞士军刀”。2. 核心设计思路为什么“零仪式感”和“本地优先”如此重要市面上的RAG方案很多从LangChain、LlamaIndex这样的框架到各种商业化的企业解决方案。mcp-rag-server选择了一条截然不同的路径其设计哲学可以概括为为个人和轻量级团队场景优化追求极简的启动成本和绝对的数据控制权。2.1 与主流方案的差异化对比为了更清晰地理解它的定位我们可以将其与几种常见方案进行对比特性维度mcp-rag-serverLangChain / LlamaIndex企业级RAG方案数据隐私绝对本地化嵌入模型在本地运行数据不出机器。取决于配置。你可以选择本地嵌入模型但默认教程和云服务往往引导你使用OpenAI等云端API。通常需要上传。数据需发送至厂商的云端进行处理和存储。启动流程零仪式感。文档放入data/文件夹启动服务器即刻可用。无需单独的“摄取”或“索引”命令。多步骤流程。通常需要编写单独的摄取脚本明确调用索引构建流程步骤分离。复杂配置。涉及系统部署、管道配置、定时任务等启动成本高。部署依赖无需Docker默认。使用本地文件存储的Qdrant开箱即用。Docker仅用于进阶的服务器模式。视情况而定。向量数据库如Chroma可能以客户端库形式运行也可能需要单独服务。通常强制Docker/K8s。作为微服务架构的一部分部署复杂。协议兼容原生MCP。专为Claude、Cursor、Continue.dev等现代AI客户端设计无缝集成。无原生支持。需要通过额外适配层或自定义代码来对接MCP客户端。通常无。提供自有API或SDK与AI客户端集成需要开发工作。本地LLM支持核心场景。为Ollama等“裸”模型提供工具能力是其重要价值。支持但非核心。框架支持多种模型但本地化部署和工具暴露需要更多配置。罕见。主要面向云端大模型或企业内私有化部署的大模型。代码复杂度~640行Python。两个核心文件逻辑清晰半小时可通读。数千至上万行框架本身。概念繁多学习曲线陡峭。不适用。闭源或高度定制化。这个对比揭示了一个关键洞察mcp-rag-server不是为了解决“如何构建RAG系统”这个通用问题而是为了解决“如何以最省心的方式让我正在用的AI助手立刻能查询我的私人文档”这个具体痛点。2.2 “智能工具路由”的设计巧思另一个精妙的设计是“智能工具路由”。服务器通过MCP协议向AI客户端发送指令建议其遵循“先知识库后网页”的查询策略。但这只是“建议”最终决定权在AI客户端LLM手中。如何确保这个策略被执行呢项目通过两个层面来保障服务器层面的清晰信号当knowledge_base_search工具在知识库中未找到相关性分数超过阈值默认0.66的内容时它会返回明确的提示信息如“I couldnt find a relevant answer in the knowledge base”。这个清晰的信号被设计成易于AI识别从而触发其下一步的决策。客户端层面的强制指令项目文档强烈建议用户在各自的AI客户端中配置自定义指令。例如在Claude Desktop的项目说明中添加“Always use the rag-knowledge-base tools to search my documents before answering from your own knowledge.” 这相当于给AI加了一条“硬性规定”。这种“软建议硬指令”的组合既保持了MCP协议的灵活性又通过最佳实践引导用户实现了可靠的查询流程。对于本地LLM如通过Ollama调用而言这个设计至关重要因为web_search工具是它们获取实时信息的唯一途径而knowledge_base_search是查询私有知识的唯一途径。服务器一站式提供了两者。2.3 技术栈选型的深层考量为什么选择Nomic v1.5嵌入模型这并非随意选择。Nomic v1.5是一个非对称嵌入模型。这意味着它对“文档”和“查询”的处理方式是不同的。在生成文档嵌入向量时它会为文本加上search_document:前缀而在生成查询嵌入向量时则会加上search_query:前缀。这种区分让模型能更好地理解检索任务中“被找的东西”和“找东西的请求”之间的差异从而在MTEB等标准基准测试中取得更优的检索效果。此外它完全本地运行模型文件约550MB只需下载一次并缓存此后所有计算离线完成是隐私和离线能力的完美结合。为什么选择Qdrant作为向量数据库Qdrant提供了极佳的灵活性。它既可以作为一个轻量级的、嵌入式的数据库运行local模式数据存储在本地./qdrant_data/目录也可以作为一个独立的服务运行server模式。mcp-rag-server默认使用嵌入式模式这意味着用户无需安装和运维任何额外的数据库服务真正做到了开箱即用。当未来数据量增长或需要多客户端共享时又可以无缝切换到服务器模式无需重写任何业务代码。这种“从小规模开始平滑扩展”的能力非常适合项目演进。为什么采用基于段落边界的分块策略很多简单的RAG实现采用固定字符数如500字符的滑动窗口进行分块这很容易在句子或段落中间切断破坏语义的完整性。mcp-rag-server默认采用基于双换行符\n\n的段落边界分块。它首先尝试按段落分割只有当段落超过设定的chunk_size默认500字符时才会在句子边界进行二次分割。这种方法最大程度地保证了每个文本块在语义上的自洽性为后续的精准检索打下了坚实基础。3. 从零到一的详细部署与配置指南理解了设计理念后让我们动手将它运行起来。整个过程力求清晰我会补充一些官方文档中未提及的细节和避坑点。3.1 基础环境准备与项目初始化首先确保你的系统已安装Python 3.10或更高版本。你可以通过python3 --version来检查。# 1. 克隆项目仓库 git clone https://github.com/agarwalvishal/mcp-rag-server.git cd mcp-rag-server # 2. 创建并激活虚拟环境强烈推荐避免污染系统Python环境 python3 -m venv venv # 激活虚拟环境 # 在 macOS/Linux 上 source venv/bin/activate # 在 Windows 上 # venv\Scripts\activate # 3. 安装依赖 pip install -r requirements.txt注意第一次运行pip install时它会下载包括sentence-transformers、pymupdf4llm、qdrant-client等在内的多个包。其中sentence-transformers会自动处理Nomic模型的下载。如果网络环境不佳这一步可能会耗时较长或者需要配置镜像源。你可以通过设置环境变量HF_ENDPOINThttps://hf-mirror.com来使用Hugging Face镜像加速下载。3.2 准备你的知识库文档项目根目录下有一个data/文件夹。首次运行时里面包含一些示例文档来自一个虚构的初创公司用于演示功能。你可以直接使用它们进行测试更推荐的做法是替换成你自己的文档。支持的文件格式Markdown (.md)支持最好能利用#标题自动提取章节结构。纯文本 (.txt)按段落分块无额外元数据。PDF (.pdf)通过pymupdf4llm库解析能识别基于字体大小的标题结构并保留页码信息在搜索结果中能精确引用到第几页。操作很简单只需将你的.md,.txt,.pdf文件直接放入data/文件夹即可。服务器在启动时会自动扫描、解析、分块、嵌入并索引该目录下的所有支持文件。实操心得对于PDF文件特别是扫描版或复杂排版的PDF解析效果可能因库的版本而异。如果遇到提取文本乱码或标题识别不准的情况可以尝试先用其他工具如Adobe Acrobat、pdftotext命令行工具将PDF转换为文本或Markdown格式再放入data/目录通常能获得更稳定可靠的结果。3.3 连接到你常用的AI客户端这是最关键的一步让AI助手知道这个“工具服务器”的存在。配置的核心是提供一个JSON文件告诉客户端如何启动这个MCP服务器进程。3.3.1 配置 Claude DesktopClaude Desktop 是目前集成体验最好的客户端之一。找到配置文件位置macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.jsonLinux:~/.config/Claude/claude_desktop_config.json如果文件或目录不存在手动创建即可。编辑配置文件你需要将下面配置中的/absolute/path/to/mcp-rag-server替换为你电脑上项目的绝对路径。{ mcpServers: { rag-knowledge-base: { command: /absolute/path/to/mcp-rag-server/venv/bin/python, args: [/absolute/path/to/mcp-rag-server/mcp_server.py], env: { FIRECRAWL_API_KEY: your-firecrawl-api-key-here } } } }关键参数解释command: 指向你虚拟环境中Python解释器的绝对路径。args: 要执行的Python脚本即mcp_server.py。env: 环境变量。FIRECRAWL_API_KEY是用于web_search工具的如果你暂时不需要网页搜索功能可以省略整个env块。重启Claude Desktop保存配置文件后完全退出并重新启动Claude Desktop应用。验证连接启动后Claude Desktop会在后台启动你配置的MCP服务器。你可以在Claude的输入框里尝试问一个关于你文档的问题比如“我们项目的API速率限制是多少”。如果配置成功Claude会在回复时显示它使用了knowledge_base_search工具。避坑指南路径错误是最常见的问题。在macOS/Linux上你可以通过在终端中进入项目目录执行which python或realpath venv/bin/python来获取虚拟环境Python的绝对路径。在Windows上你需要获取类似C:\Users\YourName\Projects\mcp-rag-server\venv\Scripts\python.exe的路径。务必使用绝对路径相对路径会失效。3.3.2 配置 Cursor 或 VS Code with Continue.dev对于代码编辑器场景配置类似但配置文件的位置和名称不同。对于 Cursor在项目的根目录下创建或编辑文件.cursor/mcp.json{ mcpServers: { rag-knowledge-base: { command: /absolute/path/to/mcp-rag-server/venv/bin/python, args: [/absolute/path/to/mcp-rag-server/mcp_server.py] } } }对于 VS Code Continue.dev 插件编辑Continue的全局配置文件~/.continue/config.json{ models: [ { title: Ollama, provider: ollama, model: llama3.2 } ], mcpServers: [ { name: rag-knowledge-base, command: /absolute/path/to/mcp-rag-server/venv/bin/python, args: [/absolute/path/to/mcp-rag-server/mcp_server.py] } ] }这个配置尤其强大因为它为本地运行的Ollama模型赋予了文档搜索和网页搜索的能力。配置完成后在VS Code中你的本地模型就可以像云端模型一样利用工具来回答问题。3.3.3 强化客户端指令关键步骤为了让AI更稳定地优先使用你的知识库强烈建议在客户端添加一条自定义指令Claude Desktop: 在聊天设置或项目指令中添加“在回答问题时请优先使用‘rag-knowledge-base’工具搜索我的知识库。只有在知识库中没有找到相关信息时才使用你自己的知识或进行网页搜索。”Cursor: 在项目根目录创建或编辑.cursorrules文件加入上述指令。Continue.dev: 可以在~/.continue/config.json的contextProviders或模型配置中添加系统提示词。这条指令能有效引导AI的行为逻辑确保查询流程符合“先私密后公开”的原则。3.4 启动服务器与初次查询完成客户端配置后当你启动AI客户端如Claude Desktop它会自动在后台启动MCP服务器。你也可以手动启动服务器进行测试或调试# 在项目目录下确保虚拟环境已激活 python mcp_server.py你会看到类似以下的输出表明服务器已启动并成功加载了data/目录下的文档INFO:root:Starting MCP RAG server... INFO:knowledge_base:Loading documents from ./data INFO:knowledge_base:Loaded 5 documents (23 chunks) into collection knowledge_base INFO:root:Server started. Tools available: knowledge_base_search, web_search, ingest_document现在回到你的AI客户端尝试提出一些问题。如果使用的是示例文档可以问“我们选择了哪种认证方法” 应匹配ADR文档“开发环境设置步骤是什么” 应匹配入职指南“比较一下Redis和Memcached。” 应匹配研究笔记观察AI的回复它应该会显示调用了knowledge_base_search工具并引用了来源文档和相关性分数。4. 核心功能深度解析与高级用法4.1 知识库搜索语义理解如何工作knowledge_base_search工具是核心。它不仅仅是关键词匹配。当你提问“我们怎么验证用户身份”时即使用户文档里写的是“采用JWT令牌进行身份认证”没有出现“验证”这个词语义搜索也能找到它。背后的流程如下查询向量化你的问题被发送到服务器服务器使用Nomic v1.5模型以search_query:为前缀将其转换为一个768维的向量一串数字。向量相似度计算服务器在Qdrant向量数据库中计算这个查询向量与所有预先存储的文档块向量之间的余弦相似度。余弦相似度的值在-1到1之间越接近1表示越相似。结果筛选与排序数据库返回相似度最高的前k个结果k由search_top_k配置默认3。然后服务器会应用一个score_threshold默认0.66过滤掉相似度低于此阈值的结果。格式化返回最后将超过阈值的文本块连同其元数据来源文件、章节标题、页码、相似度分数一起返回给AI客户端。为什么阈值是0.66这个值不是随便定的。作者通过在示例文档集上进行大量查询测试绘制了相关查询和不相关查询的分数分布图。发现相关查询的分数大多集中在0.66-0.81之间而不相关查询则在0.48-0.67之间。0.66正好位于这个分界点附近能够有效过滤掉大部分噪音同时保留所有真正相关的信息。这个阈值可以通过config.yaml或环境变量MCPRAG_SEARCH_SCORE_THRESHOLD进行调整。4.2 网页搜索为本地LLM打开天窗web_search工具通过集成 Firecrawl 服务实现。Firecrawl是一个能将任何网页转换为LLM友好格式Markdown的API。这对于本地LLM如Ollama来说是革命性的因为它们本身不具备浏览网页的能力。配置步骤访问 Firecrawl 官网注册并获取API密钥。在启动MCP服务器时通过环境变量FIRECRAWL_API_KEY提供该密钥如前面Claude Desktop配置所示。当AI在知识库中找不到答案时它就会自动或在你的指令下调用web_search工具。AI会生成一个搜索查询例如“最新的Python 3.12特性”Firecrawl会执行搜索并抓取结果页面的主要内容以干净的结构化文本返回供AI消化并生成回答。注意事项Firecrawl是一项第三方服务有免费额度限制。频繁使用可能产生费用。此外网页搜索的质量和覆盖率取决于Firecrawl的能力。对于某些需要登录或JavaScript重度渲染的网站抓取效果可能不理想。4.3 运行时文档摄取动态更新知识库ingest_document工具允许你在不重启服务器的情况下临时添加单个文档到知识库中。这在某些场景下非常有用例如分析一个刚刚收到的PDF报告。使用方法是通过AI客户端直接“告诉”服务器去摄取一个文件请使用 ingest_document 工具摄取我桌面上的新报告/Users/YourName/Desktop/quarterly_report.pdf并给它起个标题叫“2024年Q1财报”。服务器会立即处理该文件将其分块、嵌入并添加到当前的向量数据库中随后即可被搜索到。重要限制临时性通过此工具添加的文档仅存在于当前服务器会话的内存中。一旦你停止MCP服务器这些临时添加的文档就会丢失。持久化方法如果你希望文档被永久索引唯一可靠的方法是将其复制到data/目录下然后重启服务器。服务器每次启动都会清空并重建整个索引基于data/目录的内容这保证了索引状态与文件系统状态的严格一致避免了“幽灵文档”问题。4.4 存储模式详解从单机到可扩展mcp-rag-server提供了三种存储模式适应不同阶段的需求模式启动命令/配置持久化适用场景local(默认)python mcp_server.py或qdrant_mode: local是数据保存在./qdrant_data/目录个人使用或小团队。无需任何外部服务数据持久化重启后索引仍在。memorypython mcp_server.py --in-memory否仅进程内存快速测试/CI环境。启动最快进程退出后所有数据消失。server需先启动Qdrant容器然后python mcp_server.py --qdrant-mode server --qdrant-url http://localhost:6333是数据在Qdrant容器内团队协作/生产环境。多个MCP服务器实例可以连接同一个Qdrant服务共享同一份知识库索引。切换到服务器模式的步骤确保已安装Docker。在项目目录下启动Qdrant服务docker compose up qdrant -d。这会根据项目内的docker-compose.yml文件启动一个Qdrant容器。以服务器模式启动MCP服务python mcp_server.py --qdrant-mode server --qdrant-url http://localhost:6333现在你可以启动多个MCP服务器进程甚至在不同的机器上只要网络能通它们都将连接到这个中央Qdrant实例操作同一份知识库。这对于需要高可用性或负载均衡的场景非常有用。5. 配置详解与性能调优所有配置都集中在config.yaml文件中并支持通过环境变量和命令行参数覆盖优先级从高到低为CLI参数 环境变量 config.yaml 默认值。# config.yaml 示例与详解 data_dir: ./data # 文档目录。你可以将其指向一个云盘同步文件夹如Dropbox实现多台电脑的知识库同步。 file_types: [md, txt, pdf] # 支持的文件后缀。可以添加其他后缀但需要确保有对应的加载器。 qdrant_mode: local # 存储模式local, memory, server qdrant_local_path: ./qdrant_data # local模式下的数据存储路径 qdrant_url: http://localhost:6333 # server模式下的Qdrant服务地址 collection_name: knowledge_base # Qdrant中的集合名。如果修改相当于创建了一个新的独立知识库。 embedding_model: nomic-ai/nomic-embed-text-v1.5 # 嵌入模型。可以替换为其他sentence-transformers支持的模型如all-MiniLM-L6-v2更小更快。 chunk_size: 500 # 目标块大小字符数。这是一个“软”限制分块器会优先保证段落完整。 chunk_overlap: 50 # 块与块之间的重叠字符数。防止一个句子被切分到两个块边缘导致语义丢失。 search_top_k: 3 # 每次搜索返回的最相似块数量。增加此值可以召回更多结果但可能会引入更多噪音。 search_score_threshold: 0.66 # 相关性分数阈值。根据你的文档和查询特点调整。如果发现漏掉相关结果可适当降低如0.6如果结果中噪音太多可提高如0.7。 log_level: INFO # 日志级别。调试时设为DEBUG可以看到更详细的分块、嵌入过程。通过环境变量覆盖配置示例# 在启动前设置环境变量 export MCPRAG_DATA_DIR/path/to/your/docs export MCPRAG_CHUNK_SIZE800 export MCPRAG_SEARCH_TOP_K5 # 然后启动服务器 python mcp_server.py性能与效果调优建议chunk_size与chunk_overlap这是影响RAG效果最重要的参数之一。对于技术文档、代码等密集文本500-800的块大小比较合适。对于叙事性、段落较长的内容可以增加到1000-1500。重叠部分通常设为块大小的10%-20%以确保上下文连贯。嵌入模型选择Nomic v1.5在质量和速度上取得了很好的平衡。如果你更追求速度并且文档主要是英文可以尝试all-MiniLM-L6-v2约80MB它体积小嵌入速度快。对于中文文档可以考虑BAAI/bge-small-zh-v1.5等针对中文优化的模型。更换模型只需修改embedding_model配置但注意首次运行需要下载新模型。处理大量文档默认配置适合数百个文档。如果文档数量达到数千启动时的嵌入和索引过程会变慢。此时可以考虑使用server模式将Qdrant部署在性能更强的机器上。编写一个简单的脚本定期如每天运行索引而不是每次启动都重建。但这需要修改服务器的启动逻辑目前版本每次启动都会重建。6. 常见问题排查与实战技巧在实际使用中你可能会遇到一些问题。以下是一些常见情况的排查思路和解决方法。6.1 客户端连接失败或工具不显示症状配置完成后AI客户端中看不到knowledge_base_search等工具或者提示连接错误。排查步骤检查路径这是最常见的问题。确保claude_desktop_config.json或.cursor/mcp.json中的command和args路径是绝对路径并且指向正确的虚拟环境Python和脚本。手动测试服务器打开终端激活虚拟环境手动运行python mcp_server.py。观察是否有错误输出。常见的错误包括ModuleNotFoundError依赖未安装。重新运行pip install -r requirements.txt。端口冲突或Qdrant启动失败检查6333端口是否被占用仅在server模式下有关。查看客户端日志Claude Desktop: 日志通常位于~/Library/Logs/Claude/(macOS) 或%APPDATA%\Claude\logs\(Windows)。Cursor/Continue: 查看编辑器内的输出面板或终端寻找MCP相关的错误信息。验证MCP连接可以使用MCP Inspector工具进行调试pnpx modelcontextprotocol/inspector python3 mcp_server.py。这是一个独立的工具可以列出服务器提供的所有工具并测试调用。6.2 搜索返回“未找到相关结果”症状提问后AI回复说在知识库里没找到答案但你确信文档里有相关内容。可能原因与解决阈值过高默认的search_score_threshold是0.66。尝试将其临时降低到0.5看看是否能搜到。如果能说明你的查询和文档的语义匹配度在边界上可能需要优化查询方式或调整分块策略。分块不当内容可能被切分在不合适的边界。检查你的文档格式。确保文档有清晰的结构使用Markdown标题。对于纯文本可以尝试在关键段落之间添加空行帮助分块器识别边界。查询表述问题尝试用文档中更可能出现的术语或同义词来提问。例如文档里写的是“部署流程”你问“怎么上线”可能就不如问“部署步骤是什么”效果好。文档未被加载检查服务器启动日志确认你的文档文件被成功加载并分块。日志会显示类似Loaded 5 documents (23 chunks)的信息。确保文件在data_dir指定的目录下并且后缀名是支持的.md,.txt,.pdf。6.3 网页搜索无法工作症状配置了FIRECRAWL_API_KEY但AI无法进行网页搜索或返回API错误。排查验证API密钥确保在环境变量或配置中设置的Firecrawl API密钥正确无误且未过期。检查网络连接服务器需要能访问Firecrawl的API端点。如果你的网络环境有特殊限制可能导致连接失败。查看额度登录Firecrawl控制台检查免费额度是否已用尽。服务器日志在启动服务器时设置log_level: DEBUG可以看到更详细的网络请求和错误信息。6.4 如何备份和迁移知识库知识库的核心是两部分原始的文档文件在data/目录和Qdrant向量数据库在qdrant_data/目录或Docker容器中。备份只需备份整个项目目录即可。如果你使用local模式qdrant_data/文件夹就在项目内。如果你使用Docker的server模式需要备份Qdrant容器的数据卷具体方法取决于你的Docker部署方式。迁移将整个项目文件夹复制到新机器确保Python版本和依赖一致然后启动即可。因为嵌入模型缓存在~/.cache/huggingface/和Qdrant数据都是可移植的。6.5 高级技巧自定义与扩展项目的简洁性两个主Python文件mcp_server.py和knowledge_base.py使其易于扩展。支持新文件格式如果你想支持Word文档.docx可以在knowledge_base.py的load_documents函数中参照现有的PDF加载器添加一个使用python-docx库的加载器。修改分块逻辑KnowledgeBase类中的_chunk_text方法控制了分块策略。你可以根据你的文档特点实现更复杂的分块逻辑例如按章节、按幻灯片等。添加新的工具MCP协议允许服务器暴露任意工具。你可以在mcp_server.py中参照现有工具的定义方式添加新的工具函数并通过mcp.tool()装饰器暴露它。例如你可以添加一个search_code工具专门用于搜索代码仓库。这个项目的魅力在于它不是一个黑盒。当你需要它做更多事情时你有完全的能力去改造它而这通常只需要修改几十行代码。