首页 › tutorials › 造HTTP探针MCP让Claude秒测REST接口

用 FastMCP 造一个 HTTP 接口探针:让 Claude Code 秒测 REST API

概述

本教程带你用 Python 的 FastMCP 框架,从零打造一个 HTTP 接口探针 MCP 服务器,把 http_get / http_post / http_put / http_delete 四个工具暴露给 Claude Code。完成后,Claude 可以直接在你的对话里"按需发请求、解析响应、定位报错"——相当于给它装了一个内置 Postman。适合已经能跑通 Claude Code、想理解 MCP 工具开发闭环、并希望落地一个真实可用工具的进阶读者。

前置条件

为什么需要一个 HTTP 探针 MCP

很多人第一反应是:Claude Code 不是有 WebFetch、Bash 吗,为什么还要单独造一个 HTTP MCP?

三个理由:

  1. 可控的参数语义。WebFetch 偏向"抓网页正文",对 JSON 接口、自定义 Header、鉴权 Token、POST body 的支持很弱;Bash 里 curl 能做,但每次都要 Claude 自己拼命令,容易拼错、且无法做结构化的超时与错误归因。
  2. 结构化返回。MCP 工具返回的是带类型的数据,我们可以把"状态码、耗时、响应头、截断后的 body"打包成一个 dict,Claude 解析起来比解析 curl 的自由文本可靠得多。
  3. 权限边界。通过一个专门的 MCP 工具,你可以在代码里做白名单域名、超时上限、敏感字段脱敏——这是把"AI 能联网"这件事关进笼子里的最干净方式。

理解了动机,下面进入实操。

第一步:理解 MCP 的三层抽象

在动手前,先把心智模型建对。MCP(Model Context Protocol)是 Anthropic 主导的开放协议,它定义了"宿主(Host)/ 客户端(Client)/ 服务器(Server)"三者关系:

flowchart LR
    U["你(在 Claude Code 里对话)"] -->|"自然语言"| CC[Claude Code 主进程]
    CC -->|"MCP 协议 stdio"| S["你的 MCP Server\nhttp_probe.py"]
    S -->|"HTTP 请求"| API["外部 REST API"]
    API -->|"响应"| S
    S -->|"结构化结果"| CC
    CC -->|"自然语言答复"| U

在 Claude Code 里,Claude Code 既是宿主又是客户端,它通过 stdio(标准输入输出)拉起你的 Python 进程作为 Server,用 JSON-RPC 消息来通信。你写的每一个 @mcp.tool 函数,都会变成 Claude 工具列表里的一项;当 Claude 决定调用它时,它收到一个 tools/call 请求,执行后返回结果。

关键认知:你的函数不是被你的代码直接调用,而是被 Claude 在对话中"决定调用"。 所以函数签名、docstring、返回结构都要写得让模型容易正确使用。

第二步:建项目骨架

创建一个干净的项目目录:

mkdir http-probe-mcp && cd http-probe-mcp
python3 -m venv .venv
source .venv/bin/activate          # Windows 用 .venv\Scripts\activate
pip install "fastmcp>=2.3" httpx

为什么用 httpx 而不是 requests?因为 httpx 同时支持同步和异步、默认更好的超时语义,并且 FastMCP 的异步工具能直接 await 异步请求,吞吐更高。本教程用同步风格降低门槛,但代码结构随时能升级成异步。

新建 http_probe.py,先写最小骨架并跑通"一个工具":

# http_probe.py
import time
import json
import httpx
from fastmcp import FastMCP

mcp = FastMCP("http-probe")

@mcp.tool()
def http_get(url: str, timeout: float = 10.0) -> dict:
    """向指定 URL 发送 GET 请求,返回状态码、耗时与响应体。

    适用于查询 REST 接口、拉取 JSON 数据、做接口连通性探测。
    Args:
        url: 完整的 http(s) 地址,例如 https://api.example.com/users
        timeout: 超时秒数,默认 10 秒
    """
    started = time.perf_counter()
    try:
        with httpx.Client(timeout=timeout, follow_redirects=True) as client:
            resp = client.get(url)
        elapsed = round((time.perf_counter() - started) * 1000, 1)
        return _shape(resp, elapsed)
    except httpx.HTTPError as e:
        return {"ok": False, "error": f"{type(e).__name__}: {e}", "url": url}


