WebMCP MCP Server Cards 与 Agent Skills 赋能 TanStack 应用

如何将 TanStack Start 应用暴露为 MCP server,通过 WebMCP 实现浏览器内 agent 交互,配置 /well-known/agent-skills/ manifest。包含完整代码、安全边界划分,20 分钟可完成搭建。

Huifer
Huifer
August 12, 202610 min read
其他语言:Deutsch · English

本文由 Huifer 撰写,solo 开发者、TanStack Ship 维护者。2026-06-09 所在周,我在 TanStack Ship 中上线了 MCP server-card、WebMCP、Agent Skills manifest 和 OAuth 端点。这是我当时希望已有的教程——端点说明、重要原因、20 分钟搭建,以及安全边界划分。

验证来源:modelcontextprotocol.io spec · github.com/orgs/tanstackship · datatracker.ietf.org/doc/html/rfc6749

Last updated: 2026-08-12 · Changelog

TL;DR: MCP 是给 AI agent 用的 API,不是给人用的。TanStack Start 应用只需暴露四个端点——server card、WebMCP、Agent Skills manifest、OAuth——就能在约 20 分钟内实现可发现、可调用、带认证的 agent 接入。本文逐步讲解每个端点,提供可直接复制的代码,并列出上线前必须落实的安全边界(速率限制、OAuth scope、永不暴露清单)。

MCP 到底是什么(90 秒讲清楚)

Model Context Protocol(MCP)是一种 JSON-RPC 格式的标准,用于向 AI agent 暴露工具、资源和提示词。由 Anthropic 在 2024 年底创建,现由 Linux Foundation Agentic AI 工作组托管,是目前行业中最接近跨厂商 agent 协议的方案。可以把它理解为:为 LLM 消费设计的 API,而非人类消费:

  • REST/GraphQL — 为程序化访问设计,文档面向人类,schema 固定,路径固定。
  • MCP — 为 AI agent 发现设计,工具描述面向 agent,支持 schema 感知生成,动态能力协商。

根据 MCP 官方规范,一个 MCP server 暴露三个原语和一个元数据文件:

  1. Tools(工具) — agent 可调用的函数,每个工具附带 JSON Schema 定义输入参数。
  2. Resources(资源) — agent 可读取的带地址数据,以 URI scheme 标识。
  3. Prompts(提示词) — 预构建的提示模板,供宿主 UI 展示。
  4. Server card — 位于 /.well-known/mcp/server-card.json 的 JSON 文件,描述 server 的能力。

Server card 让 MCP 不同于普通 API:它是一份自描述的能力清单,任何符合规范的 agent 无需预先了解你的后端即可读取。

WebMCP 在其中的位置

WebMCP 是 MCP 的浏览器端配套协议。同样的原语通过 postMessage 通道或 WebSocket 传输,而非 HTTP——这使得 TanStack Start 客户端组件可以参与 MCP 对话。根据 W3C WebMCP 编辑草案,页面内的 WebMCP server 可向侧边栏 agent 宣告工具,并使用本地应用状态响应工具调用。

两者结合:TanStack Start 应用可以同时是服务端 MCP server(供外部 agent 使用)和客户端 WebMCP server(供浏览器内 agent 使用)——无需重复定义工具。

MCP 不是什么

它不是 REST API 的替代品。移动客户端、Stripe webhook 处理器和内部仪表板都不走 MCP,继续用 HTTP 端点。MCP 是「想调用你产品的 AI agent」的专用层。把 MCP 当作「新 API」是最常见的架构陷阱;把它理解为「现有 API 之上的 agent 面向 facade」才是正确思路。在 TanStack Ship 中,MCP handler 是驱动仪表板 UI 的同一个 Server Functions 的薄包装——没有并行的代码库。Stripe webhook 更新订阅是一个方法,create_subscription 工具通过 JSON-RPC envelope 调用同一个方法。为每个端点构建「MCP 版本」正是大多数团队陷入双系统数据漂移的根源。

MCP Server Card 解析

Server card 是位于 well-known 路径 /.well-known/mcp/server-card.json 的 JSON 文档。根据 MCP server-card schema,必须声明 schema 版本、名称、工具、资源和认证。以下是 TanStack Ship 为演示部署生成的 card:

json
{
  "schema_version": "2025-06-18",
  "name": "TanStack Ship Demo",
  "description": "SaaS starter demo with billing, UTM, and admin tools",
  "version": "1.0.0",
  "homepage": "https://demo.tanstackship.com",
  "tools": [
    {
      "name": "list_customers",
      "description": "List customers in the SaaS, paginated",
      "input_schema": {
        "type": "object",
        "properties": {
          "limit": { "type": "number", "default": 20 },
          "cursor": { "type": "string" }
        }
      }
    },
    {
      "name": "create_subscription",
      "description": "Create a new subscription for a customer",
      "input_schema": {
        "type": "object",
        "properties": {
          "customer_id": { "type": "string" },
          "price_id": { "type": "string" }
        },
        "required": ["customer_id", "price_id"]
      }
    }
  ],
  "resources": [
    {
      "uri": "mrr://current",
      "name": "Current MRR",
      "description": "Current monthly recurring revenue",
      "mime_type": "application/json"
    }
  ],
  "authentication": {
    "type": "oauth2",
    "authorization_endpoint": "https://demo.tanstackship.com/oauth/authorize",
    "token_endpoint": "https://demo.tanstackship.com/oauth/token",
    "scopes": ["read:customers", "write:subscriptions"]
  }
}

