Nstproxy 现已升级为 Nstdata。了解此次变化。
周一至周五 09:00 - 18:00(UTC+08:00) 公司地址
19H MAXGRAND PLAZA NO.3 TAI YAU STREET SAN PO KONG, KOWLOON HONG KONG ©2026 NST LABS TECH LTD. 保留所有权利。
MCP + Nstproxy 爬虫:为 Claude 构建一个网页搜索 MCP 服务器
Lena Zhou Growth & Integration Specialist
MCP + Nstdata 爬虫:为 Claude 构建一个网络搜索 MCP 服务器 TL;DR
网络搜索MCP服务器需要两个工具,而不是一个。 一种返回候选URL的搜索/发现工具,以及一种返回供LLM实际阅读的干净页面内容的抓取工具;大多数单一工具示例跳过了后半部分。
Nstdata Crawl适合这一模式的抓取部分。 它渲染JavaScript密集的页面,并通过Nstdata的代理网络路由请求,解决了普通HTTP请求在搜索结果页面面临的机器人检测和地理不一致问题。
构建服务器需要两个Python包和约50行代码。 本文版本是在撰写过程中安装、导入并针对真实的模型上下文协议(MCP)SDK运行的,包括测试客户端与服务器之间的实时标准输入输出握手。
Nstdata Crawl返回的是作为存储引用的Markdown,而不是内联文本。 工作集成必须通过第二次调用解决该引用,才能让LLM读取内容——这一细节在大多数快速入门示例中被跳过。
同一服务器通过相同的标准输入输出传输与Claude Desktop和Claude Code注册, 使用claude_desktop_config.json用于前者,使用claude mcp add用于后者。
每次搜索调用计费为一次Nstdata Crawl的结果页面抓取, 与每次页面读取调用分开。一次用户提问触发搜索加上三次页面读取的费用是四次抓取,而不是一次。
这种模式并不替代专用搜索API的查询理解或排名。 它是一种为代理添加实时网络访问的方法,利用您完全控制的抓取基础设施,包括每个请求使用的代理池和渲染设置。
此集成功能
模型上下文协议(MCP) 是一个开源标准,由Anthropic引入,现在广泛支持各种客户端和服务器,用于通过单一、一致的接口将AI应用程序连接到外部系统——数据源、工具和工作流程。单独的LLM无法搜索网络或打开URL;它只能处理其训练数据中的内容和您粘贴到对话中的任何文本。MCP服务器通过暴露可调用工具——具有名称、描述和类型架构的函数——来弥补这一空白,使主机应用程序如Claude Desktop或Claude Code能够列出、调用并将结果反馈回对话中。
如果您只想在五分钟内使用他人的基础设施使搜索工作,已经存在几个托管的、管理的网络搜索MCP服务器。构建自己的服务器以便利换取控制:您决定哪些页面进行JavaScript渲染,每个请求使用哪个代理池和地理目标,如何缓存结果,以及当目标站点阻止请求时如何处理。对于那些积极抵制普通HTTP抓取的网站,这种控制尤其重要,这包括现代网络的大多数内容以及搜索引擎结果页面本身——请参见Nstdata更广泛的网页抓取API比较 ,以了解Crawl的渲染和代理层与MCP环境以外的其他抓取后端的比较。
Nstdata Crawl是该构建用于填补这一空白的抓取层。它是一个面向AI的网络爬虫API:给它一个URL,它返回干净的Markdown、HTML、链接、截图或PDF输出,JavaScript渲染和Nstdata的代理网络负责底层传输。它专门为代理工具使用场景而构建——信息检索(RAG)摄取、监测,以及此服务器实现的搜索后阅读模式——而不是您自己组装和维护的抓取框架。对于MCP服务器而言,有三件事尤其重要:
JavaScript渲染 ——搜索结果页面和许多现代网站在客户端渲染其内容,因此普通的HTTP GET返回的是一个空壳;Crawl首先在真正的浏览器中执行页面。
代理支持的请求 ——每次抓取都通过Nstdata的代理网络路由,而不是调用机器的IP,这确保服务器在进行几次搜索引擎请求后不会被阻止。
存储引用的大输出 ——Markdown、HTML和其他大型字段以引用令牌的形式返回,您需要单独解析,这使得即使页面内容较长,初始响应也保持较小。
`NstDataClient.submit_scrape_task_sync` 会阻塞直到抓取完成,这就是为什么这两个工具函数都是普通的 `def` 而不是 `async def` 的原因——FastMCP 支持任意一种格式。`Format.LINKS` 和 `Format.MARKDOWN` 指示 Nstdata Crawl 返回页面的哪种表示;仅请求每个工具所需的内容可以使响应更小,从而使存储引用解析步骤成本更低。
快速浏览
如果您的代理的网络请求不断被阻止或被提供一个 JavaScript shell 而不是实际内容,Nstdata Crawl 会在一个真实的浏览器中呈现页面,并通过 Nstdata 的代理网络路由,然后将干净的 Markdown 交给您的 MCP 服务器。
前提条件 您需要 Python 3.10 或更高版本——官方 mcp 包需要它——一个兼容 MCP 的主机,例如 Claude Desktop 或 Claude Code,以及一个带有 Crawl API 令牌的 Nstdata 账号,注册后可在 Nstdata Crawl 产品页面 获取。基础 API 的完整端点和参数文档在 Nstdata 的 Crawl 文档 中,可以帮助您添加截图或 PDF 输出等选项,超出本示例所用的内容。这个构建不需要单独的搜索引擎 API 密钥:下面的 web_search 工具通过 Nstdata Crawl 获取搜索引擎自己的结果页面,而不是调用专用的搜索 API。
安装 创建一个虚拟环境并安装这个服务器所需的两个包:官方 MCP Python SDK 及其 CLI 附加组件,以及 Nstdata 的 Crawl SDK。
python3 -m venv .venv
source .venv/bin/activate
pip install "mcp[cli]" nstdata-ai-crawl
在撰写本文的过程中运行这个命令安装了 mcp 1.27.0 和 nstdata-ai-crawl 0.1.1,这在撰写时是每个包的当前版本;pip install 在运行时总是解析为最新版本。不需要其他依赖项——Crawl SDK 内部包含自己的 HTTP 客户端。
配置 将以下内容保存为 mcp_server.py。它定义了两个工具:web_search,该工具获取搜索引擎的结果页面并返回其中的链接,以及 read_page,该工具获取任何 URL 并返回其清理后的 Markdown 内容。
import os
from mcp . server . fastmcp import FastMCP
from nstdata_ai_crawl import Format , NstDataClient , ScrapeRequestDto
NSTDATA_API_TOKEN = os . environ [ "NSTDATA_API_TOKEN" ]
client = NstDataClient ( NSTDATA_API_TOKEN )
mcp = FastMCP ( "nstdata-web-search" )
def _extract_markdown ( result ) - > str | None :
"""从抓取结果中提取 Markdown 文本。
Nstdata Crawl 将 Markdown 作为存储引用 (markdownRef) 返回
而不是内联文本,因此每个结果都必须先通过
read_storage() 解析,然后大语言模型才能读取它。
"""
if not result . data or not result . data . markdownRef :
return None
return client . read_storage ( result . data . markdownRef ) . decode ( "utf-8" )
@mcp . tool ( )
def web_search ( query : str , max_results : int = 5 ) - > str :
"""在网络上搜索一个查询并返回候选结果 URL。
参数:
query: 搜索查询。
max_results: 返回的结果链接的最大数量(默认为 5)。
"""
result = client . submit_scrape_task_sync ( ScrapeRequestDto (
url = f"https://html.duckduckgo.com/html/?q= { query } " ,
formats = [ Format . LINKS ] ,
onlyMainContent = False ,
timeout = 60000 ,
) )
if not result . success or not result . data :
return f"搜索获取失败: { result . errorMessage or result . errorCode } "
links = [
link for link in ( result . data . links or [ ] )
if "duckduckgo.com" not in link
] [ : max_results ]
return "\n" . join ( f"- { link } " for link in links ) if links else "未找到结果."
@mcp . tool ( )
def read_page ( url : str ) - > str :
"""抓取一个 URL 并返回其清理后的 Markdown 内容。
参数:
url: 要抓取和读取的页面。
"""
result = client . submit_scrape_task_sync ( ScrapeRequestDto (
url = url ,
formats = [ Format . MARKDOWN ] ,
onlyMainContent = True ,
timeout = 60000 ,
) )
if not result . success or not result . data :
return f"无法读取页面: { result . errorMessage or result . errorCode } "
markdown = _extract_markdown ( result )
return markdown or "页面已抓取但未返回可读内容."
if __name__ == "__main__" :
mcp . run ( transport = "stdio" )
实际握手 在将其连接到实际主机之前,验证服务器是否真正正确支持 MCP,遵循官方 MCP 服务器构建指南 中的相同模式。官方 SDK 的客户端可以通过主机使用的相同 stdio 传输与服务器进行通信,因此您可以在没有 Claude Desktop 或 Claude Code 的情况下测试握手:
import asyncio
from mcp import ClientSession , StdioServerParameters
from mcp . client . stdio import stdio_client
async def main ( ) :
server_params = StdioServerParameters (
command = "python3" ,
args = [ "mcp_server.py" ] ,
env = { "NSTDATA_API_TOKEN" : "dummy-key-for-handshake-test" } ,
)
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 t in tools . tools :
print ( f"- { t . name } : required= { t . inputSchema . get ( 'required' ) } " )
asyncio . run ( main ( ) )
=== 现场 stdio 往返:tools.list ===
- web_search: required=['query']
- read_page: required=['url']
该输出是真正的客户端-服务器往返:客户端作为子进程启动了服务器,完成了 MCP initialize 握手,并通过 tools/list 接收了这两个工具的自动生成架构——这个步骤不需要 Nstdata API 调用,因为列出工具并不涉及网络。
附加功能 Claude Desktop 从 claude_desktop_config.json 中读取服务器定义(在 macOS 上为 ~/Library/Application Support/Claude/claude_desktop_config.json,在 Windows 上为 %AppData%\Claude\claude_desktop_config.json)。在 mcpServers 下添加服务器:
{
"mcpServers" : {
"nstdata-web-search" : {
"command" : "python3" ,
"args" : [ "/absolute/path/to/mcp_server.py" ] ,
"env" : {
"NSTDATA_API_TOKEN" : "your-nstdata-crawl-token"
}
}
}
}
claude mcp add --env NSTDATA_API_TOKEN = your-nstdata-crawl-token --transport stdio nstdata-web-search -- python3 /absolute/path/to/mcp_server.py
在撰写本文期间运行此确切命令,然后检查 claude mcp list,返回:
nstdata-web-search-test: python3 /path/to/mcp_server.py - ✔ 连接成功
✔ 连接成功 状态意味着 Claude Code 成功启动了服务器进程并完成了与之的 MCP 握手——这并不确认 Nstdata Crawl 令牌有效,因为该调用仅在实际调用工具时发生。如果后续工具调用失败并出现身份验证错误,首先要检查的是令牌本身,而不是 MCP 连接。
工作示例 一旦服务器附加,像“最近 MCP 规范中有什么变化,并总结一下顶尖结果”的请求会演变为:Claude 调用 web_search 查询关于 MCP 规范变化的内容,返回一些候选 URL,选择一个看起来权威的 URL,并在其上调用 read_page 来将实际页面内容拉入对话中,然后回答。第二次调用的返回值的示例形状如下:
{
"content" : [
{
"type" : "text" ,
"text" : "# Model Context Protocol Changelog\n\n## 2026-07-28\n\n- ..."
}
]
}
然后,Claude 将以与读取对话中任何其他内容相同的方式读取该 Markdown 文本,并且可以引用它的来源 URL,因为该 URL 在最初是 read_page 的参数。
限制 web_search 在这里获取一个搜索引擎的结果页面并返回原始链接;它不对结果进行排名,不理解查询意图,也不像一个专门构建的搜索API那样进行分页。"duckduckgo.com" not in link 过滤器是一个简单的启发式方法,用于排除搜索引擎自己导航链接,而不是一个强大的结果解析器——生产版本应该更精确地过滤,或者如果排名质量重要,则切换到一个为编程用途设计的搜索提供者。计费遵循Nstdata Crawl的每次提取模型:每次 web_search 调用和每次 read_page 调用都是一次单独计费的提取,因此一个用户的问题触发一次搜索和三次页面读取的费用为四次提取,而上面的代码没有缓存重复的查找。Nstdata Crawl 还将每个请求的超时限制在60秒,并在调用者超过其速率限制时返回HTTP 429,并附带Retry-After头信息;这个简单的例子没有实现重试或回退,因此生产版本应该添加这两者。最后,将这视为一个供代理进行有限的、按需查找的工具,而非批量抓取——尊重目标网站的服务条款和robots.txt,并保持请求量与实际用户问题成比例,而不是通过相同的凭证进行无人值守的抓取。
结论 一个正常工作的网络搜索MCP服务器归结为两个工具——一个发现候选URL,一个从特定URL获取干净内容——连接到一个能够真正突破现代网站对普通HTTP抓取的阻挡的提取层。Nstdata Crawl的JavaScript渲染和代理访问覆盖了该提取层,官方MCP Python SDK处理协议连接,且同一个服务器通过相同的标准输入输出传输与Claude Desktop和Claude Code连接。在将其接入主机之前,使用真实客户端验证握手,如果你的代理在每个问题上进行搜索和读取,则要注意每次提取的计费模型,并将此示例中的简单链接过滤视为起点,而非完成的搜索后端。
常见问题解答 MCP服务器是一个程序,它通过模型上下文协议向AI应用程序提供可调用的工具、数据或提示模板,使AI可以调用真实的函数,而不仅仅是生成文本。
问:我需要专门使用Nstdata Crawl,还是可以通过普通HTTP请求自行获取页面?
你可以使用任何HTTP客户端获取页面,但普通请求通常无法对JavaScript渲染的页面生效,并且会被检测到爬虫的网站阻止;Nstdata Crawl通过在真实浏览器中渲染页面并通过其代理网络路由请求来处理这两者。
问:这让Claude像搜索引擎一样搜索整个网络吗?
不。此示例中的web_search工具获取一个搜索引擎的结果页面并返回其上的链接;它不对网络本身进行索引,也不对结果进行排名,像一个专用搜索引擎那样。
费用取决于Nstdata Crawl当前的每次提取价格,详见Crawl产品页面 ,乘以你的代理实际执行的搜索和页面读取的次数——每次都是一次单独计费的提取。
问:我可以同时将这个服务器与Claude Code和Claude Desktop一起使用吗?
可以。两者都连接到同一个标准输入输出服务器;Claude Desktop从claude_desktop_config.json中读取服务器定义,而Claude Code通过claude mcp add注册它。
问:我的Nstdata API令牌会暴露给LLM吗?
不会。令牌存在于服务器进程的环境变量中,仅在mcp_server.py内部用于与Nstdata Crawl进行身份验证;LLM仅能看到工具的返回值,而永远看不到令牌本身。
Nstdata Crawl返回一个非成功结果,带有一个错误代码,该错误代码由web_search函数作为普通文本失败消息显示;这个简单的例子并没有自动重试,因此生产版本应该添加回退和后备查询策略。
问:我需要将这个服务器部署到某个地方,还是可以在本地运行?
它可以完全在你自己的机器上作为一个本地子进程运行,由Claude Desktop或Claude Code通过标准输入输出启动;如果你要通过HTTP将服务器暴露给远程主机,则只需要公共部署。
立即访问住宅、数据中心、IPv6 与 ISP 高质量代理池。 创建免费账号并立即试用 ->