带有MCP的ChatGPT待办事项应用
通过这个分步教程,学习如何构建一个ChatGPT应用程序!这个待办事项列表应用展示了如何使用模型上下文协议(MCP)与ChatGPT进行集成。你将了解到如何创建ChatGPT可以调用的MCP工具,构建一个显示在ChatGPT界面中的React小部件,以及实现ChatGPT对话与你应用程序用户界面之间的实时同步。
要了解详细的分步教程,您可以阅读它 这里。
建筑
MCP服务器
服务器实现了模型上下文协议,以便向ChatGPT提供任务管理功能:
- MCP 工具为ChatGPT注册了三种工具供其使用:
- createTask创建一个新的待办事项任务 - getTasks检索所有任务 - completeTask将任务标记为已完成
- 小部件资源服务器将React客户端作为可嵌入的小部件提供服务(
ui://widget/chatgpt-app-todo.html)
- 工具元数据每个工具都包含OpenAI特定的元数据:
- openai/outputTemplate指向用于渲染的UI小部件 - openai/widgetAccessible使小部件可通过工具输出访问 - openai/toolInvocation/*自定义加载和成功消息
使用useOpenAiGlobal钩子的客户端
React 客户端使用 useOpenAiGlobal 挂钩以集成ChatGPT的全局状态:
\useOpenAiGlobal.js\ 翻译成中文是:\使用OpenAI全局配置.js\ 或 \OpenAI全局使用.js\(具体翻译可能根据上下文有所调整,以更贴合实际用途)。不过,通常我们会更倾向于保持文件名的简洁性,直接翻译为“使用OpenAI.js”或“OpenAI全局文件.js”也是可以的,但这里为了更贴近原文件名的结构,给出了上述翻译
这个自定义钩子利用了React的 useSyncExternalStore 收件人:
- 订阅ChatGPT的全局状态变化通过
openai:set_globals事件 - 从……访问值
window.openai物体;对象 - 当ChatGPT更新工具输出时自动重新渲染
主要特点:
const toolOutput = useOpenAiGlobal("toolOutput");- 当ChatGPT调用MCP工具时
toolOutput值更新 - 客户端自动获取并刷新任务列表
- 提供ChatGPT操作与用户界面之间的实时同步
它是如何运作的
- 小部件渲染当ChatGPT调用MCP工具时,客户端应用程序会以小部件的形式呈现
- 状态同步该
useOpenAiGlobal钩子检测工具输出并刷新用户界面 - 交互式用户界面用户可以直接与小部件进行交互(添加、完成任务)
- 自然语言用户也可以通过ChatGPT对话来管理任务
设置
先决条件
- Node.js 18及以上版本
- npm 或 yarn
安装
- 安装依赖项:
# Install root dependencies
npm install
# Install server dependencies
cd server
npm install
# Install client dependencies
cd ../client
npm install配置环境变量
根据示例创建一个客户端环境文件,并将其指向中央 Better Auth 部署(或在开发时指向本地隧道):
cp client/.env.example client/.env.local
# Update the values as needed核心价值观:
VITE_AUTH_BASE_URL– 更好的认证基础URL(例如。,https://auth.onemainarmy.com/api/auth或者您的本地隧道VITE_MCP_METADATA_URL– 由认证服务器托管的MCP元数据VITE_TODO_API_BASE_URL——此待办事项API公开的URL(生产主机或http://localhost:3000(在开发中)- 可选的
VITE_CLIENT_BASE– 在子目录下部署时,覆盖 Vite 的构建基础路径 - 可选
VITE_ENABLE_AUTH_GATE– 设置为false在紧急回滚时绕过Better Auth网关
对于服务器,请复制示例并调整受信任的来源以匹配您的部署:
cp server/.env.example server/.env
# Update AUTH_BASE_URL, AUTH_MCP_METADATA_URL, TODO_API_BASE_URL, TRUSTED_ORIGINS as needed服务器端标志:
ENABLE_AUTH_GATE– 设置为false在调查问题时禁用身份验证强制中间件(恢复为传统的无身份验证行为)
构建客户端
在启动服务器之前先构建React客户端:
cd client
npm run build这产生了 client/dist 服务器将提供服务的目录。
运行服务器
启动MCP服务器:
cd server
npm run dev服务器运行于 http://localhost:3000。
将应用程序与ChatGPT连接
要使用这个应用程序与ChatGPT配合,你需要将本地服务器暴露出来并连接到ChatGPT。
1. 使用ngrok暴露本地服务器
由于ChatGPT需要访问您的本地服务器,请使用ngrok创建一个公共URL:
# Install ngrok (if not already installed)
# Visit https://ngrok.com/ to download
# Start ngrok tunnel to your local server
ngrok http 3000这将生成一个类似如下的公共URL: https://xxxx-xx-xx-xx-xx.ngrok-free.app
重要确保您的服务器正在运行于 http://localhost:3000 在启动 ngrok 之前。
2. 在ChatGPT中配置MCP
- 打开ChatGPT(使用桌面版或网页版应用)
- 首选 设置 → 应用与连接器
- 点击 创建
- 输入您的信息,并粘贴您的ngrok URL
/mcp终点至 MCP服务器URL:
https://your-ngrok-url.ngrok-free.app/mcp- 保存
3. 开始使用自然语言
一旦连接成功,您就可以在ChatGPT中使用自然语言指令:
- 输入“@ChatGPT App Todo”以添加您的应用。
- “创建一个新任务:购买杂货”
- “显示我的所有任务”
- “完成任务1”
- “将杂货购物任务标记为已完成”
4. 小部件交互
当ChatGPT调用任何与任务相关的工具时:
- 待办事项应用小部件出现在ChatGPT界面中
- 该小部件显示所有任务,并带有进度条
- 你可以在小部件中直接添加任务
- 你可以通过点击复选框来标记已完成的任务
故障排除
- 连接失败确认您的服务器正在运行且ngrok隧道处于活动状态
- 小部件未显示确保你已经跑了
npm run build在客户端目录中 - 任务未同步检查浏览器控制台中的CORS或网络错误
发展
项目结构
chatgpt-app-todo/
├── server/ # MCP server with Express
│ ├── index.js # MCP server, tools, and REST API
│ └── package.json
├── client/ # React client app
│ ├── src/
│ │ ├── App.jsx # Main UI component
│ │ ├── useOpenAiGlobal.js # ChatGPT integration hook
│ │ └── main.jsx
│ └── package.json
└── README.md技术
服务器:
- Express.js - 网络服务器
- @modelcontextprotocol/sdk - MCP协议实现
- Zod - 架构验证
客户:
- React 19 - 用户界面库
- Vite - 构建工具
- Tailwind CSS - 样式设计
- 更优的React客户端认证方案 - 认证
- \
useSyncExternalStore\- React 用于外部状态管理的 Hook
特点/功能
- ✅ 通过ChatGPT或用户界面创建任务
- ✅ 查看所有任务并跟踪进度
- ✅ 一键或语音指令完成任务
- ✅ ChatGPT与小部件之间的实时同步
- ✅ 使用Tailwind CSS打造的美观现代用户界面
- ✅ 响应式设计
如何使用OpenAiGlobal(注:此处“OpenAiGlobal”可能是一个假设的或特定上下文中的名称,实际翻译时可能需要根据具体情境调整)
该 useOpenAiGlobal Hook 在 ChatGPT 和您的 React 应用之间搭建了一座桥梁:
- 外部存储模式使用 React 的
useSyncExternalStore在React之外管理状态 - 事件监听监听
openai:set_globals自定义事件 - 全球访问从……读取值
window.openai[key] - 自动更新当ChatGPT更新工具输出时,触发重新渲染
这种模式使得小部件能够在ChatGPT执行MCP工具时立即作出响应,从而创造出无缝的交互体验。
许可证
ISC(International Security Council,国际安全理事会)
