mcpField
P
peering-mcp

peering-mcp

本地服务
github.com云与基础设施06 周前更新

关于 peering-mcp

peering-mcp 是面向 Agent 和网络研究者的只读 MCP 服务,可查询互联网互联关系与 IP 注册信息。它整合 PeeringDB 和 RDAP,并返回经过校验的精简结果。

主要功能

  1. 01查询网络信息和对等互联策略
  2. 02列出交换中心与设施的接入情况
  3. 03查找网络间的共同交换中心
  4. 04查询交换中心内的网络
  5. 05查询 IP 和 AS 注册信息

适用场景

  1. 01确认网络可在哪里互联
  2. 02查找指定交换中心的对等网络
  3. 03识别 IP 地址段的注册机构

MCP 相关

向 MCP 客户端提供五种只读工具,数据来自 PeeringDB 和 RDAP。响应经过校验、清理和缓存,并实施速率限制与字节预算控制。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"leonardmichalas-peering": {
"command": "uvx",
"args": [
"peering-mcp"
],
"env": {
"PEERINGDB_API_KEY": "<PEERINGDB_API_KEY>",
"PEERING_MCP_CACHE_TTL": "<PEERING_MCP_CACHE_TTL>",
"PEERING_MCP_CACHE_DIR": "<PEERING_MCP_CACHE_DIR>",
"PEERING_MCP_NO_CACHE": "<PEERING_MCP_NO_CACHE>",
"PEERING_MCP_TIMEOUT": "<PEERING_MCP_TIMEOUT>",
"PEERING_MCP_MAX_RETRIES": "<PEERING_MCP_MAX_RETRIES>"
}
}
}
}
启动命令uvx peering-mcp
需要配置的环境变量
PEERINGDB_API_KEY密钥A free PeeringDB API key. Recommended, not required: it raises the rate limit, and every tool works without one.
PEERING_MCP_CACHE_TTLHow long a cached upstream response stays usable, in seconds.
PEERING_MCP_CACHE_DIRWhere the on-disk cache lives. Defaults to $XDG_CACHE_HOME/peering-mcp.
PEERING_MCP_NO_CACHESet to 1 to disable caching. Slower, and harder on the upstream APIs.
PEERING_MCP_TIMEOUTPer-request timeout in seconds.
PEERING_MCP_MAX_RETRIESAttempts before an upstream failure is reported.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypipeering-mcp0.1.1

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

peering-mcp

CI Python 3.12+ MCP server Ruff mypy: strict Licence: MIT

一个让 AI Agent 查询互联网实际连接方式的 MCP 服务——哪些网络彼此互连、在哪些互联网交换中心和设施互连、遵循什么对等互联政策,以及某个地址范围登记在谁名下。

通过两个公共数据源提供五种只读工具:PeeringDB 用于查询互连信息,以及通过 RDAP 查询区域互联网注册管理机构的登记信息。上游响应经过验证和整理,纯文本内容在到达模型前会被移除结构,请求速率限制遵循 PeeringDB 的要求,答案会在运行期间缓存在磁盘上。每个响应都受字节数上限约束。

使用 uvx peering-mcp 安装。这是一个采用 MIT 许可的个人项目。

为什么要开发它

互联网由大约八万个独立网络组成,它们约定相互传输流量。哪些网络彼此互连、在哪里互连,以及遵循什么条款,都是公开、免费的结构化信息,由 PeeringDB 和区域互联网注册管理机构通过稳定的 API 发布。

AI Agent 却无法获取这些信息。询问编码助手某个运营商接入了哪些互联网交换中心,它就会凭记忆作答:措辞流畅、语气肯定,却往往有误。它无法核实,因此也不会核实。

这个服务提供了核实信息的途径。

功能介绍

工具解答的问题
lookup_network这是哪个网络?它的对等互联政策是什么?
list_presence它们出现在哪些互联网交换中心和设施?
find_at_exchange这个交换中心还有哪些网络?它们愿意对等互联吗?
find_common_presence这些网络可以在哪里互联?
lookup_registration这个 IP 地址范围或 AS 号码注册给了谁?

