周一至周五 09:00 - 18:00(UTC+08:00) ©2026 NST LABS TECH LTD. 保留所有权利。
如何使用 FastMCP-2026 构建 MCP 服务器的逐步指南
Marcus Chen Product & Network Architect
如何使用 FastMCP 构建 MCP 服务器(带有真实抓取支持的工具)
TL;DR
FastMCP 是将 Python 函数转变为 MCP 工具的最快方式。 在一个类型化函数上使用单个 @mcp.tool 装饰器生成 MCP 客户端所需的 JSON schema,无需手动处理协议。
通过一个命令安装:pip install fastmcp。 本教程直接从 PyPI 安装并验证了 FastMCP 3.4.7。
一个最小化的服务器只需要三行实际代码。 创建一个 FastMCP 实例,用 @mcp.tool 装饰一个函数,并调用 mcp.run()。
FastMCP 服务器可以在没有单独客户端进程的情况下进行测试。 fastmcp.Client 类可以直接连接到同一 Python 进程中的服务器对象,本教程使用此功能来验证每个示例实际上都可以运行。
一个仅仅加两个数字的工具不值得建立一个服务器。 本教程中的示例将 FastMCP 工具连接到 Nstproxy Crawl,以便 MCP 客户端可以交付一个 URL 并返回清理后的 Markdown。
从工具调用真实的外部 API 意味着处理实际的失败模式。 缺失的凭证、非 200 响应和网络错误都需要明确处理 — 本教程展示了当 API 密钥未配置时捕获的实际错误输出。
FastMCP 是官方 MCP Python SDK 的超集,而不是其竞争者。 FastMCP 1.0 已经被合并入官方 SDK;在 PyPI 上积极开发的 fastmcp 包是 FastMCP 2.x,它增加了本教程所依赖的开发者体验层。
引言:为什么 FastMCP 是进入 MCP 服务器的实际切入点
FastMCP 将一个类型化的 Python 函数转换为 AI 代理可以调用的工具,而无需手动编写 JSON-RPC 消息处理。模型上下文协议(MCP)定义了 AI 应用程序(如 Claude Desktop、IDE 助手或自定义代理)如何发现和调用由单独的“ MCP 服务器”进程暴露的工具、资源和提示。手动实现该协议意味着在一个工具实际有用之前,需要编写 schema 生成、消息路由和传输组件。
FastMCP 消除了这些组件。您编写一个普通的 Python 函数,添加类型提示,使用 @mcp.tool 装饰它,FastMCP 将生成该工具的 schema,处理 JSON-RPC 消息循环,并通过您选择的任何传输方式公开它。官方的 模型上下文协议文档 将 MCP 本身描述为 “一个将 AI 应用程序与外部系统连接的开源标准” — FastMCP 是将该连接的服务器端构建得迅速的 Python 框架,其源代码在 官方 FastMCP GitHub 仓库 下以 Apache-2.0 许可证发布。
本教程实际安装 FastMCP,运行一个最小服务器,然后构建代理实际需要的东西:一个通过 API 获取 URL 并返回清理后的 Markdown 给调用代理的 MCP 工具。下面的每个代码块都是在真实的 Python 环境中执行的;在需要凭证而本文章无法提供的步骤中,那个缺口被披露,而不是用虚构的响应掩盖。
安装 FastMCP 在 Python 3.10+ 环境中使用 pip install fastmcp(如果你的项目使用 uv,则使用 uv add fastmcp)安装 FastMCP。此教程运行了:
pip install fastmcp requests --break-system-packages
在一个沙盒的 Linux 环境中,并通过 pip show fastmcp 确认安装,该命令返回:
Name: fastmcp
Version: 3.4.7
Summary: 构建 MCP 服务器和客户端的快速 Pythonic 方法。
该版本与 FastMCP 的 PyPI 页面 在写作时列出的当前版本相符。requests 是与 FastMCP 一起安装的,因为本教程后面的工作示例向 Nstproxy Crawl API 发起了外部 HTTP 调用。状态: 已实时运行 — 这是用于撰写本文的沙盒实际输出,而不是复制的版本号。
配置一个最小的 FastMCP 服务器 FastMCP 服务器从一个名为服务器并持有你在其上注册的每个工具的单个 FastMCP 实例开始。创建一个名为 hello_server.py 的文件:
from fastmcp import FastMCP
mcp = FastMCP ( "Hello MCP Server" )
@mcp . tool
def greet ( name : str ) - > str :
"""通过名字问候用户。"""
return f"你好, { name } !"
if __name__ == "__main__" :
mcp . run ( )
@mcp.tool 装饰器读取函数的类型提示(name: str 作为输入,str 作为输出)及其文档字符串,然后构建 MCP 客户端用于了解如何调用 greet 及期望的返回内容的 JSON schema — 你无需手动书写该 schema。mcp.run() 不带参数启动服务器,通过 stdio 传输,这是 MCP 客户端像 Claude Desktop 用于作为子进程启动本地服务器的默认传输模式。状态: 仅配置 — 本节完全针对此框架,下一节将进行实际测试。
基本实现:运行和调用服务器 运行 python hello_server.py 启动服务器并阻塞,等待 MCP 客户端通过 stdio 连接 — 在该模式下没有可见输出,这是设计使然,因此有用的验证步骤是通过客户端调用它,而不是盯着一个阻塞的终端。FastMCP 自己的 Client 类可以直接在同一 Python 进程中连接到服务器对象,这是在连接真实 MCP 客户端之前确认工具实际上有效的最快方法:
import asyncio
from fastmcp import Client
from hello_server import mcp
async def main ( ) :
async with Client ( mcp ) as client :
tools = await client . list_tools ( )
print ( "工具:" , [ t . name for t in tools ] )
result = await client . call_tool ( "greet" , { "name" : "Nstproxy" } )
print ( "结果:" , result . data )
if __name__ == "__main__" :
asyncio . run ( main ( ) )
工具: ['greet']
结果: 你好,Nstproxy!
状态: 已实时运行 — 这是从一起执行这两个文件中捕获的真实标准输出,确认工具注册、schema 生成和调用路径在外部 API 介入之前都能正常工作。
要将同一服务器作为独立进程运行,而不是作为内置客户端,可以直接调用它(python hello_server.py,std.io 传输)或通过 HTTP 启动以进行远程访问:
mcp . run ( transport = "http" , port = 8000 )
The FastMCP CLI 提供了相同的选项而无需编辑文件: fastmcp run hello_server.py:mcp 用于 stdio,或是 fastmcp run hello_server.py:mcp --transport http --port 8000 用于 HTTP。CLI 直接导入服务器对象,并不执行 if __name__ == "__main__": 块,因此当你仅通过 CLI 启动时,这个保护是可选的。状态:说明性 — 记载自官方 FastMCP 快速入门的传输语法,没有单独重新运行已经通过的 stdio 路径。
高级模式:赋予服务器一个值得称道的工具 一个可以相加两个数字的工具证明了装饰器的有效性,但这并没有给代理运行这个服务器的理由,而不是仅仅自己进行算术运算。实际上需要 MCP 服务器的场景是给代理提供一些它无法独自完成的任务 — 访问外部 API,获取页面,或读取它无法访问的文件系统。本教程的工作示例是一个 crawl_url 工具,它调用 Nstproxy Crawl 的单页抓取端点,并将页面内容返回为 Markdown,因此任何连接到这个服务器的 MCP 客户端都可以提交一个 URL,并获取 LLM 可以直接读取的文本。
Nstproxy Crawl 的单页抓取端点位于 POST https://api.nstproxy.com/api/v1/crawl/scrape,需要通过 x-api-key 头进行身份验证。调用该端点时不带额外参数,立即返回一个任务 ID,并将 status: "processing" 用于异步轮询;添加查询参数 async=true 则使得相同的端点等待并在一个响应中返回结果,这就是同步 MCP 工具调用需要的。请求体需要 url,一个 formats 数组(markdown,html,rawData,screenshot,pdf),可选的 timeout(以毫秒为单位),和 onlyMainContent 用于剥离导航和样板内容。响应包括一个 markdown 字段,其中包含清理后的内容,或者当结果太大不能内联时返回一个 markdownRef 令牌 — 通过 GET /api/v1/crawl/storage/read?st={ref} 单独解析。这些细节是直接根据 Nstproxy Crawl API 文档 确认的,而不是根据端点名称假设的。
import os
import requests
from fastmcp import FastMCP
mcp = FastMCP ( "Nstproxy Crawl MCP Server" )
NSTPROXY_API_KEY = os . environ . get ( "NSTPROXY_API_KEY" , "YOUR_API_KEY" )
CRAWL_ENDPOINT = "https://api.nstproxy.com/api/v1/crawl/scrape"
@mcp . tool
def crawl_url ( url : str ) - > str :
"""通过 Nstproxy Crawl 抓取一个 URL 并返回清理后的 Markdown。
需要设置 NSTPROXY_API_KEY。以 async=true 调用 Nstproxy Crawl 的
单页抓取端点以获得立即的同步风格响应。
"""
try :
response = requests . post (
CRAWL_ENDPOINT ,
params = { "async" : "true" } ,
headers = {
"x-api-key" : NSTPROXY_API_KEY ,
"Content-Type" : "application/json" ,
} ,
json = {
"url" : url ,
"formats" : [ "markdown" ] ,
"onlyMainContent" : True ,
"timeout" : 60000 ,
} ,
timeout = 65 ,
)
except requests . RequestException as exc :
return f"请求在收到响应之前失败: { exc } "
if response . status_code != 200 :
return (
f"Nstproxy Crawl 返回 HTTP { response . status_code } : "
f" { response . text [ : 500] } "
)
body = response . json ( )
if not body . get ( "success" , False ) :
return f"抓取请求未成功: { body } "
return body . get ( "data" , { } ) . get ( "markdown" , "(未返回 markdown 字段)" )
if __name__ == "__main__" :
mcp . run ( )
在 crawl_url 能够返回真实页面内容之前,你需要将自己的 Nstproxy Crawl API 密钥设置为 NSTPROXY_API_KEY 环境变量 — 如果没有,函数仍然会执行,仍然会以正确的模式注册为工具,并且仍会发起真实的外部请求,但该请求无法进行身份验证。将 FastMCP Client 连接到该服务器并列出其工具的方法,与 hello-world 示例完全相同:
在本文章的沙箱中调用 crawl_url 针对 https://example.com — 该沙箱没有配置 Nstproxy API 密钥,并且也有限制出站网络访问 — 产生了这个真实的、捕获的错误,而不是虚构的成功响应:
结果:请求在收到响应之前失败:HTTPSConnectionPool(host='api.nstproxy.com', port=443): 超过最大重试次数,网址为:/api/v1/crawl/scrape?async=true (由 ProxyError('无法连接到代理', OSError('隧道连接失败:403 禁止访问')) 引起)
状态:先决条件缺口 ,专门针对出站抓取调用——工具注册和crawl_url的模式生成实时运行 ,但实际页面抓取在此环境中无法完成,因为没有可供测试的真实 API 密钥,而本文未伪造 JSON 响应使示例看起来更完整。在具有有效 NSTPROXY_API_KEY 和开放网络访问的正常部署中,相同的代码路径会返回成功 Nstproxy 抓取响应中的 markdown 字段,而不是此错误字符串。
诚实的限制 FastMCP 处理模式生成、传输以及请求/响应循环,但它不处理工具函数内部发生的事情——那是普通的 Python,具有普通的失败模式。上面的 crawl_url 在每个路径中返回一个普通字符串,包括失败路径,因为 MCP 工具结果旨在可以被调用模型读取;而引发未捕获的异常则会表现为对客户端的通用工具调用失败,字符串中没有任何诊断细节。该工具的生产版本还应将 url 限制为预期的方案,设置比客户端自身耐心更短的请求超时,并明确决定是否应重试或将目标网站的 403/404(这两者在抓取的按抓取定价下都是计费的,因为抓取本身已完成)返回给调用者。
FastMCP 本身不管理 API 速率限制、重试或您工具调用的任何外部服务的身份验证——这一切都属于您工具自己的代码,正如上述所示。它还不验证工具返回内容的有效性,超出匹配您声明的类型提示,因此承诺 -> str 的工具返回格式错误的 Markdown 仍将通过 FastMCP 的检查;验证输出质量是工具作者的工作。
故障排除 运行但报告零工具的服务器通常意味着该函数从未被装饰,或是在与传递给 mcp.run() 的不同 FastMCP 实例上被装饰——检查每个 @mcp.tool 是否直接位于函数上方,并且每个文件中是否仅存在一个 FastMCP() 实例。可以列出工具但每次调用时出错的客户端通常是类型提示不匹配:如果模式承诺 int 而客户端发送无法强制转换的字符串,FastMCP 会在您的函数体运行之前拒绝该调用。当调用外部 API 的工具返回没有有用内容时,请单独检查响应状态和主体,正如上面的 crawl_url 所做——Nstproxy 抓取响应可以以正常的 HTTP 200 到达,而其 success 字段为 false,仅检查状态代码的代码会遗漏这一点。
结论 FastMCP 的全部价值在于缩短工作 Python 函数与 AI 代理可调用的工具之间的距离——本教程从空目录到具有真实外部 API 调用的服务器仅用了两个文件。从 pip install fastmcp 到注册且经过模式验证的工具仅需几分钟;更难、更有价值的部分在于代理调用该工具后,它实际做了什么,这就是为什么这里的工作示例连接到外部 API,而不是停在算术运算上。
对于其整个工作就是将 URL 转换为代理可读内容的工具,Nstproxy 抓取正是为这一交接而构建的。它是一个专注于 AI 的网页抓取 API,接受一个 URL 并返回干净、结构化的输出——Markdown、清理过的 HTML、链接、截图或 PDF——带有 JavaScript 渲染和 Nstproxy 自己的代理支持访问通过单个 API 调用处理,而不是要求您在工具函数内运行无头浏览器和代理池。它自然适用于像上面的 crawl_url 这样的 MCP 工具,因为两者都解决了同样的问题:获取模型可以读取的内容,而不让代理的代码负责浏览器自动化。
单次调用的 Markdown 输出 ——一个 POST 请求返回已转换为 Markdown 的页面内容,因此 MCP 工具函数仅需检查 success 并返回 markdown 字段,而无需进行自己的 HTML 到文本转换。
包含 JavaScript 渲染 ——在捕获响应之前,客户端构建其内容的页面全部渲染,因此调用抓取的工具不需要单独的无头浏览器依赖与 FastMCP 一起使用。
多页面工具的站点级抓取 ——除了这里的单页面 crawl_url 示例,同一账户可以调用 POST /api/v1/crawl 来在一个作业下抓取多个页面(明确设定 maxDepth 和 maxPages 限制),对于需要将整个网站部分而非单个 URL 交给代理的工具非常有用。
重试和大结果存储在服务器端处理 — 失败的获取会自动重试,超大的 Markdown/HTML/截图负载会以参考令牌返回,通过一个单独的存储读取调用解决,因此工具函数不需要拥有自己的重试循环或 blob 大小处理。
常见问题解答 问:FastMCP 和官方的 MCP Python SDK 是一样的吗?
不是 — FastMCP 1.0 合并到了官方的 MCP Python SDK 中,但在 PyPI 上积极维护的 fastmcp 包是 FastMCP 2.x,这是建立在该基础上的超集框架,具有额外的开发者体验特性,例如本教程中使用的进程内部 Client、身份验证帮助程序和专用 CLI。从 PyPI 安装 fastmcp 会获取 2.x 版本,而不是被吸收进基础 SDK 的代码。
只需在后半部分。hello_server.py 示例完全可以免费运行,无需外部账号,而本教程的 TOOLS: ['greet'] / RESULT: Hello, Nstproxy! 输出是在没有任何凭据的情况下捕获的。crawl_url 示例需要 Nstproxy Crawl API 密钥才能实际返回页面内容 — 如果没有,工具仍然注册并尝试请求,但调用失败,正如本文章捕获的错误输出所示。
问:我可以将 FastMCP 服务器与 Claude Desktop 或其他 MCP 客户端一起使用吗?
可以 — 任何支持通过标准输入输出启动本地服务器的 MCP 客户端,包括 Claude Desktop,都可以以相同的方式运行 FastMCP 服务器,就像它运行任何其他 MCP 服务器一样,通过指向其配置至 Python 文件,并让客户端管理该过程。本教程使用 FastMCP 自己的 Client 类验证了工具调用行为,这样在编写和测试工具时迭代速度更快,而不是在完全连接桌面客户端之前。
问:如果我的工具函数引发异常而不是返回字符串,会发生什么?
工具函数内部未捕获的异常会作为通用工具调用失败返回给调用的 MCP 客户端,而没有捕获并返回的错误字符串可以携带的具体诊断细节。本教程中的 crawl_url 示例故意捕获 requests.RequestException 并明确检查响应状态和 success 字段,而是返回每条路径上的描述字符串,而不是让异常传播。
问:FastMCP 是否处理我的工具调用的外部 API 的速率限制或重试?
不 — FastMCP 管理 MCP 协议层(模式生成、消息路由、传输),而不是工具函数的内部工作。速率限制、重试逻辑和超时处理等外部 API(如 Nstproxy Crawl)的管理应在您工具的代码中,正如上面的 crawl_url 设置自己的请求超时并检查响应体,而不是假设 FastMCP 处理它。
爬取您被授权访问的公共可访问页面是标准实践,但您仍然需要遵循目标站点的服务条款,遵守适用的 robots.txt,并且不使用爬取工具提取非公开或访问控制的内容。Nstproxy Crawl 是基于合法、允许的公共网络数据收集而构建的,而不是绕过身份验证或支付墙,您自己的工具函数在处理其检索到的数据时同样负有责任。
立即访问住宅、数据中心、IPv6 与 ISP 高质量代理池。 创建免费账号并立即试用 ->