mcpField
W
webweaver-mcp-server

webweaver-mcp-server

远程服务
github.com商业与营销06 天前更新

关于 webweaver-mcp-server

webweaver-mcp-server 将 WebWeaver Nexus 的联系表单、产品介绍和联系方式提供给支持 MCP 的主机。联系表单以交互式 MCP App 呈现,并通过 MCP 桥接提交。

主要功能

  1. 01交互式联系表单
  2. 02提交表单至联系 API
  3. 03获取产品介绍
  4. 04获取联系信息

适用场景

  1. 01在 MCP 主机中填写并提交联系表单
  2. 02查询 WebWeaver Nexus 的产品信息
  3. 03查找 WebWeaver Nexus 的联系方式

MCP 相关

这是一个 MCP 服务,向 MCP 客户端提供联系表单、产品介绍和联系信息工具;表单作为 MCP App 呈现,提交通过 MCP 桥接调用服务器工具。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://webweaver-nexus-mcp.vercel.app/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

工具

4

此工具无需参数。

详细介绍

WebWeaver Nexus MCP 服务

一个 MCP 服务,向支持 MCP 的宿主(Claude Desktop、claude.ai、ChatGPT、Cursor、MCP Inspector、basic-host)提供 WebWeaver Nexus 服务——联系表单(以交互式 MCP App 的形式呈现)、产品概览和联系信息。

生产环境 URL: https://webweaver-nexus-mcp.vercel.app/mcp — Streamable HTTP,公开访问,无需身份验证。

根主机名提供静态导航页面(public/index.html)。只有 /mcp 会路由到函数,因此在浏览器中直接打开端点会返回 406 Not Acceptable——这是正确的内容协商行为,并非故障。

MCP 注册表

已发布到官方 MCP 注册表,名称为 io.github.webweaver-nexus/webweaver-mcp-server。请参阅发布到 MCP 注册表。

工具

工具类型描述
get_contact_formMCP App(UI)在宿主内部原生呈现 WebWeaver Nexus 联系表单
get_product_overview普通工具返回 WebWeaver Nexus 的功能说明
get_contact_info普通工具返回联系方式和链接
submit_contact_form仅限 App提交表单。标记为 _meta.ui.visibility: ["app"],因此兼容的宿主会对模型隐藏此工具

表单在 App 自身的文档中呈现,不会发出外部请求。表单通过 MCP 桥接(app.callServerTool)提交给 submit_contact_form,后者使用 bearer 密钥向落地页的 /api/contact 发起服务器到服务器的 POST 请求。这就是它能在先前的 Tally iframe 无法运行的环境中正常工作的原因——请参阅已知限制。

前置条件

  • Node.js 24.x(参见 package.json 中的 engines)
  • npm

engines 特意指定为 24.x:Vercel 会根据它选择函数运行时。较新的本地 Node 主版本也能正常运行,但会导致 npm 打印 EBADENGINE 警告。

安装

npm install

构建

npm run build

此步骤使用 Vite + vite-plugin-singlefile 打包 MCP App UI,对客户端和服务端进行类型检查,并生成编译后的服务端。

Vite 步骤会先运行,这是构建必不可少的一环:构建插件会将打包后的 HTML 写入 generated/mcp-app-html.ts,server.ts 会导入该文件。若未先运行此步骤,服务端类型检查将失败。generated/ 是构建产物,已加入 Git 忽略列表。

运行 npm run dev 时,监视构建会在每次客户端变更时重新生成该模块;但 npm run serve 只会在启动时加载一次,因此修改 UI 后需要重启服务端。

运行

# HTTP transport (default, port 3001)
npm run serve

# Custom port
PORT=4000 npm run serve

# stdio transport (for Claude Desktop local config)
npm run serve:stdio

# Dev mode (watch + serve)
npm run dev

服务端默认监听 http://localhost:3001/mcp。

配置

环境变量

