15,465 个 MCP 服务器,零治理:Agent 供应链的缺口有多大

·阅读约10分钟·Evergreen Tools Team

2026 年 9 月 24 日,OX Security 发布研究,分析了三个公开注册表中的 15,465 个已发布 MCP 服务器,去重后得到 5,095 个唯一主机名用于基础设施分析。四类发现值得逐条记住:15.6% 的主机名(796 个)解析到美国之外,其中 19 个在中国、18 个在俄罗斯;约 0.45% 的服务器走家用网络与消费级隧道服务;2.3% 的主机名已不再解析,其中 6 个域名未注册、每年 4 至 12 美元即可被他人买下并冒充原服务器;以及在一次针对 Claude Code 配 Haiku 3.5 的测试中,一次 always-allow 授权被复用去读取敏感文件(含 .env),没有再弹窗。本文梳理这四类发现,并给出收紧自身 MCP 足迹的做法。

一、数据集:15,465 个服务器,5,095 个主机名

先看研究的方法,因为结论的可信度取决于样本。OX Security Research 从三个公开注册表——mcp-official-registry、cline-marketplace 与 github-mcp-registry——抓取了 15,465 个已发布的 MCP 服务器,随后为做基础设施分析,把数据集收敛到 5,095 个唯一主机名。去重这一步很关键:同一个端点可能出现在多个注册表里,不去重就会把噪声当成规模。研究得出的结论是,MCP 在很多情况下就是「影子 AI 基础设施」:它由 AI Agent、开发者工作站与自动化流水线按需连接第三方服务器,从而绕开了企业过去十年围绕云建立的治理。示例 1 用一段最短的代码表达了这套清点逻辑。

# Finding 1: the dataset. MCP servers are published across multiple public
# registries. Deduplicate before you reason about infrastructure, because the
# same endpoint can appear in several places.

REGISTRIES = ["mcp-official-registry", "cline-marketplace", "github-mcp-registry"]

def build_inventory(registries):
    servers = [s for r in registries for s in fetch(r)]
    hostnames = {host(s.url) for s in servers if s.url}
    return {
        "servers": len(servers),              # 15,465 published servers
        "unique_hostnames": len(hostnames),   # 5,095 unique hostnames
        "by_registry": {r: len(fetch(r)) for r in registries},
    }
无国界的 MCP 基础设施

15.6% 的主机名解析到美国之外

二、发现一:无国界的基础设施

第一类发现直指数据驻留。分析的主机名中,15.6%(5,095 个里的 796 个)解析到美国之外,其中 19 个在中国、18 个在俄罗斯。问题的要害在于协议本身:MCP 在协议层没有「地理区域」这个概念。企业可以对自己的云工作负载强制严格的数据驻留要求,同时它的 AI Agent 又能自由连接到这些控制之外的服务器。这不是某个配置疏忽,而是治理边界的结构性错位——你把驻留规则写在了自己控制的云上,但 Agent 的连接路径并不经过这些规则。示例 2 给出一段按主机名解析与地理归类、把驻留风险量化的代码。

# Finding 2: infrastructure without borders. MCP has no protocol-level
# concept of geographic region, so an enterprise can enforce strict residency
# on its own workloads while its agents connect freely to servers outside
# those controls. Resolve every hostname and classify its location.

def residency(hostnames):
    out = {"outside_us": 0, "in_china": 0, "in_russia": 0, "unresolved": 0}
    for h in hostnames:
        ip = dns_resolve(h)
        if ip is None:
            out["unresolved"] += 1            # 2.3% no longer resolve
            continue
        country = geoip_country(ip)           # e.g. "CN", "RU", "US"
        if country != "US":
            out["outside_us"] += 1            # 796 of 5,095 = 15.6%
        if country == "CN":
            out["in_china"] += 1              # 19
        if country == "RU":
            out["in_russia"] += 1             # 18
    return out

三、发现二与发现三:消费级基础设施与废弃域名

第二类发现是「生产流量跑在消费级基础设施上」。研究识别出通过家用网络与消费级隧道服务代理的 MCP 服务器,约占数据集的 0.45%。这意味着生产 AI 工作流可能依赖没有可用性保证、没有企业访问控制、也无可真正审计性的基础设施——因为它本来就不是为做企业基础设施而建的。第三类发现是「废弃域名,现实风险」。2.3% 的主机名已不再解析;其中 6 个域名未注册、每年约 4 至 12 美元即可买下,而它们很可能仍被某人的配置或流水线引用。任何人买下它,就能冒充它曾指向的服务器,而仍然信任并调用该端点的客户端会照常连接。示例 3 把这套检测写成代码:解析失败应当被当作事故,而不是警告。

# Finding 3: abandoned domains, live risk. When a domain lapses, anyone can
# register it and impersonate the server it used to point to -- while stale
# configs and pipelines still trust and call that endpoint. Treat resolution
# failure as an incident, not a warning.

def domain_risk(hostnames, price_range=(4, 12)):
    risks = []
    for h in hostnames:
        if dns_resolve(h) is None:
            registrable = registrable_domain(h)
            if not is_registered(registrable):
                risks.append({
                    "host": h,
                    "domain": registrable,
                    "available_for": f"${price_range[0]}-${price_range[1]}/yr",
                    "impact": "anyone can impersonate the former server",
                })
    return risks   # 6 such domains in the study