def _shape(resp: httpx.Response, elapsed: float) -> dict:
    body = resp.text
    # 防止把超大响应塞进上下文,统一截断到 4000 字符
    if len(body) > 4000:
        body = body[:4000] + f"\n…[truncated, total {len(body)} chars]"
    return {
        "ok": True,
        "status_code": resp.status_code,
        "elapsed_ms": elapsed,
        "url": str(resp.url),
        "headers": dict(resp.headers),
        "body": body,
    }


if __name__ == "__main__":
    mcp.run()

注意几个设计点:

预期结果:本地冒烟测试

FastMCP 自带 CLI 可以在终端直接调用工具,不用先接进 Claude:

# 安装 fastmcp 后会带一个 fastmcp 命令
fastmcp run http_probe.py

更实用的是写一个临时调用脚本 smoke.py 验证逻辑:

# smoke.py —— 仅用于本地验证,不是 MCP 的一部分
from http_probe import http_get
import json
r = http_get("https://jsonplaceholder.typicode.com/posts/1")
print(json.dumps(r, ensure_ascii=False, indent=2))
python smoke.py

预期输出(节选):

{
  "ok": true,
  "status_code": 200,
  "elapsed_ms": 312.4,
  "url": "https://jsonplaceholder.typicode.com/posts/1",
  "body": "{\n  \"userId\": 1,\n  \"id\": 1,\n  \"title\": \"sunt aut facere...\",\n  \"body\": \"quia et suscipit...\"\n}"
}

看到 ok: true、status_code: 200、elapsed_ms 有值,说明工具逻辑跑通。

常见问题

第三步:补齐 POST / PUT / DELETE 与鉴权

真实接口几乎都要带 Header、Token、JSON body。把工具补全:

@mcp.tool()
def http_post(
    url: str,
    body: dict | None = None,
    headers: dict | None = None,
    timeout: float = 10.0,
) -> dict:
    """向指定 URL 发送 POST 请求,body 会被序列化为 JSON。

    适用于创建资源、提交表单、调用需要 JSON body 的接口。
    Args:
        url: 目标地址
        body: 要发送的 JSON 对象,可为空
        headers: 自定义请求头,例如 {"Authorization": "Bearer xxx"}
        timeout: 超时秒数
    """
    return _request("POST", url, json_body=body, headers=headers, timeout=timeout)


@mcp.tool()
def http_put(
    url: str, body: dict | None = None, headers: dict | None = None, timeout: float = 10.0
) -> dict:
    """发送 PUT 请求,通常用于整体更新某个资源。"""
    return _request("PUT", url, json_body=body, headers=headers, timeout=timeout)


@mcp.tool()
def http_delete(url: str, headers: dict | None = None, timeout: float = 10.0) -> dict:
    """发送 DELETE 请求,删除指定 URL 对应的资源。"""
    return _request("DELETE", url, headers=headers, timeout=timeout)


def _request(method, url, json_body=None, headers=None, timeout=10.0) -> dict:
    started = time.perf_counter()
    try:
        with httpx.Client(timeout=timeout, follow_redirects=True) as client:
            resp = client.request(method, url, json=json_body, headers=headers or {})
        elapsed = round((time.perf_counter() - started) * 1000, 1)
        return _shape(resp, elapsed)
    except httpx.HTTPError as e:
        return {"ok": False, "error": f"{type(e).__name__}: {e}", "url": url, "method": method}

再加一个高频"组合工具"——健康检查,它专门回答"这个接口到底活没活":