有两个环境变量,都用于联系 API,且仅在 server.ts 中读取。本地开发时,请将 .env.example 复制为 .env。

变量用途
CONTACT_FORM_SHARED_SECRET用于访问落地页 /api/contact 的 Bearer 令牌。必须与该项目中的值一致。
CONTACT_API_BASE_URL表单提交的目标地址。默认为 https://webweaver-nexus.vercel.app;末尾的斜杠会被去掉。

密钥绝不能进入客户端包。 src/mcp-app.ts 会被编译为约 246 KB 的单个 HTML 字符串,并提供给每位用户。每次构建后:

grep -c 'CONTACT_FORM_SHARED_SECRET' generated/mcp-app-html.ts   # must print 0

通过 callServerTool 而不是从 App 直接请求 API,正是确保凭据留在服务端的关键。

开发时,请将 CONTACT_API_BASE_URL 指向本地落地页。 如果使用生产环境,每次测试提交都会向生产 Supabase 表写入真实数据行,并发送两封真实邮件。此服务的 Vercel 预览部署也会指向生产环境——请谨慎使用,并在之后清理数据行(这些数据行带有 source='mcp')。

npm run serve 会从工作目录加载 .env。stdio 入口的 cwd=/,无法找到该文件,因此 stdio 宿主配置需要有自己的 env 块——请参阅 Claude Desktop。

表单契约

落地页拥有表单契约,并通过 GET /api/contact/schema 发布。此仓库在 src/contact-contract.ts 中保存了一份已生成并提交的副本:

npm run sync:contract    # regenerate from the endpoint
npm run check:contract   # fail if the copy has drifted

切勿手动编辑该文件。8 个目标选项字符串也是落地页欢迎邮件中 GOAL_PARAGRAPHS 的键,因此哪怕只有一个字节不一致,也会悄无声息地丢失个性化段落,且不会在任何地方报错。提交这份副本而不是在构建时获取,是为了让 npm run build 能在离线环境和全新克隆的仓库中运行;check:contract 是发布步骤,不是构建步骤。

使用 MCP Inspector 测试(默认测试工具)

MCP Inspector 是官方 MCP 调试工具,几乎适用于此处的所有检查。已针对 v2.5.0 验证。

npx @modelcontextprotocol/inspector

Web UI 会在端口 6274 上打开。设置:

  • 传输类型: Streamable HTTP
  • 连接类型: Via Proxy 或 Direct——两者都可用。Proxy 会通过 Inspector 的本地代理(端口 6277)路由 JSON-RPC;Direct 则采用浏览器 → 服务的连接方式。
  • URL: http://localhost:3001/mcp(本地)或 https://webweaver-nexus-mcp.vercel.app/mcp(生产环境)

点击 Connect,然后在各个选项卡中验证:

  1. 工具 — 会显示 get_contact_form、get_product_overview、get_contact_info 和 submit_contact_form。最后一个标记为仅应用可用;Inspector 会列出它,因为按 visibility 过滤是宿主的职责,而非服务的职责。调用两个普通工具时,它们会返回文本。
  2. 资源 — 会列出 ui://get-contact-form/mcp-app.html;读取后会返回约 246 KB 的打包 HTML,并带有 _meta.ui.prefersBorder,完全没有 csp 键。
  3. 应用 — 选择 get_contact_form。表单会以可交互的实时控件呈现,且网络面板保持为空。Inspector 会移除 allow-same-origin,因此这里的控件能正常工作,也证明旧的 Tally 故障源于 Tally 自身的存储访问,而非结构性的沙盒限制。

此外还提供了 CLI,可在无需浏览器的情况下将部署后的检查编写成脚本:

npx @modelcontextprotocol/inspector --cli --transport http \
  --server-url https://webweaver-nexus-mcp.vercel.app/mcp --method tools/list

# App metadata for a UI tool
npx @modelcontextprotocol/inspector --cli --transport http \
  --server-url https://webweaver-nexus-mcp.vercel.app/mcp \
  --method tools/call --tool-name get_contact_form --app-info

