Todo MCP Server
ChatGPT Todo MCPデモ (Apps SDK + React)
ChatGPT用の最小限のTodoアプリです。MCPサーバーがツールとインタラクティブなHTML UIを公開します。React + Viteで構築され、単一ファイルのバンドルとして埋め込まれています。ChatGPTのコネクタウィザードが検出を完了できるように、小規模な開発用OAuthレイヤーが含まれています。
公式リファレンス: Apps SDKクイックスタート。
クイックスタート
npm install
npm start # builds widget (prestart) then runs server on port 8787 by defaultMCPエンドポイント:
http://localhost:8787/mcpChatGPT用: HTTPSで公開し(例: ngrok)、
https://<your-host>/mcpを指すコネクタを作成します。トンネル経由で検出URLのスキームやホストが正しく表示されない場合は、以下を設定してください:
export PUBLIC_BASE_URL=https://your-ngrok-host.example
Related MCP server: mcp-todo-demo
プロジェクト構成
パス | 役割 |
| HTTPルーター: OAuth検出 + CORS + |
| 開発専用のOAuth 2.1検出 + DCR/PKCE(本番環境では実際のIdPに置き換えてください) |
| チャット内UI用のVite + Reactソース |
| ビルド済みの単一ファイルHTML(gitignored); 起動時に |
アーキテクチャとコンセプト
一文でのモデル
ChatGPTはMCPクライアントとして機能します。/mcpにあるNodeサーバーとHTTPS経由のMCPで通信します。サーバーはツール(モデルが呼び出せるもの)とリソース(ウィジェット用のHTML)を登録します。ウィジェットはiframe内で実行され、postMessageを介したJSON-RPCブリッジを通じてChatGPTと通信します。同じオリジン上のOAuthメタデータにより、ChatGPTはコネクタを接続できます。これはMCPツールの実行とは別ですが、オンボーディングには必須です。
Model Context Protocol (MCP)
MCPは、ホスト(ChatGPT)がサーバー上のツールを検出・呼び出し、リソースを読み取るための標準的な方法です。このリポジトリでは@modelcontextprotocol/sdkを使用しています。McpServerインスタンスが機能を登録し、MCPメッセージをHTTPにマッピングするトランスポート(StreamableHTTPServerTransport)に接続されます。
Base MCPとApps SDKヘルパー
@modelcontextprotocol/sdk: コアとなるMcpServer、スキーマ、トランスポート。@modelcontextprotocol/ext-apps:registerAppToolおよびregisterAppResourceは、UIメタデータ(ツールを表示するためにどのHTMLリソースを使用するか)を正規化し、Apps HTMLのMIMEタイプ(RESOURCE_MIME_TYPE)を設定します。
ウィジェットは論理URI(例: ui://widget/todo.html)のリソースとして登録されます。そのURIは公開Web URLである必要はありません。ホストはMCPのresources/readを介して解決します。各ツールの_meta.ui.resourceUriは同じURIを指しているため、ChatGPTはどのUIサーフェスがどのツールに属しているかを認識できます。
HTTPフロントドア (server.js)
1つのNode http.Serverが複数のサーフェスを処理します:
OAuth / 検出 (
oauth-dev.js) — ChatGPTが期待する既知のURLとトークンエンドポイント。CORS
/mcp用のOPTIONS。ヘルスチェック
GET /。MCP ストリーミング可能なHTTPトランスポートを介した
/mcpへのPOST/GET/DELETE。404 不明なパス用。
つまり、1つのプロセスで複数の論理HTTP API(OAuth HTTP + MCP HTTP)を扱います。
ストリーミング可能なHTTPとサーバーのライフタイム
トランスポートは受信したMCPリクエストごとに作成され、sessionIdGenerator: undefined(このデモではステートレスモード)が使用されます。リクエストごとに新しいMcpServerが構築され、レスポンスが閉じられると破棄されます。
重要: インメモリのTodo状態(server.js内のtodos)は、McpServerインスタンス内ではなく、モジュールスコープに存在します。そのため、各リクエストが新しいMCPサーバーオブジェクトを取得しても、Nodeプロセスのライフタイム中は状態が保持されます。
ツールとUIの契約
ツール(add_todo、complete_todo)は入力スキーマ(Zod)を宣言し、ホストが引数を検証できるようにします。
ツールの結果には以下が含まれます:
content: モデル/会話用の通常のMCPコンテンツ(テキストなど)。structuredContent: ウィジェットが消費するJSON(ここでは{ tasks: [...] })。
すべての変更に対して同じstructuredContentの形状を使用することで、呼び出しがウィジェット内のユーザーによってトリガーされたか、チャット内のモデルによってトリガーされたかに関わらず、React UIを同期させることができます。
OAuth (oauth-dev.js)
ChatGPTのコネクタフローは、OAuth保護リソースメタデータと認可サーバーメタデータを取得します(Apps SDK認証を参照)。これらのルートがないと、セットアップ時に「OAuth設定の取得エラー」が発生する可能性があります。
このリポジトリには、ChatGPTのリダイレクトURLにスコープされた開発専用の認可サーバー(検出、動的クライアント登録、認可リダイレクト、PKCEトークン交換)が含まれています。本番環境でそのまま使用しないでください。Auth0、Stytch、Cognitoなどに置き換え、MCPリクエストでトークンを検証してください。
PUBLIC_BASE_URLは、プロキシやngrokがHost / X-Forwarded-Protoを期待通りに設定しない場合に、メタデータ内の公開https://オリジンを強制します。
ウィジェットブリッジ (widget/src/bridge.ts)
ビルドされたHTMLはChatGPTのiframe内で実行されます。通常のSPAのように/mcp URLを呼び出すのではなく、MCP Apps UIブリッジを使用します:
ui/initializeその後にui/notifications/initialized— ホストとのハンドシェイク。tools/call— 名前付きMCPツールを引数付きで実行するようホストに依頼(モデルが使用するものと同じツール)。ui/notifications/tool-result— モデルがツールを実行した際、ホストが結果をプッシュすることで、tools/callからの直接の戻りパスなしでUIを更新できます。
つまり、2つの更新パスがあります。UIが開始した呼び出しに対するRPCレスポンスと、モデルが開始した呼び出しに対する通知です。
なぜ単一ファイルHTMLなのか (Vite + vite-plugin-singlefile)
ChatGPTはウィジェットを「あなたのサイト + 個別のJSチャンク」としてではなく、MCPリソース読み取りからの埋め込みHTMLとして受け取ります。相対的なチャンクURLは、その埋め込みモデルでは機能しません。ビルドによって、JS/CSSがインライン化された1つのdist/todo-widget.htmlが生成されます。server.jsは起動時にそれをtodoHtmlに読み込みます。
Reactは開発者の人間工学のためのレイヤーであり、デプロイ可能な成果物は静的なHTMLです。
エンドツーエンドのフロー
ChatGPTのユーザー: メッセージ → モデルがツールを選択 → ChatGPTがあなたの/mcpにPOST → ツールが実行 → structuredContent.tasksを返す → ホストがウィジェットを表示/更新。
ウィジェットのユーザー: React → postMessage経由でtools/call → ホストがMCPに転送 → 同じハンドラー → RPC結果が状態を更新。
コネクタ設定: ChatGPTがオリジンの/.well-known/...にアクセス → 必要に応じてOAuthリンク → その後の/mcpへのMCP呼び出しにはAuthorization: Bearer ...が含まれる場合があります(すべてのツールでこれを強制するのが本番ステップです)。
今後のステップ
エリア | 方向性 |
状態 | データベースにTodoを永続化し、アクセストークンから取得した認証済みユーザーIDでスコープ化する。 |
認証 |
|
MCPセッション | 異なるストリーミングやライフサイクルセマンティクスが必要な場合は、ステートフルなセッションを検討する。 |
ツール | より詳細な説明/スキーマ、オプションの |
ウィジェット | 同じブリッジを使用し、UX、エラー処理、読み込み状態を改善する。 |
スクリプト
スクリプト | 説明 |
|
|
|
|
| Viteビルドのみ |
デフォルトポート: 8787 (PORT環境変数で上書き可能)。
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Build your own tools from chat: design, test and host them on a shareable MCP server URL.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA minimal MCP server demonstrating how to build ChatGPT-compatible applications using Next.js with widget rendering capabilities. Provides a starter template for integrating Next.js applications with the ChatGPT Apps SDK through the Model Context Protocol.-
- FlicenseNot gradedqualityDmaintenanceA minimal MCP server that provides an interactive to-do list with checkboxes in chat, demonstrating MCP Apps UI resource integration and tool-based state updates.-
- FlicenseNot gradedqualityDmaintenanceA minimal Next.js application demonstrating how to build an OpenAI Apps SDK compatible MCP server with widget rendering in ChatGPT.-
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol server with a built-in OAuth 2.1 authorization server and a Next.js todo app, enabling authenticated task management (create, read, update, delete tasks) via natural language through an MCP client.7 npmISC