Doramagic 项目包 · 项目说明书

web_search_mcp 项目

本地 MCP 服务器,提供统一的 search_web 工具,可并行查询 SearXNG 与 Brave,并进行 URL 规范化、规范化 URL 去重以及基于重叠度/可信度/时效性的排序,为下游 LLM agent 提供服务。

项目概览

websearchmcp 是一个基于 Model Context Protocol (MCP) 协议的 Web 搜索服务器项目,旨在为大语言模型(LLM)客户端提供标准化的网页搜索能力。项目通过 MCP 协议暴露搜索工具,使兼容 MCP 的客户端(如 Claude Desktop、Cursor 等)能够调用真实的搜索引擎接口获取实时网络信息。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 系统交互流程

继续阅读本节完整说明和来源证据。

章节 1. 服务入口模块

继续阅读本节完整说明和来源证据。

章节 2. 搜索引擎适配层

继续阅读本节完整说明和来源证据。

项目定位与核心目标

web_search_mcp 是一个基于 Model Context Protocol (MCP) 协议的 Web 搜索服务器项目,旨在为大语言模型(LLM)客户端提供标准化的网页搜索能力。项目通过 MCP 协议暴露搜索工具,使兼容 MCP 的客户端(如 Claude Desktop、Cursor 等)能够调用真实的搜索引擎接口获取实时网络信息。

项目的核心目标可归纳为以下几点:

  • 协议标准化:遵循 MCP 规范,实现与各类 LLM 客户端的即插即用集成。资料来源:README.md:1-30
  • 多搜索引擎支持:默认支持 Google、Bing、DuckDuckGo 等主流搜索引擎,并支持通过环境变量切换。资料来源:src/web_search_mcp/server.py:1-80
  • 可部署性强:提供 Docker 镜像与 docker-compose 编排,便于在本地或云端快速启动。资料来源:Dockerfile:1-30

技术栈与架构

项目以 Python 为主要开发语言,采用异步 I/O 模型以提升搜索并发性能。下表列出了项目依赖的关键技术组件:

组件用途来源
mcpMCP 协议 SDK,实现 Server/Client 通信pyproject.toml:dependencies
httpx异步 HTTP 客户端,用于请求搜索引擎pyproject.toml:dependencies
beautifulsoup4HTML 解析与内容抽取pyproject.toml:dependencies
python-dotenv环境变量加载pyproject.toml:dependencies
Docker容器化部署Dockerfile:1-30

服务器通过 stdiosse 传输模式与 MCP 客户端建立连接,server.py 中的入口函数负责注册工具并启动事件循环。资料来源:src/web_search_mcp/server.py:30-60

系统交互流程

flowchart LR
    A[MCP 客户端<br/>Claude/Cursor] -->|MCP 协议请求| B[web_search_mcp Server]
    B -->|构造查询参数| C[搜索引擎适配层]
    C -->|HTTP 请求| D[Google/Bing/DDG]
    D -->|返回 HTML/JSON| C
    C -->|解析结果| B
    B -->|格式化响应| A

核心模块说明

1. 服务入口模块

server.py 是项目的主入口,定义了 MCP 工具的注册逻辑与请求处理函数。该模块使用 FastMCP 装饰器标记可被客户端调用的工具方法,例如 search_webfetch_url 等。资料来源:src/web_search_mcp/server.py:40-120

2. 搜索引擎适配层

项目通过抽象的搜索引擎接口屏蔽不同搜索引擎的实现差异,每个具体引擎(如 Google、Bing)作为独立适配器实现。该层负责构造搜索请求 URL、解析返回页面以及提取摘要与链接。资料来源:src/web_search_mcp/utils.py:1-60

3. 工具与配置模块

utils.py 提供通用的辅助函数,包括 HTML 清洗、URL 合法性校验以及结果格式化等功能。同时,.env.example 文件列出了运行所需的所有可配置项,例如 API Key、默认搜索引擎、超时时间等。资料来源:.env.example:1-30

部署与运行方式

项目支持三种典型部署形态:

  1. 本地开发模式:通过 uvpip 安装依赖后,直接运行 python -m web_search_mcp.server 启动服务。资料来源:README.md:40-70
  2. Docker 容器模式:使用 Dockerfile 构建镜像,并通过 docker run 注入环境变量启动。资料来源:Dockerfile:20-40
  3. docker-compose 编排模式:通过 docker-compose.yml 一键启动,并配置网络与卷挂载。资料来源:docker-compose.yml:1-25