**Inspector 无法做到的一件事:**接收 ui/update-model-context。截至 v2.5.0,它既不会注册 onupdatemodelcontext 处理程序,也不会声明该能力,因此 app.updateModelContext() 无处生效。只有这一项检查需要使用 basic-host。

使用 basic-host 测试(模型上下文更新)

MCP Apps 的 basic-host 测试工具副本已随仓库提供,位于 tools/basic-host/。它是唯一声明 updateModelContext 能力并会显示 📋 模型上下文面板的本地宿主,可用于确认表单在提交后确实会通知宿主模型。

# once
cd tools/basic-host && npm install && npm run build

# Terminal 1 — the server under test
npm run build && npm run serve

# Terminal 2 — the harness
cd tools/basic-host && SERVERS='["http://localhost:3001/mcp"]' npm run serve
# Open http://localhost:8080

改为针对生产环境测试:

cd tools/basic-host && SERVERS='["https://webweaver-nexus-mcp.vercel.app/mcp"]' npm run serve

SERVERS 是一个 JSON 数组,端口 8080/8081 实际上是固定的——有关其来源和完整注意事项,请参阅 tools/basic-host/README.md。

模型上下文面板会一直隐藏,直到收到第一次更新。对于 get_contact_form,这意味着必须实际提交表单——此操作会写入一行数据并发送通知邮件。请将 CONTACT_API_BASE_URL 指向本地落地页,并使用一次性信息。

何时使用哪种工具

需求使用
“服务能否访问?工具是否已列出?”Inspector(脚本可使用 --cli)
“普通工具返回的文本是否正确?”Inspector
“表单是否能正常呈现,控件是否可用?”Inspector
“用户能否实际填写并提交表单?”basic-host,或 Claude Desktop——两者都声明了 serverTools
“app.updateModelContext() 能否到达宿主?”basic-host——Inspector 无法做到
“它能否在客户实际使用的宿主中正常工作?”Claude Desktop,通过本地 stdio 配置
部署后最快的基本检查Inspector --cli
经验证的宿主支持情况

每次发布时都应重新检查;以下是观察结果,并非保证。

宿主表单可渲染控件可用serverTools(提交)遵循 visibility: ["app"]
Claude Desktop✅✅✅✅
tools/basic-host✅✅✅不适用(显示所有工具)
MCP Inspector✅✅✅❌ 会列出该工具
claude.ai connector✅✅✅✅

Claude Desktop 和 Inspector 已于 2026 年 10 月 5 日验证,并于 10 月 6 日在 v2.1.0 上重新验证;claude.ai 已于 10 月 6 日通过 cloudflared 隧道验证。Inspector 还无法接收 ui/update-model-context。

将本地开发服务暴露给 claude.ai(Cloudflare 隧道)

如果要针对 claude.ai 的自定义连接器 UI 在本地迭代服务器(该 UI 仅接受公共 HTTPS URL,不接受 localhost),可通过 Cloudflare 隧道暴露开发服务器:

# Terminal 1 — run the server
npm run build && npm run serve

# Terminal 2 — start the tunnel
npx cloudflared tunnel --url http://localhost:3001

从隧道输出中复制 https://*.trycloudflare.com URL。在 Claude 设置中,添加一个指向 https://<tunnel-url>/mcp 的自定义 MCP 连接器。

*.trycloudflare.com 已加入 api/mcp.ts 中 Host 标头检查的允许列表——cloudflared 转发的是公共主机名,而不是 localhost,因此如果没有该条目,所有经隧道转发的请求都会被拒绝,并返回 403 Invalid Host。

注意: get_contact_form 完全没有声明 CSP,因此阻止此前 Tally 嵌入的 frameDomains 问题不再适用。需要留意的是宿主是否声明了 serverTools 能力——如果没有,App 会显示“在网页上继续”的备用选项,而不是提交按钮。请参阅已知限制。