find_common_presence 是促成本项目诞生的工具。要找出两个或多个网络可以在哪里互联,需要逐个查询每个网络,列出它们出现的所有地点,再手动求交集。这大约需要一小时,还得打开十几个浏览器标签页。它本该只需问一个问题。

它接收两个到五个 AS 号码,无论网络数量多少,只需四次请求就能给出答案。共享交换中心按最窄瓶颈容量从高到低排列——也就是按任一网络在该处的最小容量排序,因为网络之间的连接能力会受此限制。

它还会返回每个网络各自拥有的地点数量,让空结果有据可解:要么这些网络确实没有共同地点,要么其中一个网络根本没有记录,这是完全不同的情况。

find_at_exchange 则从另一个角度提问:哪些网络已经接入了 DE-CIX Frankfurt?其中哪些愿意与任何网络对等互联?它接收交换中心名称或其 PeeringDB ID,可选地仅保留声明了某一种对等互联政策的网络,并按容量从高到低返回结果。一个名称匹配多个交换中心时——有十个交换中心都叫 LINX,分布在四大洲——它会返回候选项供选择,绝不猜测用户指的是哪一个。

lookup_registration 是这里唯一不读取 PeeringDB 的工具。它会查询负责分配地址的注册机构——RIPE NCC、ARIN、APNIC、LACNIC 或 AFRINIC——并返回持有者、分配日期、注册实际涵盖的范围,以及举报滥用行为的渠道。要查询哪个注册机构本身也需要先查找;系统会根据 IANA 自己的引导文件确定,而不是通过第三方重定向服务,因此答案能够说明信息来源。

查询单个地址时,结果会给出它所属的地址块:查询 8.8.8.8 会返回 8.8.8.0 - 8.8.8.255,注册持有者为 Google LLC。对于没有任何注册机构负责的地址范围,例如 240.0.0.0/8,系统无需发出请求即可返回结果。

答案示例

每个工具都返回相同的封装结构,让模型只需学习一种格式,而不是五种。使用 lookup_network 查询 AS3320 时会返回以下内容——整个响应在网络上传输时为 854 字节,而上游记录包含 42 个字段:

{
  "status": "ok",
  "data": {
    "network": {
      "asn": 3320,
      "name": "Deutsche Telekom",
      "long_name": "Deutsche Telekom AG",
      "website": "https://wholesale.telekom.com",
      "network_type": "NSP",
      "traffic_estimate": "50-100Tbps",
      "scope": "Global",
      "traffic_ratio": "Mostly Inbound",
      "ipv4_prefixes": 150000,
      "ipv6_prefixes": 40000,
      "exchange_count": 7,
      "facility_count": 53,
      "policy": {
        "general": "Restrictive",
        "locations": "Required - International",
        "ratio_required": true,
        "contract_required": "Required",
        "url": null
      },
      "irr_as_set": "AS3320:AS-DTAG AS3320:AS-DTAG-V6",
      "looking_glass": "https://lg.telekom.com"
    },
    "candidates": []
  },
  "note": "PeeringDB records are maintained by the networks themselves. Treat a missing field as unrecorded, not as evidence it is untrue.",
  "provenance": {
    "source": "peeringdb",
    "fetched_at": "2026-09-14T16:44:53.916085Z",
    "record_updated": "2026-08-31T13:30:19Z",
    "from_cache": false
  }
}

首先应查看 status 字段,ok 只表示一件事:答案位于 data 中。名称匹配多个网络时,会返回 ambiguous,并提供候选项供选择,绝不猜测用户指的是哪一个。未列出的 AS 号码会返回 not_found,并附注说明网络即使未注册,也可以路由流量。

另外三个示例,展示工具如何发挥作用

真实响应,已按标记裁剪。这里没有示意内容:每条都是工具于 2026-09-19 返回的结果。

“Deutsche Telekom 和 Hurricane Electric 可以在哪里互相对等互联?”——调用一次 find_common_presence,传入 [3320, 6939],发出四次上游请求:

{
  "status": "ok",
  "data": {
    "networks": [
      { "asn": 3320, "name": "Deutsche Telekom", "exchanges": 7, "facilities": 53 },
      { "asn": 6939, "name": "Hurricane Electric", "exchanges": 335, "facilities": 342 }
    ],
    "exchanges": {
      "items": [
        {
          "name": "NL-ix",
          "city": "Amsterdam, Rotterdam, Brussels, Luxembourg, Frankfurt,…",
          "country": "NL",
          "networks": [
            { "asn": 3320, "speed_mbps": 220000, "ports": 2, "route_server": false },
            { "asn": 6939, "speed_mbps": 400000, "ports": 1, "route_server": true }
          ]
        },
        {
          "name": "DE-CIX Frankfurt",
          "city": "Frankfurt",
          "country": "DE",
          "networks": [
            { "asn": 3320, "speed_mbps": 110000, "ports": 1, "route_server": false },
            { "asn": 6939, "speed_mbps": 800000, "ports": 1, "route_server": true }
          ]
        }
      ],
      "total": 6,
      "truncated": false
    }
  }
}

六个共同交换中心,按最窄瓶颈从大到小排列:NL-ix 位居首位,因为两个网络中较窄的那个在此有 220 Gbps,而不是因为任何一方的总容量更大。下方按网络列出的总数让空结果也清晰易懂——Deutsche Telekom 总共记录了 7 个交换中心,因此“没有重叠”与 Hurricane Electric 的 335 个所代表的情况截然不同。

“哪些网络已经接入 DE-CIX Frankfurt?它们愿意与任何人对等互联吗?”——使用 find_at_exchange,并设置 policy: "Open":

{
  "status": "ok",
  "data": {
    "exchange": { "exchange_id": 31, "name": "DE-CIX Frankfurt", "city": "Frankfurt", "country": "DE", "networks_recorded": 1020 },
    "networks": {
      "items": [
        { "asn": 24940, "name": "Hetzner Online", "speed_mbps": 2800000, "ports": 3, "route_server": true, "policy": "Open" },
        { "asn": 20940, "name": "Akamai Technologies", "speed_mbps": 2100000, "ports": 4, "route_server": true, "policy": "Open" }
      ],
      "total": 649,
      "truncated": true
    }
  },
  "note": "Participation is self-reported by each network in PeeringDB; a network missing here is unrecorded, not absent. Showing the 2 largest of the 649 networks (of 1020 here) stating policy Open; raise limit for more, at most 200."
}

该处的 1,020 个网络中,有 649 个声明了开放策略。筛选条件应用于交换中心,而非页面,因此这是该交换中心的数量——不是“前五十个网络中采用开放策略的数量”。

“8.8.8.8 注册在谁名下?我该向哪里举报滥用行为?”——使用 lookup_registration,它查询的是注册机构,而不是 PeeringDB:

{
  "status": "ok",
  "data": {
    "target": "8.8.8.8",
    "kind": "address",
    "registry": "ARIN",
    "handle": "NET-8-8-8-0-2",
    "holder": "Google LLC",
    "covers": "8.8.8.0 - 8.8.8.255",
    "allocation_type": "DIRECT ALLOCATION",
    "registered": "2023-12-28T17:24:33-05:00",
    "abuse": { "name": "Abuse", "email": "[email protected]" }
  },
  "note": "Registry data: it says who an allocation was made to, which is not always who operates the resource today.",
  "provenance": { "source": "rdap", "record_updated": "2023-12-28T17:24:56-05:00", "from_cache": true }
}

问题问的是一个地址,答案则涵盖了它所属的地址块,这正是 covers 的用途。

工作原理

Agent 通过 stdio 与 peering-mcp 通信。只有服务端会访问公共互联网,向 PeeringDB 和 RDAP 发送 HTTPS GET 请求,并读取和写入本地磁盘缓存。

Agent 本身不会访问互联网。所有请求都经由服务端处理;只有在服务端才能真正实施速率限制、缓存、验证和清理。

请求会沿两条路径之一处理:

查询会先检查磁盘缓存。命中后即告完成。未命中则等待速率限制器,从 PeeringDB 获取最多 130 KB 的 JSON,然后在返回 854 字节给 Agent 之前进行验证、清理、整形并存储。