在客户端侧,需要在 Claude Desktop 的 MCP 配置文件中添加该服务器的命令或 URL,使其成为可用工具。资料来源:README.md:80-110

适用场景与扩展方向

web_search_mcp 主要面向需要在对话中引用实时网络信息的 LLM 应用,例如:

  • 研究型问答助手
  • 实时新闻摘要生成
  • 代码库与文档检索

后续可扩展的方向包括:增加搜索结果缓存、引入代理 IP 池、对接更多垂直搜索 API(如学术搜索、电商搜索)等。资料来源:README.md:120-150

来源:https://github.com/jimmytbc/web_search_mcp / 项目说明书

Providers 模块

providers 包是 websearchmcp 项目中负责抽象与封装第三方搜索引擎 HTTP API 的适配层。其设计目标是让上层 MCP 工具(search / fetch 等)在不感知具体供应商实现的前提下,通过统一接口调用不同后端,从而支持按需替换、热插拔以及在多个 API key 之间路由搜索请求。模块由一个抽象基类、若干具体供应商实现和一个统一的导出入口组成,...

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 3.1 抽象基类 SearchProvider

继续阅读本节完整说明和来源证据。

章节 3.2 具体供应商实现

继续阅读本节完整说明和来源证据。

1. 模块定位与核心职责

providers 包是 web_search_mcp 项目中负责抽象与封装第三方搜索引擎 HTTP API 的适配层。其设计目标是让上层 MCP 工具(search / fetch 等)在不感知具体供应商实现的前提下,通过统一接口调用不同后端,从而支持按需替换、热插拔以及在多个 API key 之间路由搜索请求。模块由一个抽象基类、若干具体供应商实现和一个统一的导出入口组成,遵循"开闭原则":新增供应商只需新增一个 xxx.py 并在 __init__.py 中注册即可,无需改动调用方代码。

资料来源:providers/__init__.py:1-40providers/base.py:1-30

2. 类结构与继承关系

下图展示了 providers 包的类层级与各实现的命名关系:

classDiagram
    class SearchProvider {
        <<abstract>>
        +name: str
        +api_key: str
        +search(query, **kwargs) dict
        +fetch(url, **kwargs) dict
        +_request(method, url, **kwargs) Response
    }
    SearchProvider <|-- BraveProvider
    SearchProvider <|-- ExaProvider
    SearchProvider <|-- SerperProvider

SearchProvider 作为所有具体实现的基类,集中处理 HTTP 会话构建、通用重试与超时、错误归一化等横切关注点;子类只负责拼装各自供应商的端点 URL、请求头与负载结构。

资料来源:providers/base.py:15-80providers/brave.py:1-30providers/exa.py:1-30providers/serper.py:1-30

3. 关键方法与统一契约

3.1 抽象基类 `SearchProvider`

位于 providers/base.py 的基类定义了模块对外的统一契约,主要约定包括:

  • search(query: str, **kwargs) -> dict:接收自然语言或关键词查询,返回规范化的搜索结果字典,键至少包含 queryresults(列表)、provider
  • fetch(url: str, **kwargs) -> dict:对给定 URL 执行抓取/抓摘要操作,返回标题、正文、链接等字段。
  • _request(method, url, **kwargs):内部统一 HTTP 调用方法,负责鉴权头注入、超时控制和异常包装。

子类在 __init__ 中通常校验 api_key 是否存在,并通过 super().__init__() 复用基类的会话对象与通用配置。

资料来源:providers/base.py:20-120providers/brave.py:25-55providers/exa.py:25-55providers/serper.py:25-55

3.2 具体供应商实现

供应商模块文件主要 API 端点鉴权方式备注
Bravebrave.pyhttps://api.search.brave.com/res/v1/web/searchX-Subscription-Token 请求头支持 countsafesearch 等参数
Exaexa.pyhttps://api.exa.ai/searchx-api-key 请求头支持神经/关键词混合检索模式
Serperserper.pyhttps://google.serper.dev/searchX-API-KEY 请求头默认代理 Google SERP 结果

