JezwebPortfolio-ChatGPT应用程序SDK演示
概念验证ChatGPT应用程序,演示了带有MCP(模型上下文协议)的OpenAI Apps SDK
这个项目展示了如何构建直接在ChatGPT对话中呈现的交互式小部件。作为学习练习,在构建更复杂的应用程序之前了解Apps SDK架构。
这表明了什么
- MCP服务器:ChatGPT的JSON-RPC 2.0协议实现
- 交互式小部件:在ChatGPT的iframe沙箱中呈现的React组件
- 真实数据集成:用于投资组合项目的WordPress REST API
- 潜在客户捕获:Cloudflare D1数据库存储的联系表单
- 边缘优先:Cloudflare Workers为小部件资产提供全球CDN
架构概述
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ User: "Show me Jezweb's portfolio" │ │
│ │ │ │
│ │ ChatGPT calls → show_portfolio tool │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Portfolio Widget (iframe) │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │Card │ │Card │ │Card │ ← React Carousel │ │ │
│ │ │ └─────┘ └─────┘ └─────┘ │ │ │
│ │ │ [◀] [▶] │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Workers (Edge) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ /mcp endpoint │ │ /api/contact │ │
│ │ (JSON-RPC 2.0) │ │ (Direct API) │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ ┌────────▼─────────────────────▼─────────┐ │
│ │ Hono Server │ │
│ │ • tools/list, tools/call │ │
│ │ • resources/list, resources/read │ │
│ └────────┬─────────────────────┬─────────┘ │
│ │ │ │
│ ┌────────▼─────────┐ ┌────────▼─────────┐ │
│ │ WordPress API │ │ D1 Database │ │
│ │ (Portfolio) │ │ (Leads) │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘主要学习内容
1.MCP协议基础
模型上下文协议使用JSON-RPC 2.0。关键方法:
| 方法 | 目的 |
|---|---|
initialize | 与协议版本握手 |
tools/list | 用模式宣传可用的工具 |
tools/call | 使用参数执行工具 |
resources/list | 列出可用的小部件资源 |
resources/read | 返回小部件HTML内容 |
2.小部件集成(至关重要!)
小部件由HTML提供 mimeType: "text/html+skybridge"ChatGPT
- 调用您的工具
- 从以下位置读取小部件资源
openai/outputTemplate统一资源标识符 - 在iframe沙箱中呈现HTML
- 通过以下方式注入数据
window.openai.toolOutput
重要元数据字段:
_meta: {
"openai/outputTemplate": "ui://widget/portfolio.html", // Widget URI
"openai/widgetDescription": "Description for ChatGPT", // Reduces narration
"openai/widgetAccessible": true, // Widget can call tools
"openai/resultCanProduceWidget": true, // Tool produces widget
"openai/toolInvocation/invoking": "Loading...", // Loading text
"openai/toolInvocation/invoked": "Ready", // Complete text
}3.小部件数据访问
ChatGPT通过 structuredContent 作为 window.openai.toolOutput:
// In your widget React component:
useEffect(() => {
// Direct path (ChatGPT flattens structuredContent)
const projects = window.openai?.toolOutput?.projects;
// Listen for updates
window.addEventListener('openai:set_globals', (event) => {
const data = event.detail?.globals?.toolOutput;
});
}, []);4.布局控制
ChatGPT通过以下方式提供布局约束 window.openai:
// Read constraints
const maxHeight = window.openai?.maxHeight;
const displayMode = window.openai?.displayMode; // 'inline' | 'pip' | 'fullscreen'
// Request more space
await window.openai?.requestDisplayMode({ mode: 'fullscreen' });5.从小部件调用工具
这太棘手了! window.openai.callTool() 仅适用于生成小部件的工具。对于仅数据操作(如我们的联系人表单),请改用直接API端点:
// DON'T rely on this for non-widget tools:
// await window.openai.callTool('contact_about_project', data);
// DO use direct API:
await fetch('https://your-worker.workers.dev/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});6.CORS配置
Widget沙盒有着不同寻常的起源。对小部件可访问的端点使用许可CORS:
app.use('/api/*', cors({
origin: '*', // Widget sandbox can have null/blob origins
allowMethods: ['GET', 'POST', 'OPTIONS'],
allowHeaders: ['Content-Type'],
}));7.小部件资产服务
从与MCP服务器相同的源提供CSS/JS,以避免CSP问题:
技术栈
| 层 | 技术 |
|---|---|
| 运行时 | Cloudflare Workers+静态资产 |
| 前端 | React 19+Vite 7+顺风v4 |
| UI组件 | shadcn/UI+根UI |
| 后端 | Hono 4 |
| 数据库 | Cloudflare D1+Drizzle ORM |
| 验证 | Zod |
| 数据源 | WordPress REST API |
项目结构
chatgpt-app-sdk/
├── src/
│ ├── client/ # React frontend (dev UI)
│ │ ├── components/ui/ # shadcn/ui components
│ │ └── types/ # TypeScript types
│ ├── server/ # Hono backend
│ │ ├── routes/mcp.ts # MCP endpoint handler
│ │ ├── tools/ # Tool implementations
│ │ │ ├── portfolio.ts # show_portfolio tool
│ │ │ └── contact.ts # contact_about_project tool
│ │ └── index.ts # Server entry
│ ├── lib/
│ │ ├── mcp/ # MCP protocol (types, server)
│ │ ├── db/ # Drizzle schema
│ │ └── wordpress/ # WordPress API client
│ └── widgets/ # Widget entry points
│ └── PortfolioWidget.tsx
├── docs/ # Architecture docs
├── wrangler.jsonc # Cloudflare config
├── vite.config.ts # Main Vite config
└── vite.widget.config.ts # Widget bundle config发展
先决条件
- Node.js 18+和pnpm
- Cloudflare帐户
- 牧马人CLI
设置
# Clone and install
git clone https://github.com/jezweb/chatgpt-app-sdk.git
cd chatgpt-app-sdk
pnpm install
# Create D1 database
npx wrangler d1 create chatgpt-portfolio-db
# Update wrangler.jsonc with database ID
# Run migrations
pnpm db:generate
npx wrangler d1 execute chatgpt-portfolio-db --local --file=drizzle/0000_*.sql
# Start dev server
pnpm dev构建和部署
# Build for production (includes widget bundle)
CLOUDFLARE_ENV=production pnpm build
# Deploy to Cloudflare
npx wrangler deploy测试MCP端点
# List tools
curl -X POST https://your-worker.workers.dev/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
# Call show_portfolio
curl -X POST https://your-worker.workers.dev/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"show_portfolio","arguments":{}}}'Gotchas&解决方案
1.小部件未渲染
问题:小部件在ChatGPT中显示空白或错误。 解决方案:检查 mimeType: "text/html+skybridge" 在资源/读取响应中。
2.数据未加载
问题: window.openai.toolOutput 未定义。 解决方案:使用事件侦听器 openai:set_globals 因为数据可能会在小部件之后加载。
3.联系表400错误
问题: window.openai.callTool() 非小部件工具返回400。 解决方案:使用直接API端点,而不是MCP调用工具。
4.外键错误
问题:由于FK约束,数据库插入失败。 解决方案:如果使用外部ID(例如WordPress帖子ID),请删除FK引用。
5.CSP冻结资产
问题:CSS/JS被内容安全策略阻止。 解决方案:为与MCP服务器来自同一来源的资产提供服务。
6.ChatGPT过度叙述
问题:ChatGPT在小部件下方添加冗余文本。 解决方案:添加 openai/widgetDescription 元数据和简化工具响应文本。
开发时间表
| 阶段 | 描述 | 状态 |
|---|---|---|
| 1 | 项目设置(Vite+Cloudflare) | ✅ |
| 2 | 数据库设置(D1+细雨) | ✅ |
| 3 | MCP服务器(JSON-RPC 2.0) | ✅ |
| 4 | 投资组合小部件(React旋转木马) | ✅ |
| 5 | 小部件MCP集成 | ✅ |
| 6 | 联系表格 | ✅ |
| 7 | 造型和波兰语 | ✅ |
| 8 | 内容哈希 | ⏸️ |
| 9 | 生产部署 | ✅ |
| 10 | 文件 | ✅ |
资源
未来想法
这个POC为许多可能性打开了大门:
- 带购物车的电子商务产品浏览器
- 实时仪表板小部件
- 具有多步流程的交互式表单
- 地图/基于位置的界面
- 带注释的文档查看器
- 日程安排/预订小部件
许可证
MIT许可证-将其用作您自己的ChatGPT应用程序的模板!
作者
杰里米·道斯 (耶稣)
- 网站:https://www.jezweb.com.au
- 电子邮件:jeremy@jezweb.net
- github: @jweb
______________________________________________________________________
内置于 克劳德代码 AI助手
