创建远程MCP连接器 是做什么的?
构建一个无状态、基于Streamable-HTTP的MCP服务器(TypeScript,@modelcontextprotocol/sdk),作为claude.ai的自定义连接器运行,可选地包含内联可视化卡片(MCP Apps),并将其部署到Vercel。已在包含30多个工具的实际项目中得到验证。
模板位于此文件旁边的 templates/ 目录中。更详细的说明在 references/ 中;仅在步骤需要时阅读:
references/mcp-apps-cards.md— 内联卡片:ui://资源、postMessage协议、CSP、可点击列表references/deploy-vercel.md— Vercel设置、部署、在claude.ai中重新添加连接器references/gotchas.md— 实际会耗费时间的陷阱(调试前阅读)
工作流程
- 澄清(仅询问你无法推断的内容):什么数据/API、只读还是写入、是否需要认证、是否需要卡片或仅文本。默认:公共数据、只读、无认证、文本+卡片。
- 在编写代码之前探测每个上游API。 对每个端点执行
curl:状态、响应结构、日期格式、速率限制、超时、重定向(301表示URL已更改,例如api.frankfurter.dev,而不是.app)。公共SPARQL/搜索端点可能很慢:测量时间,保持查询简单,在代码中过滤。 - 从
templates/搭建脚手架(复制package.json、tsconfig.json、vercel.json、api/、src/、scripts/),重命名为my-connector,执行npm install。 - 在
src/server.ts中编写工具(每个工具一个registerTool)。遵循以下规则。 - 将每个工具添加到
scripts/test-client.ts中。 运行器会在遇到未测试的工具、空输出和isError时失败。 - 本地验证:执行
npm run typecheck,启动PORT=<新端口> npm run dev,确认日志显示“listening”(而不是EADDRINUSE),然后执行npm test -- http://localhost:<端口>/mcp。目标:Untested tools: none. Problems: 0。 - 文档:README工具表(每个工具一行)、CONTRIBUTING、LICENSE。
- 只有在用户同意后才提交、推送、部署(参见
references/deploy-vercel.md),然后针对实时URL运行测试。 - 告知用户在claude.ai中移除并重新添加连接器(它会缓存工具列表),并提供3-5个简短的测试提示。
工具设计规则
- 少量且真正有效的工具。名称使用
snake_case动词:search_x、get_x、list_x。 - Zod Schema,每个输入都有限制(
min/max、default);用示例描述每个字段。 - 描述用一两句话说明返回值以及何时使用。模型通过它来选择工具。
- 注解:
readOnlyHint: true, idempotentHint: true, destructiveHint: false,对于外部API加上openWorldHint: true。 - 将每个处理程序包装在
safe(...)中:上游故障会变成带可读消息的isError结果,永远不会崩溃。 - 在每个结果中命名数据源。当数据是社区编辑或临时性时,请明确说明。
- 切勿因缺少字段而崩溃;回退为“unknown”。
- 容错:当无匹配时,返回“您是不是要找”的建议,而不是空结果。
- 缓存上游调用(模板中的
getJson/getText具有TTL、重复请求去重、短时故障缓存、超时)。对实时数据使用短TTL(分钟),对静态数据使用小时级TTL。善待免费API。 - 通过辅助函数如
card(text, data)同时返回文本(供模型使用)和structuredContent(供卡片使用)。 - 仓库中无密钥。如果需要密钥,从
process.env读取并记录。 - 在上游速度慢时,在代码中过滤繁重的工作,而不是在上游查询中。
无状态服务器(Vercel友好)
每个请求都会创建一个新的 McpServer 和传输层实例(src/handler.ts);无会话,请求之间无内存。唯一的状态是内存中的缓存,它只是尽力而为。CORS和请求体大小限制在handler中处理。api/mcp.ts 导出 { fetch } 和 maxDuration: 30;vercel.json 将 /mcp 重写为 /api/mcp。
卡片(MCP Apps),简短版本
注册一个 ui://… HTML资源(mimeType: text/html;profile=mcp-app),然后为每个工具添加 `_meta: { ui: { resourceUri },