这篇适合谁,读完能做到什么
适合已经会写一点 Python、想让大模型能查到实时网页信息的开发者。读完你应该能把一个调用外部 API 的 MCP 服务器跑起来,知道它提供哪些工具,会用 SDK 写脚本调用它,并能在遇到 SERPAPI_KEY 找不到、401 等常见报错时自己定位问题。
它做什么
这一课要做的是一个 MCP 服务器和客户端,用 SerpAPI 给大模型补充实时网页数据[1]。大模型本身的知识有截止时间,接入搜索之后,它就能查到更新的信息。服务器用 FastMCP 实现,提供四个工具[1]:
general_search:通用网页搜索,返回整理好的结果,参数是字符串query[1]。news_search:搜索与关键词相关的最新新闻,参数是query[1]。product_search:搜索符合关键词的商品,参数是query[1]。qna:从搜索引擎获取问题的直接答案,参数是question[1]。
这四个工具共用同一个 SerpAPI 账号,因此一个密钥的额度会被它们共同消耗。服务器还要负责把 API 的返回结果解析成适合大模型读取的结构化格式[1]。
一次搜索的流程大致是:客户端发出工具调用请求,服务器接收请求后调用 SerpAPI 获取结果,再把结果解析成结构化数据返回给客户端[1]。客户端和模型拿到的是整理过的数据,而不是原始的 API 响应,这样模型更容易读懂,也更容易控制返回的长度。
准备:Python 与 SerpAPI 密钥
需要 Python 3.8 或更高版本,以及一个 SerpAPI 的 API 密钥。SerpAPI 提供免费套餐,注册后即可获得密钥[1][2]。免费额度用完后,请求可能会失败,这一点会在后面的报错部分再次提到。
安装与配置
依赖列在 requirements.txt 里。推荐用 uv 安装,也可以用 pip[1]:
# Using uv (recommended)
uv pip install -r requirements.txt
# Using pip
pip install -r requirements.txt接着在项目根目录创建 .env 文件,写入 SERPAPI_KEY=your_serpapi_key_here,把占位符换成你自己的密钥[1]。密钥只放在 .env 里,不要写进代码。
启动服务器和客户端
第一次运行,建议先执行普通模式。它会自动启动服务器并跑完所有工具的测试;如果测试都通过,说明依赖、密钥和启动方式基本没问题。确认之后,再进入交互模式,用不同的查询摸索每个工具的返回内容。免费额度有限,测试时不要在循环里反复调用,以免很快用完额度。
服务器以标准输入输出(stdio)方式运行,客户端可以直接连接它。启动命令是 python server.py[1]。
客户端 client.py 有两种模式[1]:
- 普通模式:自动运行测试,依次调用所有工具并检查返回结果。想快速确认服务器正常,用这个模式。
- 交互模式:提供菜单,可以选择工具、输入自定义的查询,并实时看到结果。想试不同的输入,用这个模式。
运行 python client.py 会自动启动服务器并执行测试;加上 --interactive 参数,即 python client.py --interactive,则进入交互菜单[1]。
服务器怎么定义工具
服务器的骨架如下。FastMCP 创建服务器对象,@mcp.tool() 把异步函数注册为工具,最后用 mcp.run() 启动[1]:
# server.py (excerpt)
from mcp.server.fastmcp import FastMCP, Context
mcp = FastMCP("WebSearchServer")
@mcp.tool()
async def general_search(query: str, num_results: int = 5, ctx: Context = None) -> str:
# ...implementation (see server.py)...
raise NotImplementedError
if __name__ == "__main__":
mcp.run()这段代码是摘录,里面的实现部分要以完整的 server.py 为准。需要注意,摘录里的 general_search 声明了 num_results 参数,默认值为 5,但工具文档里只列出了 query 一个参数[1]。调用时以 server.py 的实际定义为准。
服务器还用 MCP 的 Context 对象记录日志、跟踪请求[1]。
用 SDK 写一个调用脚本
想在自己的脚本里调用工具,可以用 MCP Python SDK。下面的例子通过标准输入输出启动服务器,初始化会话,然后调用 general_search[1]:
# Example usage of the general_search tool
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def run_search():
server_params = StdioServerParameters(
command="python",
args=["server.py"],
)
async with stdio_client(server_params) as (reader, writer):
async with ClientSession(reader, writer) as session:
await session.initialize()
result = await session.call_tool("general_search", arguments={"query": "open source LLMs"})
print(result)这种脚本适合几类场景:试验新的查询、检查服务器对不同输入的反应、重复调用工具做自动化,或者在它之上搭自己的流程[1]。
常见报错
报错信息大多来自三个地方:Python 环境(缺包)、.env 配置(缺密钥)和外部服务(密钥无效或额度用完)。先判断报错属于哪一类,再动手修,能省掉很多时间。
最常见的问题有四个[1]:
SERPAPI_KEY environment variable not found:程序找不到密钥。在项目根目录的.env文件里添加SERPAPI_KEY=...即可。ModuleNotFoundError: No module named 'httpx':依赖没有装全。运行pip install -r requirements.txt。Error during client execution:多半是客户端连不上服务器。检查server.py是否在正确的目录里运行,检查客户端和服务器版本是否兼容,然后重启两边。Search API returned error status: 401:SerpAPI 密钥缺失、写错或已过期。到 SerpAPI 控制台核对密钥,同时确认免费额度是否已经用完。
调试模式
默认情况下,程序只输出重要信息。遇到难查的问题,可以把日志级别调到 DEBUG。在 client.py 或 server.py 顶部加入下面的配置[1]:
# At the top of your client.py or server.py
import logging
logging.basicConfig(
level=logging.DEBUG, # Change from INFO to DEBUG
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)DEBUG 输出会多出 httpx 的 HTTP 请求和响应记录,能看到每次请求发生了什么[1]。排查完记得改回 INFO,避免日志过多。
限制和常见问题
- 通过 stdio 运行时,标准输出承载的是协议消息。服务器代码里随意使用
print输出内容,可能干扰客户端和服务器之间的通信,调试信息应当交给日志模块处理。 - API 有调用频率限制。SerpAPI 这类服务会限制一段时间内的请求次数,代码应该检查这些限制,并优雅地处理超限,而不是直接崩溃[1]。
- 工具参数要做校验,包括设置默认值和确认类型正确,这样能减少错误和歧义[1]。
- 搜索结果是外部内容,要注意它的时效和来源,判断结果的可信度需要另外考虑。
- 摘录中的
num_results与工具文档不一致的问题,前文已经提过,改代码前先核对。
下一步
接下来是本章的实时流式传输(Real Time Streaming)[1]。想进一步了解 SerpAPI 的服务内容和套餐,可以去它的官网查看[2]。
本文改编自 Microsoft 开源课程《MCP for Beginners》的联网搜索实验[1],原文以 MIT 许可证发布。
来源
- 1Lesson: Building a Web Search MCP ServerMicrosoft mcp-for-beginners (MIT)
- 2SerpAPISerpAPI
引用本页: 写一个联网搜索 MCP 服务器. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-web-search. 更新于 2026-11-14