构建ChatGPT应用,从概念到应用商店提交。
快速开始
新应用? → 从第1阶段(适应性评估)开始
有app-spec.md? → 从第3阶段(实现)开始
应用构建完成? → 从第4阶段(测试)开始
准备发货? → 从第5阶段(部署)开始
第1阶段:适应性评估
目标:确定这个产品是否适合成为ChatGPT应用。
第1步:收集背景
询问用户:
- 你的产品做什么?
- 你的用户是谁?
- 它暴露了哪些API/数据?
- 用户可以执行哪些操作?
第2步:应用Know/Do/Show框架
根据三个价值支柱进行评估(见fit_evaluation.md):
| 支柱 | 问题 | 强烈信号 |
|---|---|---|
| Know | 它是否提供了ChatGPT缺乏的数据? | 实时价格,用户特定数据,内部指标 |
| Do | 它能否执行实际行动? | 创建,更新,删除,发送,安排 |
| Show | 它是否能比文本更好地显示? | 列表,图表,地图,媒体库 |
最低要求:至少有一个支柱必须是强大的。
第3步:检查阻碍因素
查看fit_evaluation.md以了解:
- 禁止类别(赌博,成人,加密货币投机)
- 数据限制(无PCI,PHI,SSN,API密钥)
- 年龄要求(13+受众)
第4步:创建黄金提示集
为发现测试起草提示:
- 5个直接提示:明确命名你的产品(“显示我的TaskFlow任务”)
- 5个间接提示:描述意图而不命名(“我应该做什么工作?”)
- 3个负面提示:类似但不应触发(“创建一个提醒”)
第5步:编写app-spec.md
创建规范文件:
# [产品名称]ChatGPT应用规范
## 产品背景
- 名称:[产品名称]
- API基础:[API URL]
- 认证:[Bearer token / OAuth / 无]
## 价值主张
- 知道:[它提供了哪些数据?]
- 做:[它能执行哪些操作?]
- 显示:[需要什么UI?]
## 黄金提示
### 直接(应触发)
1. ...
### 间接(应触发)
1. ...
### 负面(不应触发)
1. ...
输出:项目目录中的app-spec.md
第2阶段:应用设计
目标:定义完整的技术规范。
第1步:定义工具(2-5)
遵循每个工具一个工作原则。见chatgpt_app_best_practices.md。
对于每个工具,指定:
name: service_verb_noun # 例如,taskflow_get_tasks
title: 人类可读名称
description: 使用此工具时用户想要... [具体]
annotations:
readOnlyHint: true/false
destructiveHint: true/false
openWorldHint: true/false
inputSchema:
param1: 类型 (必需/可选)
param2: enum["a", "b", "c"]
outputStructure:
content: [模型的文本摘要]
structuredContent: {机器可读数据}
_meta: {仅小部件数据}
第2步:决定小部件需求
应用是否需要自定义UI?
| 用例 | 小部件需要? | 组件类型 |
|---|---|---|
| 带复选框的任务列表 | 是 | 带有动作的列表 |
| 仅数据显示 | 也许 | 可以使用文本 |
| 地图,图表,媒体 | 是 | 专门化 |
| 多步骤工作流 | 是 | 有状态的小部件 |
见widget_development.md了解模式。
第3步:计划认证
如果访问用户特定数据或写操作,则需要认证。
- 众所周知的端点设置
- 提供商特定指南(Auth0,Stytch)
- 工具级securitySchemes
第4步:更新app-spec.md
添加技术规范:
## 工具
### 1. service_get_items
- **注释**:readOnlyHint: true
- **输入**:{ status?: "active" | "completed", limit?: 数字 }
- **输出**:
- content: "找到N个项目"
- structuredContent: { items: [...] }
- _meta: { fullData: [...] }
### 2. service_create_item
- **注释**:openWorldHint: true
- **输入**:{ title: 字符串, description?: 字符串 }
- **输出**:{ id, title, created_at }
## 小部件
- 类型:带有动作按钮的列表
- 显示模式:内联,全屏
- 状态:{ selectedId, filter }
## 认证
- 必需:是
- 提供商:Auth0
- 范围:read:items, write:items
输出:带有完整技术规范的更新app-spec.md
第3阶段:实现
目标:生成完整的工作项目。
第1步:初始化项目
从资产中复制并自定义:
# 项目结构
myapp-chatgpt/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # MCP服务器入口
│ ├── tools/ # 工具处理程序
│ ├── widget/ # 小部件源代码
│ └── types/ # TypeScript类型
└── scripts/
└── build-widget.ts # 小部件捆绑器
见node_chatgpt_app.md了解完整模式。
第2步:实现MCP服务器
关键组件(来自assets/server/):
- 带有SSE传输的HTTP服务器(ChatGPT应用必需):
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
// GET /mcp - SSE流连接
// POST /mcp/messages - 消息处理
- 带有JSON Schema的工具定义:
const tools: Tool[] = [{
name: "service_get_items",
title: "获取项目",
description: "当用户想要查看项目时使用...",
inputSchema: { type: "object", properties: {...} },
_meta: {
"openai/outputTemplate": "ui://widget/app.html",
"openai/widgetAccessible": true
},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}];
- 处理器注册:
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// 处理工具调用...
return {
content: [{ type: "text", text: `找到${items.length}个项目` }],
structuredContent: { items: items.slice(0, 10) },
_meta: { fullItems: items }
};
});
第3步:实现小部件
关键模式(来自assets/widget/):
// 访问数据
const output = window.openai.toolOutput;
const meta = window.openai.toolResponseMetadata;
// 调用工具
await window.openai.callTool("service_action", { id: "123" });
// 持久化状态
window.openai.setWidgetState({ selectedId: "123" });
// 布局控制
window.openai.notifyIntrinsicHeight(400);
await window.openai.requestDisplayMode({ mode: "fullscreen" });
见widget_development.md了解React钩子和模式。
第4步:构建
npm install
npm run build # 编译服务器+捆绑小部件
第5步:实施清单
在移动到测试之前,验证:
小部件要求
- [ ] 使用应用SDK UI设计令牌(见apps_sdk_ui_tokens.md)
- [ ] 实现暗模式与CSS变量架构
- [ ] 使用LoadingDots模式加载状态(见widget_ui_patterns.md)
- [ ] 在所有DOM更改后调用
notifyIntrinsicHeight() - [ ] 包括可复制内容的复制按钮反馈
- [ ] 长列表有更多/更少(>3项)
- [ ] 在移动设备上工作(在375px宽度下测试)
- [ ] 加载UI防止重新初始化(见widget_loading_patterns.md)
- [ ] SVG动画使用
.style属性,而不是setAttribute()(见widget_development.md)
安全要求
- [ ] 所有用户输入都经过验证(见security_patterns.md)
- [ ] HTML输出使用安全的DOM方法(textContent, createElement)
- [ ] 外部图像URL通过域白名单和大小限制(200KB)代理
- [ ] 每个会话实现速率限制,带有LRU驱逐
服务器要求
- [ ]
/.well-known/openai-apps-challenge端点返回挑战令牌 - [ ]
/privacy端点返回HTML隐私政策 - [ ]
/terms端点返回HTML服务条款 - [ ]
/mcp端点处理SSE连接 - [ ]
/health或/返回健康检查JSON - [ ] 仅对ChatGPT域配置CORS
- [ ] 所有响应上设置安全头
- [ ] 会话路由使用直接查找bySessionId(关键 - 见troubleshooting.md)
- [ ] 响应大小在300KB以下(删除重复项,限制图像)
生产准备(如果部署到生产)
- [ ] OAuth令牌存储在数据库中,而不是内存中(见oauth_integration.md)
- [ ] 变异工具包括幂等性检查(见chatgpt_app_best_practices.md)
- [ ] 多匹配场景的消歧模式(见chatgpt_app_best_practices.md)
- [ ] 所有变异的确认收据(见chatgpt_app_best_practices.md)
输出:工作目录中的完整项目
第4阶段:测试
目标:验证应用是否正确工作。
第1步:使用MCP Inspector进行本地测试
# 终端1:启动服务器
npm run dev
# 服务器在http://localhost:8000运行
# 终端2:运行检查器
npx @modelcontextprotocol/inspector@latest http://localhost:8000/mcp
验证:
- [ ] 所有工具在检查器中显示
- [ ] 工具调用返回预期结构
- [ ] 小部件渲染无错误
第2步:创建HTTPS隧道
ngrok http 8000
# 复制https://xxx.ngrok.app URL
第3步:创建ChatGPT连接器
- 转到ChatGPT → 设置 → 连接器
- 启用开发者模式(设置 → 应用和连接器 → 高级)
- 创建新连接器:
- 名称:你的应用名称
- 描述:来自app-spec.md
- URL:
https://xxx.ngrok.app/mcp
- 点击创建并验证工具是否显示
第4步:测试黄金提示
在新的ChatGPT对话中:
- 启用你的连接器(+按钮 → 更多 → 选择连接器)
- 测试app-spec.md中的每个黄金提示
- 验证:
- [ ] 直接提示正确触发
- [ ] 间接提示正确触发
- [ ] 负面提示不触发
- [ ] 小部件正确渲染
- [ ] 操作有效(如适用)
第5步:迭代
如果发现问题:
- 修复代码
- 重建:
npm run build - 在ChatGPT设置中刷新连接器
- 重新测试
见troubleshooting.md了解常见问题和解决方案。
输出:在ChatGPT中测试的应用
第5阶段:部署和提交
目标:生产发货和应用商店。
第1步:部署到生产
从assets/deploy/生成部署配置:
Fly.io(推荐):
fly launch
fly deploy
Vercel/Cloudflare:确保流式HTTP支持。
第2步:更新连接器
- 用生产URL替换ngrok URL
- 在ChatGPT设置中验证连接
第3步:提交前清单
必需:
- [ ] 在OpenAI平台上验证组织
- [ ] 所有工具都有清晰的描述
- [ ] 注释正确(readOnlyHint, destructiveHint, openWorldHint)
- [ ] 无禁止内容/商业
- [ ] 无限制数据收集
- [ ] 小部件在移动设备上渲染
- [ ] 准备测试凭据和样本数据
如果需要认证:
- [ ] 众所周知的端点可访问
- [ ] 文档记录测试帐户凭据
- [ ] OAuth流程成功完成
第4步:提交
- 转到platform.openai.com/apps-manage
- 输入MCP服务器URL
- 添加OAuth元数据(如适用)
- 完成提交表单
- 提交审核
第5步:提交后
- 监控电子邮件以获取审核状态
- 处理任何审核员反馈
- 批准后点击发布
输出:ChatGPT应用商店中的应用