◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/AIモデル
◉ AIモデル/2026-04-27上級

Rork × OpenAI Apps SDK:ChatGPT 内で動くアプリを Rork のコードベースから配信する実装ガイド

Rork で作ったモバイルアプリのビジネスロジックを ChatGPT 内のミニアプリとして配信する完全実装ガイド。MCP サーバー設計、UI Resource、OAuth 認証共有、Universal Link での会話継続、課金一貫性まで本番運用ノウハウを解説します。

Rork585OpenAI5Apps SDKChatGPTMCP4配信戦略

✦ プレミアム記事

「ChatGPT の中で動くアプリ」という配信チャネルが、2025 年秋に開かれました。これまでは App Store と Google Play がモバイルアプリの主戦場で、その外側にいるユーザーには SEO や広告でしか届かなかったわけですが、Apps SDK の登場によって ChatGPT を日常的に使う数億人の会話画面に直接アプリを差し込む選択肢ができました。問題は、Rork で開発しているアプリのコードベースをそのまま再利用できるのか、認証や課金が二重管理にならないか、本番でつまずきやすい場所はどこか、という実装側の生々しい疑問が公式ドキュメントだけでは解像度が荒いまま残ってしまうことです。

私はこのガイドで、Rork で動いている既存のアプリに Apps SDK 配信レイヤーを追加するときに発生する設計判断を、サンプルコードと一緒にひととおり通してみます。「ChatGPT 内とモバイルで同じユーザーを識別する」「会話の途中でモバイルに飛ばして続きを完結させる」「課金経路を分けても収益データを一本化する」といった、後回しにすると痛い目を見る部分を中心に扱います。

ChatGPT 内アプリと Rork アプリの「共通基盤」をどう設計するか

最初に決めるべきは、Apps SDK 用のサーバーを Rork のバックエンドと同じ場所に置くか、別プロジェクトにするか、です。私は Rork のバックエンドと同じ Cloudflare Workers / tRPC ベースのリポジトリの下に /mcp ルートを切る方式を好みます。理由は、ユーザー DB・課金状態・利用ログを一箇所に集中させるとデータ分析と運用が楽になるからです。

具体的には、次のレイヤー構造で考えます。共通基盤として、ドメインロジック(タスク作成・検索・更新といったビジネスルール)は core パッケージに置き、それを呼び出す入口として「Rork アプリ向けの REST/tRPC ハンドラー」と「Apps SDK 向けの MCP ハンドラー」を並列に配置します。これによりロジック修正は一箇所で済み、配信チャネルが増えても保守が破綻しません。

packages/
  core/                 # ドメインロジック(純粋関数)
  mobile-api/           # Rork アプリが叩く REST/tRPC エンドポイント
  apps-sdk/             # MCP サーバー (ChatGPT から叩かれる)
  shared/               # 認証・課金・ユーザー識別子の共通型定義

この構造の最大の利点は、ChatGPT 内アプリで生じる仕様変更(ツールの追加、UI Resource の変更)が Rork アプリのリリースサイクルから独立する点です。Apps SDK は MCP サーバーを更新するだけで反映されますが、Rork アプリは App Store の審査を経由します。サイクルが大きく違うので、最初から疎結合にしておくことが本番では効きます。

Apps SDK の MCP サーバーを Cloudflare Workers で 30 分で立ち上げる

Apps SDK は Model Context Protocol(MCP)の上に構築されています。ChatGPT は MCP クライアントとしてあなたのサーバーにツール呼び出しをかけてきます。Cloudflare Workers でホストする場合、最小構成は驚くほどシンプルです。

// apps-sdk/src/server.ts
import { Hono } from "hono";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
import { z } from "zod";
import { createTask, listTasks } from "@core/tasks";
 
type Env = { DB: D1Database; AUTH_JWKS_URL: string };
 
const app = new Hono<{ Bindings: Env }>();
 