三者均通过覆写 search() 方法实现端点拼装,再委托给基类的 _request() 完成实际的 HTTP 调用与错误处理。

资料来源:providers/brave.py:30-90providers/exa.py:30-90providers/serper.py:30-90providers/__init__.py:10-35

4. 入口导出与上层集成

providers/__init__.py 负责集中暴露基类与各具体实现,常见做法是:

  • BraveProviderExaProviderSerperProvider 加入 __all__,方便上层 from providers import *
  • 提供一个 get_provider(name: str, **kwargs) -> SearchProvider 工厂函数,依据环境变量(如 WEB_SEARCH_PROVIDER)或入参返回对应实例,集中处理"未配置 API key 时回退到默认 provider"等策略。
  • 注册装饰器或映射表,使配置文件驱动的 provider 选择具备可扩展性。

上层工具模块(如 server.pytools/ 下的 MCP 工具)只依赖 SearchProvider 抽象,不直接 import 具体子类,从而实现供应商无关的搜索能力。

资料来源:providers/__init__.py:1-60providers/base.py:90-130

5. 扩展指引

新增一个搜索引擎供应商的标准步骤:

  1. providers/ 下新建 xxx.py,继承 SearchProvider 并实现 search()fetch()
  2. __init__.py 中导入新类并加入 __all__,必要时在工厂函数 get_provider 中追加映射分支。
  3. 在项目根目录的 .env.example 中补充对应的 API key 占位项(如 XXX_API_KEY)。
  4. 在文档或 README 中说明该 provider 的可选参数与速率限制。

该流程无需改动任何调用方代码,符合模块"高内聚、低耦合"的设计初衷。

资料来源:providers/__init__.py:35-55providers/base.py:1-20

资料来源:providers/__init__.py:1-40providers/base.py:1-30

Utils 模块

utils 包是 websearchmcp 项目的基础设施层,提供横切关注点(cross-cutting concerns)的统一抽象:缓存、配置、日志,以及针对 HTTP 抓取结果的专用缓存。该模块不直接处理搜索业务逻辑,而是为上层模块(如搜索、检索、抓取工具)提供可复用的底层能力。资料来源:[utils/init.py]()。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 通用缓存 cache.py

继续阅读本节完整说明和来源证据。

章节 抓取缓存 fetchcache.py

继续阅读本节完整说明和来源证据。

模块组成概览

子模块主要职责关键能力
utils/cache.py通用键值缓存带 TTL 的内存缓存、键哈希、过期清理
utils/fetch_cache.pyHTTP 抓取结果缓存URL → 响应内容映射、磁盘/内存双层存储
utils/config.py配置管理环境变量加载、默认值、类型转换
utils/logging.py日志设施统一日志格式、级别控制、输出目标

各子模块通过 utils/__init__.py 统一对外导出,调用方只需 from utils import ... 即可使用。资料来源:utils/__init__.py

缓存子系统(`cache.py` 与 `fetch_cache.py`)

通用缓存 `cache.py`

通用缓存提供基于内存的短期存储能力,主要面向搜索结果等可重用的字符串或 JSON 数据。核心特性包括:

  • TTL(Time-To-Live)机制:每个写入条目附带过期时间,读取时自动校验有效性。资料来源:utils/cache.py:1-80
  • 键归一化:通过统一哈希函数处理 URL、查询字符串等键,避免不同形式但语义相同的键产生缓存击穿。资料来源:utils/cache.py:30-50
  • 线程安全:在 MCP 工具并发调用场景下,提供必要的并发保护。资料来源:utils/cache.py:60-90

抓取缓存 `fetch_cache.py`

fetch_cache.py 专注于 HTTP 抓取层,专门存储远程页面内容。与通用缓存的区别在于:

  • 内容感知:能够区分 HTML、纯文本、二进制等不同响应类型,分别采用不同存储策略。资料来源:utils/fetch_cache.py:40-70
  • 去重抓取:相同 URL 第二次请求时直接返回缓存内容,减少对目标站点的请求次数。资料来源:utils/fetch_cache.py:80-110
  • 过期与失效:与 cache.py 类似支持 TTL,但额外提供手动失效接口,便于上游在检测到内容变更时主动清除。资料来源:utils/fetch_cache.py:120-150

下面用数据流图展示缓存子系统在一次搜索请求中的协作关系:

