1. 为什么要在 Cursor 里搭一个会自己选工具的 RAG
MCP 是 Model Context Protocol,简单说就是让 Cursor 这类客户端用统一协议去调用外部工具;Agentic RAG 则是让模型自己判断该查向量库还是该联网搜索,而不是每次都走同一条固定链路。这套组合最适合两类人:一是手里已经有本地知识库、又经常遇到知识库覆盖不到的问题需要联网补全的开发者;二是想在 Cursor 里把「检索」做成可复用工具、不想每次手写召回逻辑的工程同学。
我这次要落地的场景很具体:在 Cursor 里通过 MCP 挂两个工具,一个查向量库(本地 Qdrant),一个走网络检索,然后用 TaoToken 统一 Key 和 API 通道,让两类工具背后的模型调用都走同一个入口。这样做的直接好处是 Key 不用散落在多个 .env 里,路由判定和调用日志也能集中看。整条链路的目标是:用户提问 → Cursor 里的模型判断走哪个工具 → 工具返回上下文 → 模型生成回答,并且这个判断过程可复现、可排查。
下面按「环境准备 → 配置骨架 → 工具注册 → 路由判定 → 验证请求 → 排错」的顺序走一遍,配置文件和代码都给到能直接改的程度。
2. TaoToken 前置:统一 Key 与 API 通道
TaoToken 在这里扮演的是统一入口:向量召回工具和网络检索工具在需要调用模型(比如做 query 改写、结果摘要、路由判定)时,都通过同一个 API 通道发出请求,Key 只维护一份。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。
你需要先拿到一个可用的 Key,然后把它写进环境变量,而不是硬编码进代码。推荐做法是在项目根目录建一个 .env:
# .env TAOTOKEN_API_KEY="sk-你的key" TAOTOKEN_BASE_URL="https://taotoken.net/api" QDRANT_URL="http://localhost:6333" BOCHAAI_API_KEY="你的网络检索key"Key 的创建入口在控制台的 API Keys 页面:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。如果你后面要长期跑编码类 Agent,可以顺带看下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。
注意:.env 一定要加进 .gitignore。我见过有人把 Key 提交到公开仓库,几分钟内就被刷爆额度。
3. 可复制配置:config.toml 与 settings.json 骨架
MCP 服务端和 Cursor 客户端各有一份配置。服务端用 config.toml 描述工具和模型通道,客户端用 settings.json(Cursor 的 MCP 配置)描述怎么启动这个服务。
先看服务端的 config.toml:
# config.toml [server] name = "mcp-rag-app" host = "127.0.0.1" port = 8080 timeout = 30 [llm] provider = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" model = "gpt-4o-mini" max_tokens = 1024 [vector_store] type = "qdrant" url_env = "QDRANT_URL" collection = "ml_faq_collection" top_k = 3 [web_search] provider = "bocha" api_key_env = "BOCHAAI_API_KEY" count = 10 [router] strategy = "llm_judge" fallback = "web_search"再看 Cursor 侧的 settings.json(在 Cursor 设置 → MCP → 添加全局 MCP 服务器里填):
{ "mcpServers": { "mcp-rag-app": { "command": "python", "args": ["/absolute/path/to/server.py"], "env": { "TAOTOKEN_API_KEY": "sk-你的key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "QDRANT_URL": "http://localhost:6333" }, "host": "127.0.0.1", "port": 8080, "timeout": 30000 } } }两份配置的对应关系可以用表格对照:
| 配置项 | config.toml | settings.json | 作用 |
|---|---|---|---|
| 服务地址 | server.host/port | host/port | MCP 服务监听地址 |
| 模型通道 | llm.base_url | env.TAOTOKEN_BASE_URL | 统一走 TaoToken |
| Key | llm.api_key_env | env.TAOTOKEN_API_KEY | 避免硬编码 |
| 向量库 | vector_store.url_env | env.QDRANT_URL | 本地 Qdrant |
| 超时 | server.timeout | timeout | 防止长请求挂死 |
4. 工具注册与路由判定示例
MCP 服务端暴露的工具必须用 tool 装饰器,并且要有清晰的文档字符串,因为模型就是靠这段描述来判断该不该调用它。下面这个 server.py 注册了两个工具:向量召回和网络检索。
# server.py from mcp.server.fastmcp import FastMCP from rag_code import Retriever, QdrantVDB, EmbedData import os, json, requests from dotenv import load_dotenv load_dotenv() mcp = FastMCP("mcp-rag-app", host="127.0.0.1", port=8080, timeout=30) @mcp.tool() def ml_faq_retrieval_tool(query: str) -> str: """从机器学习 FAQ 向量库中召回最相关文档。 当用户问题与机器学习、模型训练、特征工程等主题相关时使用。 输入: query 字符串 输出: 拼接后的上下文文本 """ if not isinstance(query, str): raise ValueError("query must be a string") retriever = Retriever(QdrantVDB("ml_faq_collection"), EmbedData()) return retriever.search(query) @mcp.tool() def web_search_tool(query: str) -> list[str]: """当问题超出机器学习 FAQ 范围、需要最新或通用信息时,使用网络检索。 输入: query 字符串 输出: 搜索结果列表 """ if not isinstance(query, str): raise ValueError("query must be a string") url = "https://api.bochaai.com/v1/web-search" api_key = os.getenv("BOCHAAI_API_KEY") if not api_key: raise ValueError("请在 .env 中设置 BOCHAAI_API_KEY") payload = json.dumps({"query": query, "summary": True, "count": 10, "page": 1}) headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} resp = requests.post(url, headers=headers, data=payload, timeout=20) return resp.json().get("organic", []) if __name__ == "__main__": print("MCP server on http://127.0.0.1:8080") mcp.run()路由判定这块,我建议不要只靠模型自由发挥,而是在工具描述里写清楚边界,再在系统提示里加一条规则。比如在 Cursor 的规则文件里写:
当问题涉及机器学习概念、训练技巧、特征处理时,优先调用 ml_faq_retrieval_tool。 当问题涉及最新资讯、具体产品、非 ML 领域知识,或向量召回结果明显不相关时,调用 web_search_tool。 如果向量召回返回内容为空或与问题无关,必须回退到 web_search_tool。这样「先向量后联网」的回退逻辑就有了明确触发条件,而不是每次靠运气。
5. 验证请求:确认调度链路可复现
配置完成后,先在 Cursor 里确认 MCP 服务已连接,工具列表里能看到两个工具。然后做两组验证。
第一组,问一个 ML 相关问题,比如「特征工程什么时候做比较合适」。预期是 Cursor 调用 ml_faq_retrieval_tool,返回 FAQ 里的相关条目。你可以在 Cursor 的 MCP 日志里看到工具调用记录。
第二组,问一个明显超出 FAQ 范围的问题,比如「今天有什么新的开源模型发布」。预期是模型先尝试向量召回,发现结果不相关后回退到 web_search_tool。这一步是验证 Agentic 路由的关键。
如果你想在命令行单独验证服务端是否正常,可以用 curl 模拟一次工具调用:
curl -X POST http://127.0.0.1:8080/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'返回里应该能看到两个工具的名称和描述。再调一次工具:
curl -X POST http://127.0.0.1:8080/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ml_faq_retrieval_tool","arguments":{"query":"如何避免过拟合"}}}'如果返回了 FAQ 上下文,说明向量召回链路通了。网络检索工具同理,把 name 换成 web_search_tool 即可。两组都通过,就说明「先向量后联网」的调度链路可复现。
6. 本篇常见错排查
第一个高频问题是 MCP 服务起不来,报端口占用。先确认 8080 没被别的进程占:
lsof -i :8080有占用就换端口,同时改 config.toml 和 settings.json 里的 port,两边必须一致。
第二个问题是 Cursor 里看不到工具。多数是 settings.json 里 args 的路径写成了相对路径。MCP 服务启动时的工作目录不一定是项目根目录,所以 server.py 的路径要用绝对路径。
第三个问题是向量召回一直返回空。先确认 Qdrant 容器在跑:
docker ps | grep qdrant再确认 collection 名字和 config.toml 里一致。如果 collection 不存在,需要先跑一次数据写入脚本把 FAQ 灌进去。
第四个问题是网络检索报 401。检查 .env 里的 BOCHAAI_API_KEY 是否被正确加载,load_dotenv() 要在读取环境变量之前调用。另外注意 Key 有没有多余空格。
第五个问题是模型调用报错,提示 base_url 不对。确认 TAOTOKEN_BASE_URL 是 https://taotoken.net/api ,不要多加斜杠或路径。如果还是不通,去 API Keys 页面确认 Key 状态:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
第六个问题是路由判定不稳定,有时该联网却走了向量库。这种情况把工具描述写得更具体,并在规则里明确「向量结果为空必须回退」。工具描述是模型判断的主要依据,描述模糊就会导致误判。
7. 继续往下走
链路跑通之后,你可以把向量库换成自己的业务文档,把网络检索工具换成你常用的搜索接口,路由规则也可以按业务调整。如果后面要做更复杂的多工具编排,建议先把每个工具的输入输出格式固定下来,再在 Cursor 里用规则约束调用顺序。
需要看接入细节的话,文档入口在这里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。想先在网页里验证模型通道是否正常,可以用模型对话:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat 。长期在 Cursor 里跑编码和 Agent 任务的话,Coding Plan 会更省心:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。