部署(Vercel)

MCP 服务使用 Express + Streamable HTTP 模式部署为 Vercel 无服务器函数。

工作原理:

  • vercel.json 将流量路由到 api/mcp.ts,该文件将 Express 应用导出为 default。
  • Vite 打包的 MCP App UI 会编译为字符串常量(generated/mcp-app-html.ts)并嵌入函数,因此运行时无需从磁盘读取文件。
  • 函数运行在 Vercel 的 Node.js 24 运行时上,maxDuration: 60(远超所需;工具调用会在毫秒内返回)。
  • 无需环境变量——所有配置都硬编码在源代码中。

**重新部署:**推送到 main 分支。Vercel 会在推送后自动部署。

验证部署:

# Sanity check — should return a JSON-RPC tools list (~880 bytes)
curl -X POST https://webweaver-nexus-mcp.vercel.app/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

# Resource read — should return ~246 KB of bundled HTML.
# A response under a few KB means the unbundled source HTML was inlined
# instead of the bundle (the build guards against this — see git history).
curl -X POST https://webweaver-nexus-mcp.vercel.app/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"resources/read","params":{"uri":"ui://get-contact-form/mcp-app.html"},"id":1}' \
  | wc -c

如需交互式的等效验证方式,可针对已部署的 URL 运行 MCP Inspector(参见上文使用 MCP Inspector 测试(默认测试工具))。建议在启动 basic-host 之前,先将其作为部署后的首项检查。

发布到 MCP 注册表

注册表条目为 io.github.webweaver-nexus/webweaver-mcp-server,定义在 server.json 中。发布只能手动触发——请在 Actions 选项卡中运行 Publish to MCP Registry 工作流。

# validate locally before dispatching
mcp-publisher validate