@mcp.tool()
def health_check(url: str, expect_code: int = 200, timeout: float = 5.0) -> dict:
    """轻量探活:只发 HEAD/GET,判断状态码是否符合预期。

    适合做定时巡检、CI 里验证服务是否起来。返回 alive 布尔值便于模型直接判断。
    """
    started = time.perf_counter()
    try:
        with httpx.Client(timeout=timeout) as client:
            resp = client.head(url)
            if resp.status_code == 405:        # 有些接口不支持 HEAD
                resp = client.get(url)
        elapsed = round((time.perf_counter() - started) * 1000, 1)
        return {
            "ok": True,
            "alive": resp.status_code == expect_code,
            "status_code": resp.status_code,
            "expected": expect_code,
            "elapsed_ms": elapsed,
            "url": url,
        }
    except httpx.HTTPError as e:
        return {"ok": False, "alive": False, "error": f"{type(e).__name__}: {e}", "url": url}

health_check 的精髓在于:它把"接口健康"这个业务判断下沉到了工具里(返回 alive: bool),而不是让模型去解读状态码。模型判断布尔值远比解读 HTTP 语义可靠——这是设计 MCP 工具的核心心法:把领域判断固化进工具,给模型留下"显而易见的信号"。

预期结果

python -c "from http_probe import health_check; print(health_check('https://httpbin.org/status/200'))"

输出:

{"ok": true, "alive": true, "status_code": 200, "expected": 200, "elapsed_ms": 245.8, "url": "https://httpbin.org/status/200"}

常见问题

第四步:加一层域名白名单(安全围栏)

让 AI 能发任意 HTTP 请求是有风险的——它可能把请求打到内网管理后台、打到元数据服务(如云厂商的 169.254.169.254)。在 _request 最前面加一道闸:

import os
from urllib.parse import urlparse

# 环境变量里配允许的域名,逗号分隔;不配则允许全部(仅本地开发用)
ALLOWED = os.environ.get("HTTP_PROBE_ALLOW", "").split(",")
ALLOWED = {d.strip() for d in ALLOWED if d.strip()}

def _guard(url: str) -> str | None:
    host = urlparse(url).hostname or ""
    if not ALLOWED:                       # 未配置白名单:放行(仅建议本地)
        return None
    if host in ALLOWED:
        return None
    return f"域名 {host} 不在白名单内,已拦截"

然后在每个工具入口调用 _guard,非 None 直接返回错误。生产环境务必配 HTTP_PROBE_ALLOW=api.yourcompany.com,internal.corp,把攻击面收紧。

flowchart TD
    Call["Claude 调用 http_post"] --> G{"_guard 域名检查"}
    G -->|"在白名单"| Send["httpx 发请求"]
    G -->|"不在白名单"| Block["返回 ok:false, error: 拦截"]
    Send --> Shape["_shape 结构化"]
    Shape --> Ret["返回给 Claude"]
    Block --> Ret

第五步:注册到 Claude Code

工具写好了,现在让 Claude Code 认识它。最稳妥的是用 stdio 传输——Claude Code 自己拉起你的 Python 进程。

# 用绝对路径,避免 Claude Code 找不到解释器
claude mcp add http-probe -- /home/you/http-probe-mcp/.venv/bin/python /home/you/http-probe-mcp/http_probe.py

如果你需要注入鉴权环境变量或白名单:

claude mcp add http-probe \
  -e API_TOKEN=sk-xxxx \
  -e HTTP_PROBE_ALLOW=api.yourcompany.com \
  -- /home/you/http-probe-mcp/.venv/bin/python /home/you/http-probe-mcp/http_probe.py

作用域参数 -s: - -s local(默认):只对当前项目目录生效。 - -s user:对你所有项目生效。 - -s project:写入项目根 .mcp.json,可提交到 Git,团队共享(注意:不要把 secret 写进 .mcp.json)。

验证注册成功:

claude mcp list            # 列出所有已注册 server,应看到 http-probe
claude mcp get http-probe  # 查看该 server 的命令与工具数

进入交互界面后输入 /mcp,应该能看到 http-probe 是 connected 状态,并列出 5 个工具:http_get / http_post / http_put / http_delete / health_check。

常见问题

第六步:实战案例——让 Claude 帮你联调一个真实接口