被复用的信任决策

一次 always-allow 换来对 .env 的访问

四、发现四:比授权决策活得更久的信任

第四类发现最贴近日常。OX 测试了一个基于信任的提示注入,目标是 Claude Code 配 Haiku 3.5:一个恶意 MCP 服务器先请求访问一个无害文件,用户以 always-allow 批准;服务器随后请求敏感文件(含 .env),并成功获取,全程无需再次确认。同一攻击对 Opus 4.6 与 4.7 失败。厂商的回应,按 OX 的转述,大意是:一旦授予 always-allow,这就是文档化的行为;而模型层对恶意内容的检测是尽力而为的启发式,并非安全边界。这里的教训很朴素:任何持久授权都是一张「长期通行证」,你必须限定它的范围并让它过期。示例 4 把「按会话批准、每次重新询问」作为默认。

# Finding 4: trust that outlives the decision that granted it. A malicious
# MCP server asked for a harmless file, the user chose always-allow, and the
# server then read a sensitive file (.env among them) with no second prompt.
# Any persistent approval is a standing grant you must scope and expire.

def approve(request, scope="session"):
    if scope == "always":
        return {"decision": "always-allow",
                "warning": "this grant can be reused for ANY later request "
                           "the server makes, not just the one you saw"}
    return {"decision": "allow-once",
            "expires": "end-of-call",
            "re_prompt": "every future request"}

# Least privilege for MCP means per-call approval for anything touching
# secrets, credentials, or data outside the task's declared scope.

五、为什么这件事重要

把四类发现放在一起,问题就清楚了:这不是某个服务器的漏洞,而是治理模型与采纳速度之间的错位。企业十年、数千亿美元投入的数据驻留,究竟是真正的底线,还是只要 AI 足够便利就可以放弃的合规表演?OX 没有给出答案,但给出了一句判断:采纳与安全不是同一条轴,安全的生态不会自动从采纳中生长出来,它来自「有人决定在意」。对工程团队而言,这意味着 MCP 的接入需要像其他生产依赖一样被清点、分类与定期复核,而不是当作开发者个人环境里的一行配置。示例 5 给出一份可执行的加固清单与一个 CI 门禁函数。

# The practical fix set. Adoption and security are different axes, so make
# the secure path the default path in your agent config and CI.

HARDENING = [
    "inventory every MCP server in dev configs and production agents",
    "record owner, hostname and the data each server can reach",
    "remove stale endpoints and unpinned, unused servers",
    "ban blanket always-allow for secrets, credentials and company data",
    "recheck domain ownership, egress rules, credentials and logs",
]

def gate(server) -> list:
    fails = []
    if not server.pinned_version:  fails.append("unpinned server version")
    if server.resolves_outside_allowlist(): fails.append("egress outside allowlist")
    if not server.has_owner:       fails.append("no named owner")
    return fails
给 MCP 足迹做一次清点

先清点,再清理过期端点

六、收紧你的 MCP 足迹

最后是做法。第一,先清点:把开发者配置与生产 Agent 里的每一个 MCP 服务器都登记下来,记录主机名、负责人与它能访问的数据(示例 1、示例 5)。第二,再清理:移除过期端点与未固定版本的服务器,别让它们长期挂着「待用」。第三,禁止对密钥、凭证与公司数据的 blanket always-allow,改为按调用批准,并让授权过期(示例 4)。第四,重新核对:域名归属、出网规则、凭证与日志,尤其是那些访问公司数据的工具。第五,把解析失败当事故处理,而不是当噪音忽略(示例 3)。把这几步做进默认配置和 CI,MCP 带来的连接便利才不会以治理边界为代价。

📌 常见问题 FAQ

研究分析了什么数据?

OX Security 分析了三个公开注册表(mcp-official-registry、cline-marketplace、github-mcp-registry)中的 15,465 个已发布 MCP 服务器,去重后得到 5,095 个唯一主机名做基础设施分析。

「无国界的基础设施」指什么?

15.6% 的主机名(5,095 个中的 796 个)解析到美国以外,包括 19 个中国与 18 个俄罗斯。MCP 在协议层面没有地理区域的概念,企业可以对自己的云负载强制严格的数据驻留,但其 AI Agent 仍可自由连接到这些控制之外的服务器。

「废弃域名」的风险是什么?

2.3% 的主机名已不再解析,其中 6 个域名未注册、每年约 4 至 12 美元即可买下,且很可能仍被某些配置或流水线引用。任何人买下它,就能冒充它曾指向的服务器,而仍然信任并调用该端点的客户端或工作流会照常连接。

「被复用的信任」是什么意思?

OX 用 Claude Code 配 Haiku 3.5 测试了一个基于信任的提示注入:恶意 MCP 服务器先请求访问一个无害文件,用户以 always-allow 批准;服务器随后请求敏感文件(含 .env)并成功获取,无需再次确认。同一攻击对 Opus 4.6 与 4.7 失败。

厂商怎么回应?

据 OX 描述,Anthropic 的回应大意是:一旦授予 always-allow,这就是其文档化的行为;而模型层对恶意内容的检测是尽力而为的启发式,并非安全边界。