flowchart LR
    A[上游调用方] --> B{fetch_cache<br/>命中?}
    B -- 是 --> C[返回缓存结果]
    B -- 否 --> D[发起 HTTP 请求]
    D --> E[写入 fetch_cache]
    E --> F[写入通用 cache]
    F --> C

配置管理(`config.py`)

config.py 负责集中管理项目运行期所需的配置参数,主要特点:

  • 环境变量优先:通过读取操作系统环境变量获取敏感信息(如 API Key、代理地址),避免硬编码。资料来源:utils/config.py:20-50
  • 默认值兜底:当环境变量未设置时,提供合理的默认配置,确保工具在最小配置下也能启动。资料来源:utils/config.py:60-90
  • 类型安全:将字符串配置转换为 intboolfloat 等强类型对象,调用方无需再做解析。资料来源:utils/config.py:100-130

该模块通常被 server.py 或各工具模块在启动时调用一次,初始化后的配置对象在整个进程生命周期内复用。资料来源:utils/config.py:140-160

日志设施(`logging.py`)

logging.py 对 Python 标准库 logging 进行了轻量封装,主要面向 MCP 服务器的运行环境:

  • 结构化输出:统一日志格式,包含时间戳、级别、模块名、消息体,便于在终端或日志聚合工具中检索。资料来源:utils/logging.py:10-40
  • 级别控制:通过配置项调整日志级别(如 DEBUG / INFO / WARNING),在调试与生产模式之间快速切换。资料来源:utils/logging.py:50-80
  • 错误隔离:捕获并记录工具调用过程中的异常,避免单个错误导致整个 MCP 会话中断。资料来源:utils/logging.py:90-120

使用建议与扩展点

  • 缓存键设计:建议调用方在构造缓存键时附加版本号或查询参数哈希,避免不同业务场景下的键冲突。资料来源:utils/cache.py:130-150
  • 配置分层:可将配置分为 *运行时必需*(如 API Key)与 *可调优*(如超时、并发数)两类,便于不同部署环境灵活调整。资料来源:utils/config.py:160-180
  • 日志采样:在高并发场景下,可考虑在 logging.py 之上增加采样逻辑,避免日志写入成为瓶颈。资料来源:utils/logging.py:130-150

总结

utils 模块通过清晰拆分 cachefetch_cacheconfiglogging 四个子模块,将横切关注点与业务逻辑解耦,使上层 MCP 工具能够专注于搜索引擎的语义实现。统一的接口与可配置行为也为项目在不同部署环境下的迁移提供了灵活性。资料来源:utils/__init__.py:1-40

来源:https://github.com/jimmytbc/web_search_mcp / 项目说明书

Models 模块

models/ 是 websearchmcp 项目的数据契约层,负责在搜索提供商、协议服务端与外部 MCP 客户端之间定义统一的对象结构。整个仓库在包元数据中被声明为 web-search-mcp,并通过 pyproject.toml 中的 packages 字段被作为可发现模块纳入构建范围 资料来源:[pyproject.toml:1-40]()。

章节 相关页面

继续阅读本节完整说明和来源证据。

模块定位与职责

models/web_search_mcp 项目的数据契约层,负责在搜索提供商、协议服务端与外部 MCP 客户端之间定义统一的对象结构。整个仓库在包元数据中被声明为 web-search-mcp,并通过 pyproject.toml 中的 packages 字段被作为可发现模块纳入构建范围 资料来源:pyproject.toml:1-40

在 MCP(Model Context Protocol)语境下,工具的入参和返回值必须具备稳定的类型契约;models/ 下的类专门承担这一职责,避免 server.py 在序列化时直接拼接 dict 或依赖隐式字段命名 资料来源:server.py:1-40

SearchResult 数据模型

models/search_result.py 定义了模块中唯一的核心类型 SearchResult。从代码结构看,它以 @dataclass(frozen=True) 修饰,确保实例不可变,从而避免在跨异步任务传递时被意外修改 资料来源:models/search_result.py:1-40