场景:你在对接一个公开的 https://jsonplaceholder.typicode.com 假数据 API,想快速验证"创建一篇 post → 拿到 id → 再 GET 回来看是否一致"这条链路。

在 Claude Code 里直接说:

用 http_post 给 https://jsonplaceholder.typicode.com/posts 创建一篇文章,标题"测试 MCP",正文"hello from claude",拿到返回的 id 后,用 http_get 查一次确认字段都在,最后用 health_check 探活整个域名。

Claude 会自动按顺序调用 http_post →(从返回里取 id)→ http_get → health_check,并把三步结果汇总成一段结论。你会看到类似这样的工具调用序列(Claude 内部):

  1. http_post(url=".../posts", body={"title":"测试 MCP","body":"hello from claude","userId":1}) → 返回 status_code: 201,body 里含 "id": 101
  2. http_get(url=".../posts/101") → 返回刚才的字段
  3. health_check(url="https://jsonplaceholder.typicode.com") → alive: true

整个联调你一行命令都没敲,全部由 Claude 通过你造的 MCP 工具完成。这就是"工具下沉"的威力——把接口交互的脏活封装好,模型只负责编排和解读。

进阶玩法:让它做回归巡检。"用 health_check 并行探活这 5 个域名,把挂掉的列出来"——MCP 工具可以被模型在同一个回合里多次调用,天然适合批量。

第七步:调试与发布的闭环

写 MCP 最痛的是"看不见它在干嘛"。三个调试技巧:

  1. fastmcp dev http_probe.py 会启动一个交互式 Inspector,能在浏览器里手动点按钮调用每个工具、看原始 JSON-RPC 报文。这是定位"工具签名/返回结构"问题最快的途径。
  2. 给工具加结构化日志:在 _shape 里 print(json.dumps({...}), flush=True) 到 stderr。stdio 模式下 stdout 是协议通道不能污染,但 stderr 是自由的,日志写 stderr 不影响 Claude 通信。
  3. 单测用 smoke 脚本:任何改动后先跑 python smoke.py,确认基础工具仍返回 ok: true,再接进 Claude。

发布层面,如果你的工具要给团队用,推荐用 -s project 生成 .mcp.json 提交到仓库,但 secret 全部走 -e 环境变量或团队密钥管理,绝不进 Git。完整文件结构:

http-probe-mcp/
├── http_probe.py      # 服务器主文件(所有 @mcp.tool)
├── smoke.py           # 本地冒烟测试
├── .mcp.json          # 团队共享配置(无 secret)
├── requirements.txt   # fastmcp>=2.3, httpx
└── .venv/             # 虚拟环境(gitignore)

关键设计原则小结

把整篇浓缩成五条可复用的心法,适用于你未来造任何 MCP 工具:

  1. 返回结构化、形状统一:所有动词工具用同一个 _shape,给模型最小解析成本。
  2. 把领域判断固化进工具:像 health_check 那样返回 alive: bool,别让模型去解读语义。
  3. 截断一切大返回:body、列表、日志,超过几千字符就截,护住上下文窗口。
  4. 安全边界写在代码里:域名白名单、超时上限、敏感字段脱敏,不依赖模型自觉。
  5. docstring 当产品文档写:写清楚"适用场景 / 不适用场景 / 参数语义",这是模型选对工具的唯一依据。

延伸阅读

训练营适用性评估

参考来源

  1. Build an MCP server — Model Context Protocol 官方文档 — 官方从零构建 MCP server 的权威教程,覆盖协议与传输细节
  2. MCP Server Tutorial: 12 Steps Python FastMCP (2026) — 基于 FastMCP 官方 SDK 的 12 步实战指南,与本篇方法论一致
  3. FastMCP in Python: Build a Real MCP Server (2026 Guide) — FastMCP 当前版本特性与生产实践解读
  4. MCP Server for Claude Code Tutorial — Deep Dive (2026) — 聚焦 Claude Code 接入与 stdio 传输的深入教程
  5. FastMCP Builder — Claude Code Skill (GitHub) — 把 MCP 构建流程封装成 Claude Code Skill 的参考实现

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入