H3 MCP 工具

2026-10-30 17:48 更新

H3 内置了两个 JSON-RPC 2.0 处理器:一个走 HTTP,一个走 WebSocket。它们的用途很清楚——把一组方法以 JSON-RPC 的形式暴露出去,这正好是 MCP(Model Context Protocol)类工具服务需要的形状。

HTTP 版:defineJsonRpcHandler

import { H3, defineJsonRpcHandler } from "h3";

const app = new H3().post(
  "/rpc",
  defineJsonRpcHandler({
    methods: {
      // 第一个参数是 JSON-RPC 的请求体(含 params),第二个是 event
      echo: ({ params }, event) => {
        return `收到 ${JSON.stringify(params)},路径 ${event.url.pathname}`;
      },
      sum: ({ params }) => params.a + params.b,
    },
  }),
);

返回值会被序列化成 JSON-RPC 的 result;抛出 HTTPError 会被映射成对应的 error 对象。方法名就是客户端调用时写的 method。

三个默认开启的安全限制

这三个默认值不是可选项,而是设计上的兜底。理解它们能省掉两类事故:

限制 防的是什么 默认行为
要求 JSON Content-Type CSRF —— 简单的表单提交可以打到任何接口 非 JSON 类型直接拒绝
拒绝跨域请求 CSRF + DNS rebinding —— 恶意页面借浏览器打内网服务 Origin 不在允许列表就拒绝
批量请求上限 50 条 fan-out 放大 —— 一个请求带 50 个方法把服务打满 超过即拒绝

第二条尤其值得注意:浏览器里随便一个页面都能向 localhost 发请求,DNS rebinding 更是能把外部域名解析到内网 IP。没有 Origin 检查的本地 RPC 服务,等于把你的方法暴露给任何网页。

对应可调的选项是 validateContentType、allowedOrigins、maxBatchSize。放宽任何一项,都要明确知道自己换掉的是什么。

WebSocket 版:defineJsonRpcWebSocketHandler

import { H3, defineJsonRpcWebSocketHandler } from "h3";

const app = new H3().get(
  "/rpc/ws",
  defineJsonRpcWebSocketHandler({
    methods: {
      sum: ({ params }) => params.a + params.b,
    },
    // 需要额外钩子时(连接建立、断开)挂在 hooks 里
    hooks: {
      open(peer) {
        console.log(`连接建立 ${peer.id}`);
      },
      close(peer, details) {
        console.log(`连接断开 ${peer.id}`, details);
      },
    },
  }),
);

每个 WebSocket 文本消息被当成一条独立的 JSON-RPC 请求处理,响应发回同一个 peer。适合需要双向、长连接的场景(比如流式工具调用)。

⚠️ 它不检查 Origin。 WebSocket 升级请求不受 CORS 约束,所以任何站点上的页面都能带上访客的 cookie 建立连接——这就是跨站 WebSocket 劫持(CSWSH)。稳妥做法是在 crossws 的 upgrade 钩子里校验 Origin,不通过就抛一个 Response 中止握手;不要依赖浏览器替你挡。

什么时候用、什么时候不用

适合 不适合
给 LLM 客户端暴露一组工具方法 普通 REST 接口(直接用路由更简单)
已有 JSON-RPC 生态,需要服务端适配 需要严格的 OpenAPI 文档与契约测试
需要批量调用(一次发多条方法) 单个方法、低复杂度接口

一个常见误区是「有 MCP 就全用 MCP」。实际上 defineJsonRpcHandler 只负责协议层,它不会帮你做鉴权、限流、参数校验——这些仍然要自己加(requireBasicAuth、bodyLimit、readValidatedBody 该用还得用)。

排查表

现象 最可能原因 处理
调用返回 4xx,方法却没问题 请求 Content-Type 不是 application/json 检查客户端请求头
本地调用正常,页面里调不通 跨域被默认拒绝 配置 allowedOrigins,别直接关掉检查
批量请求被拒 超过 50 条 拆分批次或调 maxBatchSize
WebSocket 版被任意站点连上 没做 Origin 校验 在 upgrade 钩子里校验并中止
方法里抛错,客户端只看到泛化错误 抛的不是 HTTPError 用 HTTPError 携带状态码与 message

延伸阅读

  • 错误对象与状态码的映射:见 2.6 错误处理
  • WebSocket 的握手钩子与 crossws:见 5.2 WebSocket 与 SSE
  • 请求体校验与体积限制:见 6.2 请求体与查询参数
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号