整形这一步并非表面功夫。单个网络的原始接入记录可能超过 130 KB,直接返回会淹没 Agent 的上下文窗口,并且会明显降低它执行实际任务的能力。list_presence 将 Hurricane Electric 的 336 个交换中心端口整理成一个不超过 6 KB 的交换中心页面,并按容量从大到小排列,同时说明省略了多少条——页面按预算截断,而非按数量截断,因此限制只是上限,字节数才是保证值。find_common_presence 会读取三个网络共 225 KB 的数据,并在不到 4 KB 的结果中给出答案。

设计原则

这些原则是必须遵守的,而非仅供参考;每个拉取请求都会依据这些原则进行审核。

  • 永久只读。 始终只发送 GET 请求,并在传输层强制执行,而非依赖约定。没有写入路径,也不会有。
  • 不知道时会明确说明。 PeeringDB 的数据由用户自行报告,因此缺少记录很常见,不能据此认定某事不属实。服务会区分“这个网络不存在”和“没人填写这项信息”,绝不会用看似合理的猜测填补空缺。
  • 每个回答都附带来源和数据时效。 包括上游记录最后编辑的时间,因为一条自 2019 年以来未更新的记录,不应与上个月刚编辑的记录同等看待。
  • 响应刻意保持精简,并且会强制执行大小限制。 每个工具都会返回经过整理的精简结果,而不是直接传递上游 JSON;每个工具都有字节预算,并通过测试确保即使在自身限制允许的最坏情况下也不超出预算,而不只是适用于当前数据。列表会截断到符合预算的大小,并说明省略了多少项。
  • 不信任上游文本。 自由文本字段——包括 PeeringDB 中由网络自行填写的内容,以及注册记录中的持有人名称和联系方式——都会进入大模型的上下文。这些内容在离开服务前会经过允许列表筛选、长度限制和清理。
  • 礼貌地访问上游。 PeeringDB 允许每秒发送一个请求;服务会遵守这一限制、大力使用缓存,并在每个请求中标识自身。

数据来源

均为公开、免费数据,不进行抓取。

来源用途认证费用
PeeringDB API v2网络、交换中心、设施、连接情况、对等互联政策建议使用 API 密钥,但非必需免费
RDAP通过 IANA 引导文件获取 IP、前缀和 AS 号码的注册数据无免费

RIPEstat 的路由观测数据和 CAIDA AS Rank 的拓扑数据有意不在范围内:它们回答的是互联网正在做什么,而这里回答的是谁与谁互联,以及互联条件是什么。

要求

与 Agent 搭配使用

无需预先安装:uvx 会获取软件包并运行它。

Claude Code:

claude mcp add peering-mcp -- uvx peering-mcp

任何可读取 JSON MCP 配置的客户端:

{
  "mcpServers": {
    "peering-mcp": {
      "command": "uvx",
      "args": ["peering-mcp"]
    }
  }
}

如需运行本地检出版本——用于开发或尝试修改——请将命令替换为 uv run --directory /path/to/peering-mcp peering-mcp。

然后向它询问 Agent 通常会答错的问题:“Deutsche Telekom 和 Hurricane Electric 可以在哪里进行对等互联?”

一个 PeeringDB API 密钥

推荐,但非必需。 所有工具都可以在没有密钥的情况下运行,不会限制任何功能,而且服务无需任何配置即可启动。

添加密钥的原因是,PeeringDB 对匿名调用者的限制比对已认证调用者更严格,它自己的限流响应也说明了这一点:“认证后限制更少。” /netixlan 最容易触及限额,因为它既是数据量最大的端点,也是每个网络存在情况查询都需要用到的端点——一个网络的原始端口记录超过 130 KB。匿名调用者超出限额后会收到限流通知,并被要求等待数十分钟。服务会如实处理这种情况,返回 rate_limited,而不是错误或空答案,但必须等到限流解除后才能回答。

密钥免费,申请大约需要一分钟:docs.peeringdb.com/howto/api_keys/。请使用你自己的密钥——它会标识你对 PeeringDB 的调用,并关联到你的账户。