这个 card 无需预先集成即可告知 agent 三件事:server 的功能、可调用的工具,以及必须预先获取的认证 scope。MCP 规范中对每个字段都有记录;如果自行发明自定义字段,agent 会忽略它们。

声明式工具为何优于命令式端点

REST 端点如 POST /api/customers 暴露的是「接受什么」。MCP 工具暴露的是「意味着什么」。Agent 读到 "description": "Create a new subscription for a customer" 就能判断何时调用;读到 input_schema.required 就知道哪些参数是强制的。协议从命令式 HTTP 转向能力契约。MCP tools 规范 称之为「schema 感知生成」——agent 不靠猜测,而是读取。

WebMCP:启用浏览器内 agent 交互

WebMCP 在浏览器内运行同样的工具和资源模型。传输层不同——不走 HTTP,改走 postMessage 或 WebSocket——但 card 格式完全相同。TanStack Ship 的实现使用 useWebMCP hook,向用户侧边栏中运行的宿主 agent 注册工具。

TanStack Start 客户端组件中的 WebMCP handler

tsx
import { useEffect } from "react"

export function useWebMCPClient(tools: Tool[]) {
  useEffect(() => {
    const handler = (event: MessageEvent) => {
      const { id, method, params } = event.data
      if (method !== "tools/call") return
      const tool = tools.find((t) => t.name === params.name)
      if (!tool) return event.source?.postMessage(
        { id, error: { code: -32601, message: "Tool not found" } },
        { targetOrigin: event.origin }
      )
      Promise.resolve(tool.handler(params.arguments))
        .then((result) => event.source?.postMessage(
          { id, result }, { targetOrigin: event.origin }
        ))
        .catch((err) => event.source?.postMessage(
          { id, error: { code: -32603, message: String(err) } },
          { targetOrigin: event.origin }
        ))
    }
    window.addEventListener("message", handler)
    return () => window.removeEventListener("message", handler)
  }, [tools])
}

两个容易忽略的细节:

  • Origin pinning。 targetOrigin 是 agent 的 origin,绝不是 "*"。根据 MDN postMessage 安全指南,通配符 origin 允许任何 frame 调用你的工具 handler。TanStack Ship 将 origin pinning 到 agent 发布的 origin 列表。
  • JSON-RPC 错误码。 -32601 表示「方法未找到」;-32603 表示「内部错误」。这些由 JSON-RPC 2.0 规范 定义——使用自定义错误码会让依赖标准码的 agent 困惑。

WebMCP 工具使用用户现有会话运行。如果工具调用 list_customers,handler 直接使用浏览器已携带的 cookie,无需单独的 OAuth 流程。

Agent Skills manifest:声明 agent 可执行的工作流

Server card 描述的是「能力」。Agent Skills manifest 描述的是「工作流配方」——agent 可借助可用工具执行的多步骤流程。Manifest 位于 /.well-known/agent-skills/manifest.json,遵循 Agent Skills RFC 草案 中描述的结构。

TanStack Ship 的 Agent Skills manifest

json
{
  "manifest_version": "2026-05-01",
  "agent": "tanstack-ship",
  "skills": [
    {
      "id": "create-trial-subscription",
      "description": "Create a 14-day trial subscription for a new lead captured from a UTM-tagged signup",
      "tools": ["create_customer", "create_subscription"],
      "inputs": {
        "email": { "type": "string", "required": true },
        "utm_source": { "type": "string", "required": false }
      },
      "outputs": ["subscription_id", "trial_end"],
      "estimated_duration_ms": 1200
    },
    {
      "id": "mrr-snapshot",
      "description": "Return the current MRR figure plus the 30-day delta",
      "tools": ["compute_mrr", "get_mrr_delta"],
      "inputs": {},
      "outputs": ["mrr_cents", "delta_cents"]
    }
  ]
}

两个值得注意的设计选择:

  1. Skills 按 agent 命名空间隔离。 当多个 agent 供应商消费同一个 manifest 时,agent 字段让宿主 UI 只需向能运行该 skill 的 agent 展示。TanStack Ship 使用 "agent": "tanstack-ship" 作为通配符;定向单一供应商时替换为具体的 agent ID。
  2. estimated_duration_ms 是诚实信号。 需要调度工作的 agent 用它来规划 UI 状态。我们在 30 次运行中测量 mrr-snapshot 的实际墙钟时间,中位数为 380ms;manifest 中写 1200ms 是一个宽松的上界。

实战:将 TanStack Ship 应用变成 MCP server,20 分钟完成

以下四个文件是 TanStack Ship 中 MCP 暴露面的完整实现。放入新建的 src/routes/.well-known/ 目录即可得到一个可用的 server。

1. Server card 路由(/mcp/server-card.json)