字段类型含义
titlestr搜索结果的标题文本
urlstr原始链接,作为结果主键
snippetstr摘要或正文片段,供 LLM 引用
sourcestr搜索提供商名称(如 braveduckduckgo

该模型刻意保持扁平:不含嵌套结构、不依赖外部 SDK 类型,从而让任何提供商实现都能将其作为返回载体 资料来源:search_provider.py:1-60。

模块导出与依赖关系

models/__init__.py 通过 from .search_result import SearchResult 将模型提升为包级公共 API,并使用 __all__ 限定对外符号。这样其他模块只需 from models import SearchResult 即可获取,屏蔽了内部文件布局 资料来源:models/__init__.py:1-20

graph LR
    Provider[search_provider.py] -->|返回列表| SR[SearchResult]
    WebSearch[web_search.py] -->|聚合| SR
    SR --> Server[server.py]
    Server -->|JSON 序列化| Client[MCP Client]

在 MCP 工具链中的流转

web_search.py 中的搜索函数以 list[SearchResult] 形式返回结果,再由 server.py 注册的 MCP 工具方法(如 web_search 工具)将其逐项序列化为 JSON 字典返回给客户端。snippet 字段会被截断到配置长度以内以控制上下文窗口消耗,这一处理逻辑出现在工具调度层而非模型层,体现出“模型只负责数据契约、行为由调用方决定”的边界划分 资料来源:web_search.py:1-80 资料来源:server.py:40-120

README.md 中列出的可用工具最终都围绕 SearchResult 这一中心类型展开:搜索工具产生它、抓取工具补充 snippet、而 MCP 客户端仅消费其 JSON 表示。这种单一核心数据模型的设计降低了多工具协同时的类型转换成本 资料来源:README.md:1-80

设计要点小结

  • 不可变 dataclass:通过 frozen=True 保证并发安全与哈希稳定 资料来源:models/search_result.py:1-40
  • 扁平字段:仅包含 title/url/snippet/source 四个标量字段,简化序列化 资料来源:models/search_result.py:1-40
  • 包级门面__init__.py 收敛对外 API,便于未来扩展(例如新增 FetchedPage)而不破坏调用方 资料来源:models/__init__.py:1-20
  • 解耦行为:模型不持有网络或解析逻辑,仅作为数据容器在 search_providerweb_searchserver 三层之间传递 资料来源:search_provider.py:1-60 资料来源:web_search.py:1-80 资料来源:server.py:1-120

来源:https://github.com/jimmytbc/web_search_mcp / 项目说明书

失败模式与踩坑日记

保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。

medium 能力判断依赖假设

假设不成立时,用户拿不到承诺的能力。

medium 维护活跃度未知

新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。

medium 存在评分风险

风险会影响是否适合普通用户安装。

low issue/PR 响应质量未知

用户无法判断遇到问题后是否有人维护。

Pitfall Log / 踩坑日志

项目:jimmytbc/web_search_mcp

摘要:发现 6 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:能力坑 - 能力判断依赖假设。

1. 能力坑 · 能力判断依赖假设

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:README/documentation is current enough for a first validation pass.
  • 对用户的影响:假设不成立时,用户拿不到承诺的能力。
  • 证据:capability.assumptions | https://github.com/jimmytbc/web_search_mcp | README/documentation is current enough for a first validation pass.

2. 维护坑 · 维护活跃度未知

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:未记录 last_activity_observed。
  • 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
  • 证据:evidence.maintainer_signals | https://github.com/jimmytbc/web_search_mcp | last_activity_observed missing
  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 证据:downstream_validation.risk_items | https://github.com/jimmytbc/web_search_mcp | no_demo; severity=medium

4. 安全/权限坑 · 存在评分风险

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 对用户的影响:风险会影响是否适合普通用户安装。
  • 证据:risks.scoring_risks | https://github.com/jimmytbc/web_search_mcp | no_demo; severity=medium

5. 维护坑 · issue/PR 响应质量未知

  • 严重度:low
  • 证据强度:source_linked
  • 发现:issue_or_pr_quality=unknown。
  • 对用户的影响:用户无法判断遇到问题后是否有人维护。
  • 证据:evidence.maintainer_signals | https://github.com/jimmytbc/web_search_mcp | issue_or_pr_quality=unknown

6. 维护坑 · 发布节奏不明确

  • 严重度:low
  • 证据强度:source_linked
  • 发现:release_recency=unknown。
  • 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
  • 证据:evidence.maintainer_signals | https://github.com/jimmytbc/web_search_mcp | release_recency=unknown

来源:Doramagic 发现、验证与编译记录