为何通过 CI 发布,而不是在本地电脑上发布。mcp-publisher login github 只有在 GitHub 通过 GET /user/memberships/orgs?state=active 将你识别为组织 Owner 时,才会授予组织命名空间(上游的 internal/api/handlers/v0/auth/github_at.go 中,注册表要求 role == "admin")。尽管该账户确实是 Owner、拥有公开成员身份,且组织的第三方策略并非原因,但此查询对该组织没有返回结果,因此个人登录只能获得 io.github.<user>/*,发布会因 403 失败。

GitHub Actions OIDC 可绕过这一问题:注册表会根据 repository_owner 声明推导命名空间;对于此仓库,该值为 webweaver-nexus,因此可直接授予 io.github.webweaver-nexus/*。该工作流需要 id-token: write。

触发发布前,请先递增 server.json 中的 version——注册表会拒绝重新发布已存在的版本。

在 MCP 客户端中安装

所有客户端都连接到同一个端点:

https://webweaver-nexus-mcp.vercel.app/mcp

传输方式为 Streamable HTTP。无需身份验证;所有已注册的工具都可公开调用,包括 submit_contact_form——请参阅 visibility 是提示,而非访问控制。

原生支持 Streamable HTTP 的主机(claude.ai、ChatGPT、MCP Inspector、basic-host)可直接使用该 URL。仅支持 stdio 的主机(目前包括 Claude Desktop、Cursor)需使用 mcp-remote npm 垫片——这是一个小型软件包,会以本地 stdio 进程的形式启动,并将 JSON-RPC 请求代理到远程 URL。

Claude Desktop

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json(macOS)或 %APPDATA%\Claude\claude_desktop_config.json(Windows):

{
  "mcpServers": {
    "webweaver-nexus": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://webweaver-nexus-mcp.vercel.app/mcp"]
    }
  }
}

重启 Claude Desktop;三个面向模型的工具会显示在工具菜单中。submit_contact_form 不会显示——Claude Desktop 会遵守 visibility: ["app"]。

若要将此服务作为本地 stdio 进程运行——这也是测试尚未发布的更改时的方式——请将配置指向构建后的入口点,并一并设置环境变量,因为 stdio 主机会以 cwd=/ 启动进程,导致无法找到 .env:

{
  "mcpServers": {
    "webweaver-nexus-local": {
      "command": "node",
      "args": ["/absolute/path/to/webweaver-mcp-server/dist/main.js", "--stdio"],
      "env": {
        "CONTACT_API_BASE_URL": "http://localhost:3000",
        "CONTACT_FORM_SHARED_SECRET": "local-dev-secret"
      }
    }
  }
}

上面的 mcp-remote 配置通过 HTTP 代理到已部署的服务,因此不会触发 v1.0.1 修复的 stdio cwd 问题——该问题仅影响这种本地 stdio 配置。

claude.ai(自定义连接器)

claude.ai 原生支持 Streamable HTTP,无需垫片。

  1. 打开 Settings → Connectors(或 Feature Preview → MCP,具体取决于分阶段推出情况)。
  2. 选择 Add custom connector → 粘贴 https://webweaver-nexus-mcp.vercel.app/mcp。
  3. 保存。

三个面向模型的工具都可使用。get_contact_form 能否提交,取决于 claude.ai 是否声明了 serverTools 能力——请参阅已知限制。

Cursor

编辑 ~/.cursor/mcp.json,或在 Cursor 界面中使用 Settings → MCP Servers:

{
  "mcpServers": {
    "webweaver-nexus": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://webweaver-nexus-mcp.vercel.app/mcp"]
    }
  }
}

重新加载 Cursor 窗口(Cmd+Shift+P → Reload Window)。

ChatGPT

适用于支持 MCP / 自定义连接器的订阅计划。

  1. Settings → Connectors(或 Apps)→ Add custom MCP server。
  2. 粘贴 https://webweaver-nexus-mcp.vercel.app/mcp。
  3. 在对话中启用。
验证连接(任意客户端)

向主机提出类似这样的请求:

使用 WebWeaver Nexus 连接器获取产品概览。

如果工具成功调用并返回文本,说明连接正常。对于 get_contact_form,已支持 MCP Apps 渲染的主机会直接在对话中显示表单——请参阅上文各客户端的注意事项。

已知限制

Claude Desktop stdio 集成 — 已在 v1.0.1 中修复

此前: join_waitlist(现为 get_contact_form)在 Claude Desktop 中会失败,并报错 ENOENT: no such file or directory, open '/dist/mcp-app.html'。Claude Desktop 在 macOS 上启动 MCP 服务进程时,会将工作目录设为 /,而 server.ts 通过 path.join(process.cwd(), "dist", "mcp-app.html") 定位打包后的 HTML。曾尝试在 claude_desktop_config.json 中设置 cwd 作为变通方案,但在测试的版本中该设置会被静默忽略。

修复: Vite 构建现在会将打包后的 HTML 写入 generated/mcp-app-html.ts,server.ts 将其作为字符串常量导入。运行时不再解析任何文件系统路径,因此 App 资源在 stdio、本地 HTTP 和 Vercel 环境中的行为完全一致。已通过从 / 运行服务并经由 stdio 读取资源进行验证。

此前的两次尝试保留在历史记录中,以供了解背景:一次路径探测(在 e73484f 中添加,在 32dc1aa 中撤销)在 Vercel 上解析到了未打包的源文件;以及一次将 Vite 8 降级到 7 的尝试(2e33c8e,在 c8ad36d 中撤销),其排查方向有误。现在,如果构建误将未打包的源 HTML 内联,构建流程会明确报错。

MCP Inspector 会剥除 allow-same-origin——不再导致表单失效

Inspector 的沙箱代理在自己的源代码中明确说明:App iframe 在沙箱中运行时不带 allow-same-origin,这个标志「始终会从服务器提供的值中剥除」。因此,App 运行在不透明(null)源上。

这过去会导致表单失效。旧版 Tally iframe 会继承不透明源(沙箱标志会由嵌套框架继承),其 JS 无法访问同源存储,导致下拉菜单和复选框失灵,而纯文本输入框仍可正常使用。当时通过仅更改该标志进行 A/B 测试,确认了这一点。

原生表单不受影响,因为原生控件不需要存储。但仍有一项限制必须遵守:在不透明源下,localStorage、sessionStorage 和 document.cookie 都会抛出 SecurityError,这就是为什么 src/mcp-app.ts 中的 RENDERED_AT 是模块作用域常量,而且必须保持如此。

不要用 _meta.ui.domain 来「修复」沙箱。 Inspector 确实会为声明该字段的 App 授予 allow-same-origin,但规范明确指出,其格式取决于具体主机({hash}.claudemcpcontent.com、www-example-com.oaiusercontent.com)。该值由各主机分配,因此猜一个值或许能满足 Inspector,却可能导致其他主机出问题。这里不需要它。

忽略 frameDomains 的主机——已在 v2.0.0 中通过移除依赖解决

受影响的 claude.ai 自定义连接器和 Claude Desktop。 两者都能正确渲染 App 外壳,但嵌入的 Tally 表单却只显示一片空白:主机会忽略 _meta.ui.csp 中声明的 frameDomains,因此嵌套的 tally.so iframe 被拦截。上游问题 anthropics/claude-ai-mcp#40 仍处于开放状态。

为何现在不再适用。 表单现在直接在 App 自身的文档中原生渲染,且不声明任何 csp。我们不依赖的策略自然也就不会被忽略。保留这段说明,是因为它会限制未来的工作:绝不要在 MCP App 中放置第三方 iframe——如果我们把自己的表单放进 iframe,也会遇到完全相同的问题,因为问题出在嵌套框架这一形式,而非 Tally 本身。

Claude Desktop 使用相同的技术栈——其 initialize 会发送 clientInfo.name: "claude-ai (via mcp-remote …)"——因此也会受到同一问题影响。它确实声明支持 MCP Apps(extensions["io.modelcontextprotocol/ui"],值为 text/html;profile=mcp-app),而且 MCP 日志显示 resources/read 和 tools/call 都正常返回,因此这表现为渲染故障,而非协议故障。

已从 Claude Desktop 的 DevTools 控制台确认:

Framing 'https://tally.so/' violates the following Content Security Policy
directive: "frame-src 'self' blob: data:". The request has been blocked.

只有 frameDomains 被丢弃,另外两项声明都保留了下来。App 框架通过查询字符串加载了这些声明:

mcp_apps?connect-src=https%3A%2F%2Ftally.so&resource-src=https%3A%2F%2Ftally.so+https%3A%2F%2Fassets.claude.ai

没有 frame-src 参数,因此策略回退为 frame-src 'self' blob: data:。Tally 的加载器确实运行了,App 也请求了正确的嵌入 URL——是主机拦截了框架,上游环节没有问题。两种传输方式的结果相同:HTTP 上的 mcp-remote 和本地 stdio 配置皆如此,符合渲染器侧拦截的预期。

已确认 Claude Desktop 于 2026 年 10 月 5 日修复。 现在,相同的 App 框架 URL 显示为:

mcp_apps?stable-origin=true&resource-src=https%3A%2F%2Fassets.claude.ai&dev=true

resource-src 中不再有 tally.so,也完全没有 connect-src 参数,因为 App 两者都不需要。整个会话期间,DevTools 控制台没有 CSP 违规记录——唯一的策略提示是 Claude Desktop 自身的 Unrecognized Content-Security-Policy directive 'webrtc'。表单成功渲染,控件可正常交互,且提交内容已到达 Supabase,source='mcp'。

注意新增的 stable-origin=true 参数:Claude Desktop 现在会为 App 框架分配稳定源,这正是 _meta.ui.domain 背后的机制。如果这同时带来了 allow-same-origin,最终可能会在此主机上的 App 中使用浏览器存储。但不要依赖这一点——MCP Inspector 仍会剥除该标志,因此下文的不透明源规则依然适用。

/mcp 上的 GET 和 DELETE 返回 405

从 v2.1.0 开始,此端点通过 createMcpHandler 在同一个 URL 上同时支持 2025 协议时代和 2026-07-28 修订版。旧版流量以无状态方式处理——每个请求都会创建一个新的服务实例——因此没有可供 2025 版会话操作使用的会话,所以 GET 和 DELETE 会返回 405 Method not allowed。此前它们返回 200。

这是 SDK 文档中说明的无状态惯用模式,并非故障。已验证不会影响真实的 v1.30.0 SDK 客户端、tools/basic-host、Claude Desktop 或 claude.ai——它们都不需要该流来处理请求/响应。在两个 Claude 宿主中,表单均正常呈现并提交,且请求到达了 Supabase;两个控制台中都没有出现 405。POST 未作更改。

客户端默认使用 2025 握手,不会进行探测,因此客户端无需作任何更改。选择启用 versionNegotiation: { mode: 'auto' } 的客户端则会协商使用 2026-07-28;两者都能从同一个 URL 获取相同的工具。

不具备 serverTools 能力的宿主

app.callServerTool() 要求宿主声明 serverTools;如果没有该能力,App 就无法提交。Claude Desktop 已声明该能力——已于 2026 年 10 月 5 日验证,提交流程端到端正常完成。tools/basic-host 也已声明该能力。该能力在 claude.ai 和其他宿主上仍未经验证,而且宿主可能撤回该能力,因此保留了检查逻辑。

src/mcp-app.ts 会在加载时而非提交时探测此能力;如果该能力不存在,就会将表单替换为“在网页上继续”的入口,通过 app.openLink() 打开 /#contact(此调用本身受 openLinks 控制;如果也不具备该能力,则会显示可供选择的 URL 文本)。这里的顺序是有意安排的:提交按钮按下后悄无声息地无反应的表单,比没有表单更糟,因为它看起来像是正常工作了。

由于受测宿主都具备该能力,这一回退机制只是保险措施,尚未在真实宿主中实际使用过。 目前仅通过在本地模拟该能力进行了确认。如果要修改这部分逻辑,请重新检查。

visibility: ["app"] 是提示,而非访问控制

submit_contact_form 声明了 _meta.ui.visibility: ["app"],而且 Claude Desktop 遵守了这一设置:在被要求列出其工具时,模型只列出了 get_contact_form、get_contact_info 和 get_product_overview;此前它刚刚在 App 内调用过提交工具(已于 2026 年 10 月 5 日验证)。

这是宿主的自愿配合,并非强制执行。tools/list 会向每个客户端返回该工具——服务端不会进行筛选,规范将此责任交给宿主。忽略该标记的宿主会将工具暴露给其模型。此情况可以接受,但并非毫无风险:consentPrivacy 必须严格等于 true,且落地页会将此路径上的每次提交都记录为 source='mcp',因此任何合成内容都可以被识别。不要将此标记视为安全边界。

项目结构

├── api/ │ └── mcp.ts # Vercel 无服务器入口 — 导出 Express 应用 ├── public/ │ └── index.html # 在 / 提供的静态落地页(不属于 MCP 服务) ├── main.ts # 本地开发入口 — HTTP 和 stdio 传输 ├── server.ts # 工具和资源注册(共享) ├── mcp-app.html # 应用 UI 模板(Vite 入口,源文件) ├── src/ │ ├── mcp-app.ts # 客户端应用生命周期 + 原生联系表单 │ ├── contact-contract.ts # 已生成 — 从落地页引入的表单契约 │ ├── submit-outcome.ts # server.ts 和应用共享的结果类型 │ ├── mcp-app.css # 应用专属样式 │ └── global.css # 宿主变量回退值和重置样式 ├── scripts/ │ └── contract.ts # 同步/检查引入的契约 ├── tools/ │ └── basic-host/ # 引入的 MCP 宿主测试框架(模型上下文更新) ├── generated/ # 构建产物 — 作为 TS 常量的打包 HTML(由 git 忽略) ├── dist/ # 构建输出 — 函数代码和打包后的 mcp-app.html ├── vite.config.ts # Vite 和 singlefile 插件配置 ├── tsconfig.json # 客户端和共享类型检查 ├── tsconfig.server.json # 服务端声明生成 ├── tsconfig.scripts.json # scripts/ 的类型检查(noEmit) ├── .env.example # CONTACT_API_BASE_URL、CONTACT_FORM_SHARED_SECRET ├── vercel.json # Vercel 部署配置 └── package.json

版本历史

  • v2.1.0 — 从单体版 @modelcontextprotocol/sdk 迁移到 v2 拆分包(server / node / express / client / core,全部精确锁定版本),并将 ext-apps 从 1.7.5 升级到 2.0.3。现在,端点通过 createMcpHandler 在同一个 URL 上提供协议修订版 2026-07-28 以及 2025 年版本;客户端默认使用 2025 年握手,不进行探测,因此客户端无需做任何更改。现在,对 /mcp 执行 GET 和 DELETE 会返回 405——请参阅已知限制。仅因 ext-apps 更精简,打包后的 App 就从 447 KB 缩减到 246 KB。tools/basic-host 特意保留在 SDK v1 上,作为跨版本兼容性测试,并且测试通过。工具、架构和行为均未更改。
  • v2.0.0 — 不兼容变更: join_waitlist 更名为 get_contact_form,其资源 URI 现在为 ui://get-contact-form/mcp-app.html;没有别名,因此缓存的客户端会报错,直到重新获取列表。Tally iframe 已替换为在 App 中原生渲染的表单,不会发出任何外部请求,因此无需声明 _meta.ui.csp——这是针对忽略 frameDomains 的宿主的结构性修复。提交操作通过新的、仅供 App 使用的 submit_contact_form 工具(_meta.ui.visibility: ["app"])发送服务器到服务器的 POST 请求至落地页的 /api/contact,因此共享密钥不会进入客户端包。新增仓库中的首批两个环境变量,以及通过 npm run check:contract 检查的 vendored 表单契约。还移除了一个没有来源检查、会信任任何声称提交了 Tally 表单的 iframe 的 postMessage 监听器。
  • v1.0.2 — 修复了 join_waitlist 在所有宿主中都渲染为空白面板的问题:App 加载了 Tally 的 embed.js,却从未调用 Tally.loadEmbeds(),因此表单 iframe 从未获得 src。此外,现在仅当宿主具备相应能力时才调用 updateModelContext,并将 basic-host 测试框架 vendored 到 tools/basic-host/,因此不再需要克隆 ext-apps。
  • v1.0.1 — join_waitlist 现在可通过 stdio 工作(Claude Desktop)。App HTML 现在会编译到服务中,而不是从磁盘读取,从而移除了对 process.cwd() 的依赖。速率限制现在按客户端分别计算:此前未设置 trust proxy,因此在 Vercel 代理后面的所有调用方共用一个每分钟 60 次的限流桶。宿主验证现在接受 Vercel 预览部署和 *.trycloudflare.com,文档中介绍的隧道工作流需要这些域名。更新依赖以清除所有 npm audit 警告(MCP SDK 1.29 → 1.30)。
  • v1.0.0 — 以 io.github.webweaver-nexus/webweaver-mcp-server 名称发布到官方 MCP 注册表。通过 CORS 允许列表和速率限制强化端点安全性。
  • v0.1.0 — 首次成功部署到生产环境。三个工具已上线;join_waitlist 可在 Streamable HTTP 宿主上使用。Claude Desktop stdio 集成推迟实现。

评论

支持 Markdown

第一次接触 MCP?

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

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