将其作为服务器进程的 PEERINGDB_API_KEY 环境变量传入。将密钥保存在 MCP 客户端自己的配置中,可以把机密信息限定在唯一需要它的进程内:

claude mcp add peering-mcp -e PEERINGDB_API_KEY=your-key-here -- \
  uv run --directory /path/to/peering-mcp peering-mcp
{
  "mcpServers": {
    "peering-mcp": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/peering-mcp", "peering-mcp"],
      "env": { "PEERINGDB_API_KEY": "your-key-here" }
    }
  }
}

服务器只会从环境变量读取密钥。它不会读取 .env 文件,因此放在其中的密钥会被忽略,且不会发出警告。

开发

git clone https://github.com/LeonardMichalas/peering-mcp.git
cd peering-mcp
uv sync --all-groups

uv run pytest              # tests
uv run ruff check .        # lint
uv run ruff format .       # format
uv run mypy src            # types

uv run 会处理环境,无需激活虚拟环境。

安装一次 Git 钩子后,每次提交前都会运行 lint、格式检查和类型检查:

uv run pre-commit install
图表

上方的两个图表由程序生成,而非手工绘制。docs/diagrams/*.json 是源文件,docs/images/ 中的动画 SVG 是 README 中展示的内容。

docs/diagrams/animate.mjs 会将渲染后的图表转换为一对 SVG。它需要在 PATH 中找到 Chromium 系浏览器:

node docs/diagrams/animate.mjs <rendered.html> docs/images/<name>

它会为每种主题分别生成一个文件,因为作为图像加载的 SVG 无法获知所在页面的主题;动画采用 SMIL,因此即使 GitHub 将其作为普通图像渲染,也能正常播放。

配置

所有配置项都有可用的默认值。即使没有设置任何配置,服务器也会启动并回答问题。

变量默认值用途
PEERINGDB_API_KEY未设置提高 PeeringDB 的速率限制。推荐,但非必需
PEERING_MCP_CACHE_TTL86400缓存有效期,单位为秒
PEERING_MCP_CACHE_DIR$XDG_CACHE_HOME/peering-mcp,否则为 ~/.cache/peering-mcp磁盘缓存的存放位置
PEERING_MCP_NO_CACHE未设置设为 1 可禁用缓存,用于测试
PEERING_MCP_TIMEOUT10每个请求的超时时间,单位为秒
PEERING_MCP_MAX_RETRIES3报告上游故障前的尝试次数
测试

测试分为四个层级,每个层级都能验证其他层级无法验证的内容:

目录验证内容
tests/unit/纯逻辑:结构整理、清理、引导匹配、速率限制,以及在其上限允许的最坏情况下对每种响应大小预算的验证
tests/contract/验证服务器能够处理上游实际发送的内容,包括格式错误、截断和恶意响应
tests/integration/通过 SDK 验证其作为 MCP 服务的行为:架构、信封格式和每一种状态
tests/eval/验证模型能否仅凭描述选出正确的工具

评估测试需主动选择运行,且与测试套件分开:它会使用真实工具架构,向真实模型提出二十个自然语言问题,记录模型选择调用的工具,每次运行费用约为 0.50 美元。在 Claude Opus 5 的低推理强度设置下,该测试得分为 20 题全对。

export ANTHROPIC_API_KEY=...
uv run --group eval python tests/eval/run_eval.py

没有测试会访问真实 API。 传输层会模拟上游,因此测试套件可以离线运行,并且在任何环境中结果一致。live 标记专用于会访问 PeeringDB 的可选测试;CI 会通过 -m "not live" 排除这些测试。

贡献

欢迎提交问题和拉取请求。提交 PR 前,请确认:

  1. uv run pytest、uv run ruff check . 和 uv run mypy src 均通过。
  2. 新增行为已在适当层级编写测试。
  3. 改动符合上述设计原则。特别是,对于返回大量或未经过结构化处理的响应,或可能将上游原始自由文本传递给模型的工具,我们会要求退回修改。

许可证

MIT。请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。