Skip to main content
Glama
TejasQ

CityJS London 2026 Companion

by TejasQ

基本的には、MCP Apps

MCPサーバーがテキストやJSONなどを返すことはご存知ですよね?MCP Appsはそれをさらに一歩進めたものです。ツールがフルHTMLウィジェットを返し、それがChatGPT内で直接レンダリングされます。モデルがユーザーにJSONの壁を投げつける代わりに、実際のUIが表示されます。カード、グリッド、タイムラインなど、何でも好きなものを表示できます。

これはあくまで概念実証(POC)であり、真剣なプロジェクトではありません。CityJS London 2026カンファレンスのコンパニオンアプリとして、スケジュール、登壇者、トーク検索などをChatGPT内でリッチなテーマ付きUIとしてレンダリングします。

実行方法

./start.sh

これだけです。コマンド一つで、依存関係のインストール、サーバーの起動、cloudflaredトンネルのオープンを行い、ChatGPTに貼り付けるためのURLを発行します。Node.js 18+とcloudflared(Macの場合はbrew install cloudflared)が必要です。

以下のような画面が表示されます:

  ┌─────────────────────────────────────────────────────────┐
  │                                                         │
  │   YOUR MCP ENDPOINT:                                    │
  │                                                         │
  │   https://something-random.trycloudflare.com/mcp        │
  │                                                         │
  │   NOW GO ADD IT TO CHATGPT:                             │
  │                                                         │
  │   1. Open chatgpt.com                                   │
  │   2. Click the tools icon (wrench) in the input bar     │
  │   3. Click 'Add MCP Server'                             │
  │   4. Paste the URL above                                │
  │   5. Ask: 'What's the CityJS London schedule?'          │
  │                                                         │
  └─────────────────────────────────────────────────────────┘

あとはChatGPTに「登壇者を見せて」や「Douglas Crockfordのトークについて教えて」、「AIに関するトークを探して」などと話しかけて、ウィジェットが表示されるのを確認してください。

Related MCP server: Hello Widget Example

MCP Appsについて学ぶには

ソースコードは怖くありません!中身を見てみましょう。関連するファイルは基本的には3つだけです(しかも小さいです!):

ファイル

役割

server.js

MCPサーバー。ウィジェットとツールの登録、それらの紐付けを行います。ここから始めてください。

widgets/schedule.html

カンファレンスのタイムラインをレンダリングするHTMLウィジェット。一番下の<script>タグを読んでください。

widgets/speakers.html

登壇者のカードグリッドをレンダリングするHTMLウィジェット。上記と同じパターンです。

widgets/speaker-detail.html

登壇者個人の詳細プロフィールカード用HTMLウィジェット。

ぜひ読んでみてください。本当に楽しいですよ!

基本的にはどう動くのか

MCP Appは、HTMLウィジェットも提供するMCPサーバーに過ぎません。ChatGPTがツールを呼び出すと、ウィジェットがレンダリングされ、ツールの出力データがそこに流し込まれます。これを実現するのは以下の3点です:

1. HTMLウィジェットを書く

インラインCSSとJSを含む自己完結型のHTMLファイルです。ChatGPTからデータを受け取り、それをレンダリングします。それだけです。widgets/speakers.htmlを見てください。render(data)関数といくつかのCSSがあるだけです。

ウィジェットは以下のようにChatGPTからデータを受け取ります:

// ChatGPT puts tool output here when the widget loads
tryRender(window.openai?.toolOutput);

// Or fires this event slightly later
window.addEventListener("openai:set_globals", (e) => {
  tryRender(e.detail?.globals?.toolOutput);
});

また、テーマ(window.openai?.theme)も取得するため、ChatGPTのライト/ダークモードに自動的に合わせることができます。

2. ウィジェットをリソースとして登録する

server.jsで、MCPホストに「このウィジェットがあるよ」と伝えます:

server.registerResource(
  "schedule-widget",
  "ui://cityjs/schedule.html",
  { mimeType: "text/html;profile=mcp-app" },  // <-- this MIME type is the magic
  async () => ({
    contents: [{
      uri: "ui://cityjs/schedule.html",
      mimeType: "text/html;profile=mcp-app",
      text: scheduleWidgetHtml,  // the raw HTML string
    }],
  })
);

MIMEタイプのtext/html;profile=mcp-appが、通常のMCPサーバーをMCP Appに変える鍵です。これはホストに対して「これは単なるファイルではなく、レンダリング可能なウィジェットだ」と伝えます。

3. ツールをウィジェットに紐付ける

ツールを登録する際、そのツールが呼び出されたときに「どのウィジェットをレンダリングするか」をホストに伝えます:

server.registerTool(
  "get_schedule",
  {
    title: "Get Schedule",
    description: "Get the CityJS London 2026 schedule...",
    inputSchema: { day: z.enum(["day1", "day2", "day3", "all"]).optional() },
    _meta: {
      ui: { resourceUri: SCHEDULE_URI },           // MCP spec way
      "openai/outputTemplate": SCHEDULE_URI,        // ChatGPT-specific way
    },
  },
  async ({ day }) => {
    return {
      structuredContent: { days },   // <-- your widget receives THIS
      content: [{ type: "text", text: JSON.stringify({ days }) }],  // fallback for non-UI hosts
    };
  }
);

structuredContentはウィジェットがレンダリングするデータです。contentは(Claudeのように)まだUIに対応していないホストのためのテキストフォールバックです。常に両方を返すようにしてください。

基本的にはこれだけです。ウィジェット + リソース + ツールの紐付け = MCP Appです。

プロジェクト

basically-mcp-apps/
  start.sh               <- run this. that's it.
  server.js              <- the MCP server. START READING HERE.
  package.json
  data/
    data.json            <- raw conference data (speakers, talks, bios)
    cityjs.js            <- enriches the raw data with rooms, types, etc.
  widgets/
    schedule.html        <- conference schedule timeline widget
    speakers.html        <- speaker grid widget
    speaker-detail.html  <- individual speaker profile card widget

依存関係

  • @modelcontextprotocol/sdk -- MCPサーバーSDK

  • zod -- 入力スキーマバリデーション

  • cloudflared -- ローカルホストをインターネットにトンネルし、ChatGPTがアクセスできるようにします

  • Node.js 18+

Reactもビルドステップも、バンドラーもフレームワークも不要です。HTMLファイルとNodeサーバーだけです。

ハッピーハッキング!

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A 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.
    -
  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    A minimal ChatGPT app demonstrating interactive greeting widgets with confetti animations and theme support, built as a template for creating MCP servers with custom UI components.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server template integrated with the OpenAI Apps SDK for building ChatGPT-compatible widgets with automatic tool registration. It provides a suite of interactive UI components and ecommerce examples for creating type-safe, theme-aware widgets.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables AI models to render GitHub's Primer React components directly within chat interfaces using a JSON component tree. It provides tools to list available components and display interactive UI elements with full GitHub theming support.
    -