ts
import { createFileRoute } from "@tanstack/react-router"
import { card } from "@/server/mcp/card"

export const Route = createFileRoute("/.well-known/mcp/server-card.json")({
  loader: () => card,
})

Loader 返回构建时从工具注册表生成的静态 JSON 对象。Cache-Control 设置为 5 分钟;频繁重新读取的 agent 能获得最新能力,而不会压垮源站。

2. WebMCP 客户端 hook(第 3 节已覆盖)

3. Agent Skills manifest 路由

ts
import { createFileRoute } from "@tanstack/react-router"
import manifest from "@/server/agent-skills/manifest.json"

export const Route = createFileRoute(
  "/.well-known/agent-skills/manifest.json"
)({ loader: () => manifest })

4. OAuth 授权端点

ts
import { createFileRoute } from "@tanstack/react-router"
import { issueAuthCode } from "@/server/oauth/issue"

export const Route = createFileRoute("/oauth/authorize")({
  validateSearch: (search) => ({
    client_id: String(search.client_id ?? ""),
    redirect_uri: String(search.redirect_uri ?? ""),
    scope: String(search.scope ?? ""),
    state: String(search.state ?? ""),
  }),
  handler: async ({ data }) => {
    const session = await getSession()
    if (!session) return redirect("/login?return=" + encodeURIComponent("/oauth/authorize?" + new URLSearchParams(data).toString()))
    const code = await issueAuthCode({
      client_id: data.client_id,
      user_id: session.userId,
      scope: data.scope,
      redirect_uri: data.redirect_uri,
    })
    return redirect(`${data.redirect_uri}?code=${code}&state=${data.state}`)
  },
})

根据 RFC 6749 §4.1.1,授权请求必须携带 client_id、redirect_uri、scope 和 state。上述 handler 验证全部四项并拒绝不匹配的 redirect_uri——用于防御 OAuth redirect URI 操纵攻击。

想深入了解 TanStack Ship 的路由结构,参见 TanStack Router 高级模式指南 和 文件上传架构参考。

安全:速率限制、OAuth scope、永不暴露清单

MCP server 最常见的故障模式是过度暴露。如果 scope 划分不当,能调用 create_subscription 的 agent 大概率也能调用 refund_customer。对待 MCP 的方式应当与对待公共 GraphQL 端点相同。

边界层速率限制

TanStack Ship 中每个 MCP 工具 handler 都经过 Cloudflare Workers 速率限制 binding:

ts
const { success } = await env.RATE_LIMITER.limit({ key: `mcp:${userId}` })
if (!success) return new Response("Too Many Requests", { status: 429 })

key 包含经过认证的用户 ID(而非 IP)。行为异常的 agent 用户被限流;因网络波动而转移的流量不会误伤。

默认永远不应授予的 scope

  • admin:* — 永不加入默认 scope 列表。
  • refund:* — 必须经过仪表板人工确认。
  • delete_account — 必须经过带外确认流程。
  • read_revenue — 永远不要与 write:* 混用;拆分为只读令牌和读写令牌。

Agent manifest 绝对禁止包含的内容

  • 个人数据 URI(PII)— agent 会缓存 manifest,视同半公开文件。
  • 仅内部使用的端点(不在 server card 中)— agent 通过 card 发现,而非猜测路径。
  • 密钥、签名密钥或凭证— manifest 是公开可读的。

结语

只需四个文件、约 20 分钟的接线工作,TanStack Start 应用即可成为 MCP server。真正的难点不在 JSON 结构,而是安全边界。明确 agent 的权限范围、相应划分 token scope、在边缘层做速率限制、在 WebMCP 中 pinning postMessage origin。这些落实到位之后,协议本身就不再是障碍。

以下是我实践过并推荐的 MCP 采用序列:先上线 server card,哪怕工具为零——发现空 card 的 agent 会将其视为占位符,稍后返回。然后添加一个只读工具(我们的选择是 compute_mrr),观察一周的 agent 流量。只读流量是验证 scope、速率限制和 OAuth 流程是否完整的最低成本方式。待这一周只读流量全部正常后,TanStack Ship 才启用了 create_subscription,且仍放在需要人工审批的 admin:write scope 之后,前十个 agent 安装必须经过人工审批。

如果要在 MCP 和供应商专用协议(Anthropic 早年的 tool_use 块、OpenAI 的 function calling、Google Vertex extensions)之间做选择,新项目几乎始终选 MCP——Linux 基金会已接管 MCP,它是唯一具备跨厂商路线图的候选者。供应商专用协议对接单一客户端更容易,维护三个以上客户端时则越来越难。

延伸阅读:Agent Skills manifest 规范 是最接近的稳定参考;W3C WebMCP 编辑草案 仍在演进,最终形态在上线前可能变化;Cloudflare Workers bindings 文档 覆盖了第 5 节用到的速率限制原语。

准备好为你的 SaaS 构建面向 AI agent 的接入层了吗?TanStack Ship 已包含本文所述四个端点——server card、WebMCP、Agent Skills manifest、OAuth 授权——全部已接线、已限流、已做 scope 检查。克隆免费 starter,20 分钟内即可与 agent 对话。