app.post("/mcp", async (c) => {
  // 毎リクエストで OAuth のアクセストークンを検証(audience は自分の MCP サーバー)
  const authed = await verifyBearerToken(c.req.header("authorization"), c.env.AUTH_JWKS_URL);
  if (!authed) {
    return c.json({ error: "unauthorized" }, 401, {
      "WWW-Authenticate":
        'Bearer resource_metadata="https://your-mcp.example.com/.well-known/oauth-protected-resource"',
    });
  }
 
  const server = new McpServer({ name: "task-app", version: "1.0.0" });
 
  // ツール定義: ChatGPT が呼び出せる関数
  server.tool(
    "create_task",
    "会話の中でユーザーがこれからやることに触れた時に呼びます。既存タスクの内容変更には使わず、その場合は update_task を呼んでください。",
    {
      title: z.string().describe("ユーザーの発話をそのまま写した短い文。要約や言い換えはしないでください"),
      due_date: z.string().optional().describe("YYYY-MM-DD 形式の期日。ユーザーが期日に触れていない場合は省略します"),
    },
    async ({ title, due_date }, ctx) => {
      const userId = await resolveUserId(ctx, c.env.DB);
      const task = await createTask(c.env.DB, { userId, title, dueDate: due_date });
      return {
        // モデルが会話に添える文章(UI の紐付けは後述の registerTool の _meta で行います)
        content: [{ type: "text", text: `タスク「${task.title}」を作成しました` }],
        structuredContent: { taskId: task.id, title: task.title },
      };
    }
  );
 
  // 既存タスクの一覧
  server.tool(
    "list_tasks",
    "現在のタスク一覧を取得します。タスクを更新・完了する前に必ず先に呼んでください。",
    {},
    async (_, ctx) => {
      const userId = await resolveUserId(ctx, c.env.DB);
      const tasks = await listTasks(c.env.DB, userId);
      return {
        content: [{ type: "text", text: `${tasks.length} 件のタスクがあります` }],
        structuredContent: { tasks },
      };
    }
  );
 
  // Workers では Web 標準の Request / Response を扱うトランスポートを使います
  const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  await server.connect(transport);
  return transport.handleRequest(c.req.raw);
});
 
export default app;

ここで意識すべき点は三つです。第一に、認可の検証を最初から入れること。公式ドキュメントを読み直したところ、リクエスト署名用のヘッダー(以前この記事に書いていた x-openai-apps-signature による HMAC 検証)は記載がなく、確認できませんでした。代わりに示されているのは、OAuth 2.1 のアクセストークンを毎リクエストで検証すること、そして ChatGPT 側からの通信かどうかは mTLS(OpenAI 管理のクライアント証明書)か、公開されている送信元 IP の許可リストで確かめる方法です。どちらも後回しにすると、本番デプロイ後に「誰でも MCP サーバーを叩ける」状態が露出します。第二に、ツールの戻り値は structuredContent と content を両方返すこと。前者はモデルが後続の推論で再利用する簡潔なデータ、後者は会話に添えるテキストです。両方を返さないと、会話文脈にデータが残らず、続きの質問でモデルが無駄に再取得しに行きます。

私は最初、structuredContent を省いていたのですが、ユーザーが「さっき作ったタスクの期日を変えて」と言った瞬間にモデルがタスク ID を見失うバグに遭遇しました。設計時から両輪で返すと決めておくと、こういう罠を踏みません。

第三に、ツール名の隣に説明文を渡すこと。上のコードで server.tool() の第2引数に日本語の説明を置いているのがそれです。ここを省くと、モデルが手にしている手がかりは create_task という識別子だけになります。この一点が呼び出し精度をいちばん左右しますので、節を改めて扱います。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦「ChatGPT 内アプリも作りたいが、何から手をつけていいかわからなかった」読者が、Rork のコードベースを最大限再利用しながら今日から実装を始められる
✦MCP サーバー・UI Resource・OAuth 共有・Universal Link 連携の動くサンプルコードを手に入れ、ChatGPT 内とモバイルアプリで「同じユーザー・同じ状態」を維持する設計パターンを習得できる
✦ChatGPT の数億ユーザーへ自分のアプリを露出させる新しい配信チャネルを開拓し、ASO だけに頼らない流入経路を構築できる
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

◉ AI モデル2026-08-07
MCP のツールを減らしても軽くならない — 定義 2,377 バイトと応答 110,298 バイトを測り直した記録
壁紙カタログ用のMCPサーバを依存なしの最小構成で書き直し、ツール定義2,377バイトと応答110,298バイトを実測した記録です。削るべきは応答側であること、ツール統合の本当の理由が見分けやすさにあることを、測定ハーネスと3つの設計ルールとともに示します。
◉ AI モデル2026-10-14
プロンプトをアプリから外して Edge Function に置く。守りは入力側でなく、出力側に作りました
アプリに入れた AI の指示文は、配信した日から読める側に回ります。プロンプトを Supabase Edge Function に寄せ、クライアントにはタスク名だけを言わせ、守りを出力の許可リストに置く構成を、テスト付きのコードで整理しました。
◉ AI モデル2026-10-05
Sonnet 5.5・GPT-6.1 Sol・Opus 5.5 のどれに頼むかを、修正の種類で先に決めておきます。クレジットの行き先を1週間記録して引いた割り当て
Rork のモデルメニューが Sonnet 5.5・GPT-6.1 Sol・Opus 5.5 の三つになったあと、修正を三種類に分けてモデルと effort を割り当てました。変更履歴に書かれた違いの読み方、クレジットの行き先を記録する小さな台帳、そこから引いた線引きを書き残します。