1. 项目概述与核心需求解析
1.1 为什么需要一个公众号文章搜索的 MCP 服务
微信公众号生态整体偏向封闭,官方既没有提供面向普通开发者的公开搜索 API,也不像网页搜索那样能被爬虫轻松索引。平时写技术方案、做竞品调研、整理行业资料的时候,大量高价值内容其实都沉淀在公众号文章里——但想要高效检索这些内容,往往只能打开微信客户端一篇篇翻,或者借助搜狗微信搜索这种体验一言难尽的渠道。
我实际踩过的场景是这样的:本地跑着一个基于 Ollama 的私有知识库问答服务,用户问一个业务问题,模型如果能先检索到几篇相关的公众号文章作为上下文,回答质量会明显更好。这就需要有一个能被本地 Agent 直接调用的、标准化的公众号文章搜索能力。MCP(Model Context Protocol)恰好就是干这个的——它是 Anthropic 在 2024 年底推出来的一个开放协议,本质上是给 AI 应用和外部工具之间定义了一套标准化的“插头接口”。有了 MCP,本地部署的大语言模型应用(比如 Claude Code、Dify、Cherry Studio 这类前端)就能通过统一的协议调用一个“公众号文章搜索服务”,而不需要针对每个应用单独做定制对接。
weixin_search_mcp 就是这样一个把公众号文章搜索能力封装成 MCP 服务的项目。你可以把它理解成一个“公众号文章搜索中介”——它自己不生产内容,也不直接存储文章,而是负责接收 AI 应用的搜索请求,然后去聚合搜索微信公众号文章,把标题、链接、摘要、来源公众号等信息结构化返回给 AI 应用。
这个服务最适合两类人:一类是在本地部署了大模型应用、希望给自己的 Agent 增加实时信息检索能力的开发者;另一类是平时需要大量阅读公众号技术文章、希望把检索能力集成到自己工作流里的效率工具爱好者。这篇文章我会从零开始讲清楚怎么在本地把这个服务跑起来,然后把服务安全地暴露到外部访问,最后说清楚我在实际操作中踩过的坑。
1.2 这个服务在整套架构里的位置
在动手之前,先理清楚 weixin_search_mcp 在整个链路里扮演的角色。我画一条最典型的调用链路,大家感受一下(这里用文字描述,不用流程图,各位在脑子里过一遍就行):
本地大模型客户端(比如 Claude Desktop、Cherry Studio、Dify) → 通过 MCP 协议发起工具调用请求 → weixin_search_mcp 服务(本地运行,监听某一端口) → 聚合搜索公众号文章 → 返回结构化结果 → 客户端把结果作为上下文交给大模型 → 模型生成基于真实检索内容的回答
这个链路里有三个关键点值得展开说。
第一,MCP 协议核心是 JSON-RPC 2.0 规范。客户端和服务端通过 JSON-RPC 消息通信,消息类型主要是 initialize(握手建立会话)、tools/list(获取可用工具列表)、tools/call(调用具体工具)。weixin_search_mcp 启动后会向客户端暴露一个名为“search_wechat_article”之类的工具,客户端通过 tools/call 传关键词等参数,服务端返回搜索结果。
第二,传输方式上,MCP 支持 stdio(标准输入输出)和 SSE(Server-Sent Events,基于 HTTP)两种主流传输。stdio 模式适合本地直接启动的子进程,比如 Claude Desktop 通过 npx 拉起服务;SSE 模式适合把服务运行在独立端口上,通过网络访问,这也是实现“外部访问”的基础。weixin_search_mcp 这类服务一般支持这两种模式,如果你打算让局域网内其他设备甚至公网环境访问,就必须用 SSE 模式。
第三,本地部署的核心价值在于数据不出内网。搜索请求、搜索结果、上下文组装全部发生在你自己的机器上,这对于企业内部知识管理和有隐私要求的场景格外重要。我见过不少团队把公众号文章搜索直接接入到企业内部的 RAG(检索增强生成)流程中,输入的是员工提问,输出的是经过检索增强的回答,全程不依赖第三方云服务,这个思路在数据合规方面优势很明显。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 本地部署的前置准备与方案选型
2.1 环境要求:硬性条件与推荐配置
先看硬性要求。weixin_search_mcp 本身是个轻量级 Python 服务,对硬件没有太高要求——你不需要为了跑它专门配一台高性能机器,普通办公电脑、NUC 小主机、甚至树莓派都能胜任。真正的资源消耗大户是它上游对接的大语言模型,如果你只是用 Claude Code 或 GPT 这类云端 API 充当客户端,本地机器能跑 Python 就行;如果你想全链路本地化,用 Ollama 跑 Qwen 或 DeepSeek 这类模型,那建议至少 16GB 内存、支持 AVX2 指令集的 CPU,最好有 NVIDIA 显卡(6GB 显存以上),推理速度会明显不一样。
软件层面的依赖整理如下:
- Python 3.10 或更高版本,建议 3.11/3.12(3.13 某些依赖可能还没完全适配)
- pip 包管理器
- Git(用于拉取代码)
- Node.js 18+(仅在需要通过 npx 方式调用某些 MCP 客户端时用到,服务本身不依赖)
- 一个可选的本地大模型(Ollama + qwen2.5:7b 这类,用于后续测试完整链路)
这里我需要补充一个选型建议:如果你和我一样使用 Windows 系统,强烈建议优先考虑 WSL2(Windows Subsystem for Linux 2)来部署,而不是直接用 PowerShell 跑。原因有三个:一是很多 MCP 生态的工具链在 Linux 下兼容性更好,踩坑率低;二是 WSL2 的网络模式天然支持端口转发,后续做外部访问配置更顺手;三是大量 AI 相关的 Python 依赖在 Linux 下的预编译 wheel 包更全。当然,直接在 Windows 原生环境跑也不是不行,只是 I/O 性能和进程管理上体验差一些。
2.2 方案选型:为什么选择本地部署
关于部署方式,圈子里其实有几种路线:直接用云端在线 MCP 服务(网上能找到一些托管的公众号搜索 MCP)、本地部署 weixin_search_mcp、或者自己写爬虫抓取文章存到本地再对接检索。
我的建议很明确:如果你对数据有控制欲、或者公司有合规要求、或者你希望搜索逻辑能自己随时调整,直接本地部署 weixin_search_mcp 是性价比最高的方案。对比一下就清楚了:
云端在线 MCP 服务的问题是:你不知道它背后到底在调用什么数据源,接口稳定性不受你控制,更麻烦的是你的搜索关键词会经过第三方服务,这在企业场景里是不可接受的。自己写爬虫的问题是:微信公众号的防爬机制一直在升级,你辛辛苦苦写的采集脚本可能今天还能跑、明天就被封了,而且公众号文章的链接时效性很强(后面我会专门讲这个坑),维护成本远超想象。而本地部署 weixin_search_mcp 相当于拿一个现成的、封装好的搜索服务跑在你自己的环境里,数据链路你来控制,搜索逻辑透明,需要改的时候直接改源码重启就行。
还有一个现实层面的考量:MCP 协议本身是一个快速演进的生态,本地部署一个开源项目,意味着你能第一时间跟进新特性、自己修 bug、甚至给上游提 PR。我用这个项目的时候发现它的搜索策略可以按需调整,比如设置定时缓存、切换搜索源,这些都是云端服务给不了的灵活性。
2.3 外部访问方案:先想清楚你要什么
“实现外部访问”这个需求,要分三个层次来看,因为不同层次的方案差别很大:
第一层是局域网访问:手机、平板、另一台电脑通过同一 WiFi/局域网访问你机器上的 MCP 服务。这是成本最低的方案,直接用 Uvicorn 绑定 0.0.0.0 就行,不需要任何额外工具。
第二层是公网访问:你在外面(比如公司、地铁上),希望通过互联网访问家里的服务。这种场景下可选方案有:路由器端口映射(需要有公网 IP)、内网穿透工具(比如 ngrok、cpolar 这类)、或者借助 Cloudflare Tunnel 这类方案。基于我实测的经验,如果你没有公网 IP,内网穿透是主流选择(这部分在后面的实操章节会展开细说,注意我提到的工具都是合规的远程访问技术,主要用于个人设备管理、开发调试等正当场景)。
第三层是 HTTPS 加密访问:一旦服务暴露到公网,明文 HTTP 传输的敏感信息就很危险了。MCP 工具调用过程中传递的搜索关键词、结构化结果,都可能涉及业务数据,所以只要走公网,就必须把 HTTPS 安排上。
在本文的实操环节,我会重点覆盖局域网访问和基于内网穿透的公网访问两条路线,这两条是绝大多数个人开发者和小团队最常用到的。如果你的需求只是“我自己在客厅电脑上跑着玩,手机在卧室想连一下”,那看局域网章节就够了。
3. 核心细节解析与配置要点
3.1 MCP 服务的核心机制:理解 .mcp 文件和 MCP Server
在真正动手跑 weixin_search_mcp 之前,我花了很长时间才真正理解 MCP 服务端和客户端之间是怎么“认识”的,这里把核心机制讲透,大家后面配置才不会一头雾水。
MCP 服务端的本质是一个独立的程序,它知道自己的“工具清单”。客户端(比如 Claude Desktop、Dify、Cherry Studio)想要使用这个服务,需要知道三件事:这个服务在哪个地址、通过什么协议通信、有哪些工具可用。这三件事的载体就是 .mcp 配置文件。
以一个典型的 .mcp 文件(JSON 格式)为例:
json复制{
"mcpServers": {
"weixin_search": {
"command": "python",
"args": ["path/to/weixin_search_mcp/src/server.py"],
"env": {
"SEARCH_API_KEY": "your_key_here"
},
"url": "",
"transportType": "stdio"
}
}
}
这个文件里的 transportType 字段决定了通信方式。stdio 模式下,客户端会拉起这个 Python 进程,然后通过标准输入输出跟它对话——这种方式的好处是不用管端口、不用管网络,客户端直接管理子进程的生命周期,本地单机用非常稳定。但你注意,stdio 模式的服务进程是有状态的,它跟客户端的生命周期绑定,只能被一个客户端实例使用。
如果是 SSE 模式,文件就变成这样:
json复制{
"mcpServers": {
"weixin_search": {
"command": "",
"args": [],
"env": {},
"url": "http://127.0.0.1:8000/sse",
"transportType": "sse"
}
}
}
这时候 MCP Server 本身是独立跑着的 HTTP 服务(用 Uvicorn 监听 8000 端口),客户端通过网络访问 /sse 这个端点。这种模式的优势是:服务跟客户端解耦,多个客户端可以同时连同一个服务,而且服务可以部署在远程机器上——这就是“外部访问”的底层支撑。
理解了这个机制,你就能明白为什么很多 MCP 服务同时支持 stdio 和 SSE 两种启动方式了:本地调试用 stdio 简单直接,生产环境要对外提供服务就切 SSE。weixin_search_mcp 默认用 stdio,本文的核心目标之一就是把它切到 SSE 模式并网络化部署。
3.2 weixin_search_mcp 服务的代码结构解析
如果项目已经从 GitHub 拉下来了(仓库地址在 README 里有,搜 weixin_search_mcp 就能找到),你会看到一套标准的 Python MCP 服务项目结构:
code复制weixin_search_mcp/
├── src/
│ ├── __init__.py
│ ├── server.py # MCP 服务端入口
│ ├── searcher.py # 核心搜索逻辑
│ └── config.py # 配置读取
├── requirements.txt # 依赖清单
├── .env.example # 环境变量模板
├── README.md
└── pyproject.toml
我来拆解一下每个文件的作用,这对接下来的修改和排错至关重要。
server.py 是整个服务的入口,负责创建 MCP 服务实例、注册工具函数、启动监听。在这个项目里,工具函数通常用装饰器注册,类似下面的写法:
python复制from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weixin_search")
@mcp.tool()
def search_wechat_article(keyword: str, limit: int = 5) -> list[dict]:
"""搜索微信公众号文章,返回标题、链接、摘要和公众号信息"""
results = wechat_search(keyword, limit)
return results
if __name__ == "__main__":
mcp.run()
searcher.py 是核心搜索逻辑所在。这个文件做的事情是:接收关键词和数量参数,调用微信公众号文章的搜索渠道(通常是聚合搜索接口),解析返回的 HTML 或 JSON,提取出标题、链接、摘要、来源公众号、发布时间等结构化信息,做去重和排序后返回。这部分是整个服务的价值核心,也是最容易出问题的地方——因为公众号文章的链接经常失效,搜索引擎返回的结果格式也可能变化。
config.py 负责读取环境变量,比如端口号、搜索超时时间、是否启用缓存等。环境变量文件 .env 里通常包含这样几项:
bash复制# 服务监听端口,SSE 模式生效
MCP_PORT=8000
# 搜索超时(秒)
SEARCH_TIMEOUT=10
# 结果数量上限
MAX_RESULTS=20
# 是否启用结果缓存
ENABLE_CACHE=true
理解了这些结构,后面遇到启动报错、无搜索结果、链接失效这类问题时,你就能快速定位到具体文件去排查,而不是搜索引擎乱搜一通。
3.3 配置要点的优先级:先本地跑通,再考虑外部暴露
这里有一个我反复强调的经验:第一次部署的时候,永远先以 stdio 模式跑通本地,确认服务本身没问题,再切换到 SSE 模式,最后再考虑外部访问。跳步操作会让你同时面对“服务自身问题”和“网络配置问题”两个变量,排错时非常痛苦。
本地 stdio 模式验证的核心指标只有一条:客户端能不能通过 tools/list 发现到 search_wechat_article 这个工具。如果这个都过不了,后面全部白搭。我在 3.4 节会给你一个 API 兼容层验证的方法,让你不依赖任何 MCP 客户端就能测试服务是否正常响应。
配置参数方面需要重点说明几个:
- SEARCH_TIMEOUT 建议设 10 秒以上。公众号文章搜索是聚合型请求,数据源响应慢是常态,如果超时设太短,经常会出现明明关键词没错但返回空结果的情况。
- ENABLE_CACHE 建议开启。相似的搜索请求(比如“Dify”和“dify 部署教程”)如果命中缓存,响应速度会从 3-5 秒降到毫秒级。但这个缓存会占内存,如果你机器配置低,可以关掉。
- MAX_RESULTS 默认 20 个结果够用。结果太多反而干扰大模型的上下文,我实际测试下来传给 LLM 的结果控制在 5-8 条效果最好。
4. 实操过程与核心实现
4.1 第一步:克隆项目并安装依赖
所有步骤在 Ubuntu 22.04+ / WSL2 / macOS 下都通用,我用的是 WSL2 里的 Ubuntu 22.04,Python 3.11。如果你用其他环境,命令大同小异。
先更新系统依赖并安装 Python 虚拟环境工具:
bash复制sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-venv git
然后克隆项目并创建虚拟环境:
bash复制git clone https://github.com/your-repo/weixin_search_mcp.git
cd weixin_search_mcp
# 创建独立的 Python 虚拟环境,避免污染系统 Python
python3 -m venv .venv
source .venv/bin/activate
激活虚拟环境后,安装依赖:
bash复制pip install --upgrade pip
pip install -r requirements.txt
看到最后一行输出 “Successfully installed …” 就算装完了。依赖列表里一般包含 mcp(官方 Python SDK)、httpx(用于发 HTTP 请求做搜索)、uvicorn(ASGI 服务器,SSE 模式要用)这几个核心包。
这里有个小坑提示:pip 安装 mcp 包时可能会因为网络原因很慢或超时。如果你在国内网络环境,建议切到镜像源:
bash复制pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
4.2 第二步:配置 .env 文件并启动服务
复制 .env.example 为 .env,然后按需修改:
bash复制cp .env.example .env
vim .env
参考配置如下:
bash复制# 服务监听端口
MCP_PORT=8899
# 搜索超时时间(秒)
SEARCH_TIMEOUT=15
# 最大返回结果数
MAX_RESULTS=20
# 启用缓存
ENABLE_CACHE=true
先验证 stdio 模式能不能跑通。直接运行:
bash复制python src/server.py
如果你看到类似 “MCP server running on stdio” 的日志,说明服务的 stdio 模式正常。但是注意,stdio 模式下服务会等待客户端的初始化消息,这一步光看日志看不出搜索功能是否正常,我们需要用更直接的方式验证。
在项目目录下创建一个测试脚本 test_mcp_direct.py,绕过 MCP 协议层直接测试搜索逻辑:
python复制import asyncio
from src.searcher import wechat_search
async def main():
results = await wechat_search("Dify 本地部署教程", limit=3)
for i, item in enumerate(results, 1):
print(f"{i}. {item['title']}")
print(f" 来源: {item['source']}")
print(f" 链接: {item['url']}")
print(f" 摘要: {item['snippet'][:80]}...")
print()
if __name__ == "__main__":
asyncio.run(main())
运行:
bash复制python test_mcp_direct.py
如果你能看到返回的结果列表,说明核心搜索逻辑没问题。这时候再确认一件事——服务本身能不能正常被 MCP 客户端发现工具。用一个 Python 脚本做协议层验证:
python复制import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server_params = StdioServerParameters(
command="python",
args=["src/server.py"],
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
for tool in tools.tools:
print(f"发现工具: {tool.name} - {tool.description}")
result = await session.call_tool(
"search_wechat_article",
{"keyword": "MCP 协议", "limit": 3}
)
print(f"搜索结果: {result}")
asyncio.run(main())
运行结果会列出服务暴露的所有工具(正常情况下是 search_wechat_article),并实际调用一次搜索。看到这里,本地 stdio 模式就算完全验证通过了。
4.3 第三步:切换到 SSE 模式,实现局域网访问
本地 stdio 跑通之后,接下来是核心环节——把服务从 stdio 切到 SSE 模式,让服务跑在独立端口上。
检查 server.py 的入口代码,通常 mcp.run() 默认走 stdio。要切 SSE 模式,需要修改启动方式。常见做法有两种:
如果你的 mcp SDK 版本支持 transport 参数,可以这样:
python复制if __name__ == "__main__":
# 读取环境变量,决定运行方式
import os
transport = os.getenv("MCP_TRANSPORT", "stdio")
if transport == "sse":
mcp.run(transport="sse", host="0.0.0.0", port=int(os.getenv("MCP_PORT", "8899")))
else:
mcp.run(transport="stdio")
如果你的 SDK 版本比较老不支持 transport 参数,就需要自己用 FastMCP 结合 Starlette 搭一个 SSE 服务。这里我提供一个通用的解决方式,用 FastMCP 的 SSE 支持:
python复制import os
from mcp.server.fastmcp import FastMCP
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
import uvicorn
mcp = FastMCP("weixin_search")
# 注册工具函数(同前)
# 创建 SSE 传输层
sse = SseServerTransport("/messages/")
async def handle_sse(request):
async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
await mcp.run(streams[0], streams[1], mcp.create_initialization_options())
app = Starlette(
routes=[
Route("/sse", endpoint=handle_sse),
]
)
if __name__ == "__main__":
port = int(os.getenv("MCP_PORT", "8899"))
uvicorn.run(app, host="0.0.0.0", port=port)
保存后重启服务:
bash复制export MCP_TRANSPORT=sse
python src/server.py
看到 Uvicorn 的日志输出 “Uvicorn running on http://0.0.0.0:8899”,SSE 模式就启动成功了。注意这里绑定的是 0.0.0.0,表示监听所有网络接口,关键一步。
现在验证局域网访问。在另一台设备上(手机、另一台电脑),打开浏览器访问:
code复制http://<你的局域网IP>:8899/sse
如果能看到响应(可能显示空白或一段文本),说明服务已经被外部设备访问到了。如果打不开,大概率是防火墙拦截了端口,Linux 下用:
bash复制sudo ufw allow 8899/tcp
Windows 下则需要在“防火墙-高级设置-入站规则”中放行 TCP 8899 端口。
到这步,你的 weixin_search_mcp 已经能从局域网内任意设备访问了,手机、平板、另一台开发机都能连接。接下来看怎么把它放到公网。
4.4 第四步:公网访问的实现方案
公网访问这件事,根据你有没有公网 IP,路线完全不同。
方案 A:有公网 IP,走路由器端口映射
如果你家宽带是公网 IP(打电话给运营商可以申请),那直接在路由器上做端口映射就行。登录路由器管理后台,找到“端口映射”或“虚拟服务器”设置,把公网端口 8899 映射到内网机器的 192.168.x.x:8899。然后通过 http://你的公网IP:8899/sse 就能访问。
这种方式配置简单、速度最快,但有两个硬伤:一是绝大多数家庭宽带的公网 IP 是动态的,隔一段时间会变,需要配合 DDNS(动态域名解析)服务解决;二是公网 IP 直接裸奔,服务的安全完全暴露在互联网探针之下,强烈不建议直接暴露非加密端口。
方案 B:没有公网 IP,走内网穿透
如果你没有公网 IP(绝大多数人的情况),内网穿透是主流选择。市面上的工具比较多,ngrok、cpolar、frp 是几个常见选项。我以 cpolar 为例讲一下操作流程(因为它不需要自己买服务器,注册就有免费额度;ngrok 也很类似,挑一个用就行)。
先安装 cpolar:
bash复制curl -L https://www.cpolar.com/static/downloads/releases/3.3.15/cpolar-stable-linux-amd64.zip -o cpolar.zip
unzip cpolar.zip
sudo mv cpolar /usr/local/bin/
注册账号并登录(这是免费版的基础操作),拿到 authtoken 后运行:
bash复制cpolar authtoken <你的token>
cpolar http 8899
命令执行后,cpolar 会输出一条公网地址,类似 https://xxxxxx.cpolar.top,这个地址就能从外面访问你本地的 8899 端口。把地址加上 /sse 路径:
code复制https://xxxxxx.cpolar.top/sse
就是你的 MCP 服务公网端点。
这类穿透工具的免费版有个通病:域名是随机分配的,重启服务后会变,而且免费版有时候速度会受限。如果你有预算且对稳定性有要求,可以几十块钱买个域名绑上去。如果对数据敏感,也可以用 Cloudflare Tunnel 这类方案,它可以把隧道流量加密到 Cloudflare 节点,外部无法直接探测你的源站 IP,安全性会好一截。
方案 C:HTTPS 加密是公网访问的底线
公网暴露意味着流量要经过不可信的中间网络,没有 HTTPS 加密就相当于把搜索关键词和返回内容明文摆在大马路上。cpolar 这类工具本身就给分配的域名绑定了有效的 HTTPS 证书,所以用方案 B 的话天然就是 HTTPS 访问,这个坑省了。但如果你走方案 A 用公网 IP 直连 HTTP,就一定要补上 TLS。
给 Uvicorn 服务挂 HTTPS 证书的配置方式:
bash复制# 生成自签名证书(仅测试用)或者用 certbot 申请正规证书
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365 -subj "/CN=yourdomain.com"
# 修改启动方式,启用 SSL
uvicorn.run(app, host="0.0.0.0", port=8899, ssl_keyfile="key.pem", ssl_certfile="cert.pem")
注意:自签名证书在给 MCP 客户端配置时会报证书不信任的警告,如果你用正规域名 + Let‘s Encrypt 证书,客户端这边就不用额外处理,体验会好很多。
4.5 第五步:在 MCP 客户端中配置 weixin_search_mcp
服务跑起来了,现在要真正让 AI 应用用到它。不同客户端的配置方式略有差异,我分别说。
Claude Desktop / Claude Code
Claude Desktop 的 MCP 配置文件在 claude_desktop_config.json 里(Windows 在 %APPDATA%\Claude\ 下,macOS 在 ~/Library/Application Support/Claude/)。SSE 模式的配置片段如下:
json复制{
"mcpServers": {
"weixin_search": {
"command": "",
"args": [],
"env": {},
"url": "http://127.0.0.1:8899/sse",
"transportType": "sse"
}
}
}
配置保存后重启 Claude Desktop,在对话中发送 “帮我搜索一下最近关于大模型本地部署的公众号文章”,如果配置成功,Claude 会自动调用 search_wechat_article 工具并基于返回结果回答。
Claude Code 的命令行版本,配置文件在项目目录的 .mcp.json 里,内容格式跟上面类似。配置好后,在终端里输入 /mcp 就能看到当前连接的 MCP 工具列表。
Cherry Studio 和 Dify
Cherry Studio 的 MCP 配置入口在设置里,添加 MCP Server 时选择 SSE 类型,填入 URL 即可。Dify 则在“工具”模块中添加自定义 MCP 工具,同样填 URL。这两个客户端的界面很直观,照着填就行。
这里有个值得一提的点:用 Dify 这类平台时,你可以在工作流里把公众号搜索 MCP 和本地 Ollama 模型串起来,形成一个完整的“检索增强生成”链路。比如工作流节点依次是:用户输入 → 调用 weixin_search_mcp 搜索 → 把搜索结果拼接进 Prompt → 调用本地 Ollama 模型生成回答。这个组合是我目前在本地环境里用的最多的高价值架构。
4.6 整体配置速查表
把前面所有配置汇总成一张速查表,方便部署时对照:
| 项目 | 配置项 | 建议值 | 说明 |
|---|---|---|---|
| 监听端口 | MCP_PORT | 8899 | 避免与常用端口冲突 |
| 监听地址 | host | 0.0.0.0 | 必须为 0.0.0.0 才能被外部访问 |
| SSE 端点 | path | /sse | 客户端配置时要填完整 URL |
| 搜索超时 | SEARCH_TIMEOUT | 15s | 聚合搜索响应慢,不宜设太短 |
| 结果数量 | MAX_RESULTS | 20 | 传给 LLM 的建议 5-8 条 |
| 缓存 | ENABLE_CACHE | true | 提升重复关键词响应速度 |
| 防火墙 | TCP 8899 | 放行 | UFW 或 Windows 防火墙 |
| 局域网访问 | URL | http://IP:8899/sse | 用局域网 IP 而非 127.0.0.1 |
| 公网访问 | URL | https://域名/sse | 必须带 HTTPS |
配置时记住一个验证顺序,能省去大量排错时间:
- 本地直接访问 http://127.0.0.1:8899/sse 通不通?
- 局域网内用局域网 IP 访问通不通?
- 公网用分配域名访问通不通?
哪一步不通,就集中排查哪一步的问题,不要跳步。
5. 常见问题与排查技巧实录
这部分是我实际使用中遇到最多的问题,一条条列出来,纯经验之谈,有需要可以按表对照排查。
5.1 服务启动与连接类问题
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 启动报 ModuleNotFoundError: No module named ‘mcp’ | 依赖未安装或虚拟环境未激活 | 检查 .venv/bin/activate 是否已执行,重新 pip install -r requirements.txt |
| 服务启动后局域网设备无法访问 | 防火墙拦截端口 | Linux 执行 sudo ufw allow 8899/tcp;Windows 检查入站规则 |
| 客户端连接时报 Connection refused | 服务没启动 / 端口被占用 / 绑定了 127.0.0.1 | 确认服务进程存活;`netstat -tlnp |
| SSE 连接建立后随即断开 | 代理环境干扰 / 客户端与服务端版本不兼容 | 检查本地代理设置,把 127.0.0.1 加入 no_proxy;升级 mcp SDK 到最新版 |
| 公网地址偶尔能连、偶尔超时 | 免费版穿透服务限速 / 本地网络抖动 | 升级付费版或改用 Cloudflare Tunnel;检查本地网络稳定性 |
| 工具列表里看不到 search_wechat_article | 服务端注册失败 / 客户端缓存了旧配置 | 直接访问 /sse 检查服务是否响应;重启客户端;确认 transportType 为 sse |
这里面我要特别强调一个我踩过多次的坑:如果你的机器上开了代理(比如为了访问 GitHub),MCP 客户端通过 SSE 连接本地服务时,代理可能拦截了 localhost 请求,导致连接失败。解决方案是把 127.0.0.1、localhost 加进代理的 no_proxy 列表。这类问题很隐蔽,因为服务本身是正常的,但流量被劫走了。
5.2 搜索功能异常类问题
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 搜索返回空结果 | 关键词过于冷门 / 搜索源接口变更 / 超时时间太短 | 先换个宽泛关键词测试;检查 searcher.py 的解析逻辑是否需要更新;调大 SEARCH_TIMEOUT |
| 返回结果里链接打不开 | 微信公众号链接时效性强,聚合渠道返回的临时链接已过期 | 在搜索结果中增加发布时间字段过滤;对链接做二次校验,无效的剔除 |
| 搜索用时过长(超过 5 秒) | 搜索源响应慢 / 启用了缓存但首次查询需预热 | 接受首次延时,后续相同关键词走缓存;或并行请求多个搜索源取最快返回 |
| 返回结果的标题乱码 | 编码解析问题 | 在 searcher.py 的响应解析处强制指定 UTF-8 编码 |
关于公众号链接失效的问题,这里多说几句。微信公众号文章的链接以 https://mp.weixin.qq.com/s/xxx 开头,这类链接有时候会因为文章被删除、被投诉或违规而下线。而聚合搜索返回的链接,可能是经过跳转的临时链接,时效性更差。我的做法是在搜索结果里加上发布时间和来源公众号字段,让大模型在引用时能判断信息的新旧;关键场景下,还会在调用搜索后对链接做一次 HEAD 请求验证可访问性,才把最终结果交给模型。当然,这样会增加响应时间,需要和体验做权衡。
5.3 实际问题排查案例:Claude Code 中工具注册失败
这是一个具体案例。热搜词里有人提到“figma mcp 在 codex 中总是工具注册不上”,其实 MCP 协议是通用的,换到 weixin_search_mcp 也可能遇到同样的问题。
现象:在 Claude Code 的配置文件里加了 weixin_search 的 SSE 配置,但 /mcp 命令看不到这个工具。
排查步骤:
- 先确认服务本身是活的:浏览器访问
http://127.0.0.1:8899/sse,如果服务正常会挂住连接,没有任何报错。 - 查看 Claude Code 的日志:终端里跑
claude --debug,能看到 MCP 客户端的连接日志,如果 URL 拼错了、端口不对,这一步会暴露。 - 检查配置文件格式:JSON 里不能有注释,不能有多余的逗号,
transportType字段必须是小写sse。 - 确认客户端版本:老版本 Claude Code 对 SSE MCP 的支持不完善,升级到最新版往往就解决了。
这类问题的共性结论是:绝大多数“工具注册不上”的问题不是 MCP 服务端的问题,而是客户端配置或版本兼容性的问题。排查时先信服务端正常的,把矛头转向客户端配置,效率会高很多。
5.4 Docker 部署方式(可选但推荐)
如果你的服务器环境比较干净,不想在宿主机上装 Python 依赖,用 Docker 部署更省心。项目文档或 Dockerfile 如果还没提供,可以自己写一个:
dockerfile复制FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ src/
COPY .env .
ENV MCP_TRANSPORT=sse
ENV MCP_PORT=8899
EXPOSE 8899
CMD ["python", "src/server.py"]
构建并运行:
bash复制docker build -t weixin_search_mcp .
docker run -d --name weixin-mcp -p 8899:8899 --env-file .env weixin_search_mcp
Docker 部署的一个额外优势是:你可以给容器设置资源限制(--memory、--cpus),避免搜索请求过多时拖垮宿主机的其他服务。另一个小技巧是用 --restart=always 让容器在系统重启后自动拉起。
6. 外部访问安全加固:这是最容易忽略的一环
服务暴露到外部,安全问题必须重视。我见过不少小伙伴把服务暴露到公网后完全不设防,没多久就被人扫描并滥用,甚至被当作肉鸡。下面按优先级列出几个必须做的加固措施。
6.1 必要的认证与鉴权
MCP 协议本身没有内置认证机制,一旦服务暴露到公网,任何人都能调用你的搜索工具,这不仅是流量损耗的问题,更可能被恶意利用(比如高频搜索触发搜索源的风控,导致你的服务被封禁)。所以在公网访问前面加一层认证是必须的。
一个简单的方案是在服务前面加一个反向代理做 Basic Auth。用 Nginx 配置:
nginx复制server {
listen 8899 ssl;
server_name yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:8899;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
.htpasswd 文件用 htpasswd -c /etc/nginx/.htpasswd weixin_user 创建,设置一个强密码。这样外部访问 URL 就得带用户名密码,MCP 客户端配置时需要在 URL 中嵌入认证信息或者通过请求头携带。
如果你的客户端不支持在 MCP 配置里带认证头(目前不少支持不完善),一个替代方案是使用内网穿透工具自带的基础认证功能。以 cpolar 为例,付费版可以开启“Basic Auth”选项,免费版就没有,需要注意。
6.2 访问频率限制与审计
即使加了认证,也建议限制调用频率。搜索服务很容易被循环调用,既消耗资源又容易被上游限流。在 Nginx 里加 limit_req 模块做限速:
nginx复制limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=5r/s;
server {
...
location / {
limit_req zone=mcp_limit burst=10 nodelay;
proxy_pass http://127.0.0.1:8899;
}
}
日志方面,access_log 默认会记录所有请求,建议开启并定期检查,观察是否有来源 IP 的异常高频调用。如果发现异常 IP,可以在防火墙层面直接封禁。这些操作说起来简单,但大部分人在服务跑通后根本不会去做,直到被薅了才后悔。
6.3 反代 vs 直连:推荐架构
所以,最终面向公网的推荐架构是:
公网入口(Nginx/CF Tunnel,带 HTTPS + 认证 + 限流) → 本机端口 8899(weixin_search_mcp SSE 服务) → 上游搜索源
而不是直接把 8899 端口映射出去。这样即使 MCP 服务本身有漏洞,攻击者面对的也只是一层反代,不那么容易直接打到服务本身。如果你用 Cloudflare Tunnel,还能额外获得一层 DDoS 防护和 WAF 规则,安全性更上一个台阶。
7. 经验总结与进阶玩法
7.1 从实践中总结的几条核心体会
第一,MCP 服务本质上就是一个“工具适配层”。它没有改变搜索逻辑的本质,而是用标准化协议把能力“包装”成了 AI 应用可以理解的工具。这也解释了为什么同一个 weixin_search_mcp 能同时被 Claude Desktop、Dify、Cherry Studio 这些完全不同的客户端使用——关键在于协议标准,而不是针对每个应用做定制集成。理解了这一层,你之后接任何 MCP 服务(数据库查询、GitHub 操作、浏览器自动化等等)都会触类旁通。
第二,公众号文章搜索和网页搜索有一个显著区别:它的数据源更“封闭”,结果质量高度依赖搜索渠道。这就意味着 searcher.py 的解析逻辑可能会因为搜索源的页面结构调整而失效。我建议把搜索逻辑设计成可插拔的,万一主搜索源不可用,能自动切换备选源,或者至少做到日志清晰,方便快速定位上游问题。
第三,外部访问这件事,重点不在“打通”,而在“安全地打通”。我见过不少开发者把服务暴露到公网后懒得加认证,结果某天发现日志里全是来自世界各地的扫描请求。记住一句话:任何暴露到互联网的端口,都会在几小时之内被扫描器发现——这不是吓唬人,是真实发生的。所以认证、限流、HTTPS,这三件事是公网访问的“入场券”,不是可选项。
第四,MCP 生态目前还处于快速演进期,不同版本间可能存在接口变动。建议部署时锁定依赖版本(requirements.txt 里精确到次要版本号),并定期关注上游更新。特别是 mcp 这个 SDK 本身迭代很快,半年不更新可能就出现兼容性问题。
7.2 进阶玩法一:让它接入本地 RAG 工作流
一个非常有价值的组合是 weixin_search_mcp + Ollama + Dify 的本地 RAG 链路。你可以把公众号搜索结果注入到一个记忆组件里,让本地大模型在回答问题时能引用最新的公众号技术文章。例如,用户问“怎么优化 Dify 的知识库分段设置”,模型可以先调用搜索工具找到几篇相关的公众号教程,结合自己已有的知识库给出更精准的答案。这在企业内部做知识管理系统时特别有用。
7.3 进阶玩法二:多服务协同
既然已经打通了 MCP 这条链路,后续完全可以继续往同一套架构上加更多 MCP 服务——比如 GitHub MCP(代码仓库操作)、Playwright MCP(浏览器自动化)、甚至本地数据库 MCP。最终你的本地 AI 应用会变成一个真正意义上的“Agent Hub”,能搜索公众号、读网页、写代码、查数据库,全部通过同一套 MCP 接口来编排。这也是目前 AI 工程领域热度最高的方向之一——用 MCP 把工具生态串起来,而不是每个应用各搞一套。
7.4 关于稳定性的一点补充建议
最后提一个关键但容易被忽视的点:作为常驻服务,weixin_search_mcp 建议用 systemd(Linux)或 LaunchAgent(macOS)来托管,避免终端一关服务就没了。Linux 下的 systemd 服务文件示例:
ini复制[Unit]
Description=weixin_search_mcp service
After=network.target
[Service]
Type=simple
User=your_user
WorkingDirectory=/path/to/weixin_search_mcp
ExecStart=/path/to/weixin_search_mcp/.venv/bin/python src/server.py
Restart=always
RestartSec=3
EnvironmentFile=/path/to/weixin_search_mcp/.env
[Install]
WantedBy=multi-user.target
保存后执行 sudo systemctl daemon-reload && sudo systemctl enable --now weixin-search-mcp,服务就注册成开机自启、崩溃自动重启的守护进程了。这一步做完,你才真正得到一个可以长期稳定运行的 MCP 服务节点。
我在本地跑这几个服务的实际体会是,MCP 这套生态确实把“AI 应用能干什么”的天花板抬高了一个量级。公众号文章搜索只是其中一个很小的能力点,但一旦跑通了从“本地模型 → MCP 服务 → 外部数据源 → 增强回答”这条完整链路,后面再接什么工具都是复刻同样的流程。希望这篇实操记录能帮你少走几步弯路——尤其是指定好内部调试、局域网验证、公网加固这三个阶段依次推进,不要跳步。部署过程中如果再遇到什么疑难杂症,欢迎在评论区把报错信息贴出来,我看到会尽量解答。
