
公式の`create-mcp-app` スキルを使ってMCP AppをClaude Codeで作ってみた
リテールアプリ共創部のるおんです。
以前、MCP Appsの概要と仕組み、公式サンプル map-server の内部構造を解説する記事を書きました。
前回は「MCP Appsとは何か」を理解するための記事でしたが、今回は実際に create-mcp-app スキルを使ってMCP AppをClaude Codeを使って作ってみた 記事です。
Claude Codeにスキルをインストールし、「ポモドーロタイマーを作って」と指示するだけで、MCP Appのプロジェクト一式が生成され、Claudeの会話内でインタラクティブなタイマーUIが動作するところまでを試してみました。
実際に作られたポモドーロタイマーMCP Apps

MCP Appsのおさらい
前回の記事の要点をおさらいします。
MCP Apps は、通常のMCPツール定義に _meta.ui フィールドを追加するだけで、テキストを返す関数をインタラクティブなWebアプリに変える 仕組みです。
| 通常の MCP ツール | MCP Apps ツール | |
|---|---|---|
| 結果の表示 | テキストのみ | テキスト + ifrzame 内の UI |
| ユーザー操作 | 不可(結果を読むだけ) | ボタン、フォーム等で操作可能 |
| 通信 | ツール呼び出し → 結果で終了 | 表示後も 双方向通信が継続 |
詳しくは前回の記事をご参照ください。
create-mcp-app スキルとは
create-mcp-app は、MCP Apps の公式リポジトリ ext-apps で提供されている AIコーディングエージェント向けのスキル です。
スキルとは、AIエージェントに専門的な知識やワークフローを教えるための命令セットです。create-mcp-app スキルをインストールすると、AIエージェントがMCP Appのプロジェクト構成、ベストプラクティス、APIの使い方を理解した上で、コードを生成してくれます。
つまり、「ポモドーロタイマーのMCP Appを作って」と自然言語で指示するだけで、サーバー・UI・設定ファイルの一式を自動生成 してくれるということです。
スキルのインストール
Claude Codeを使用している場合、以下のコマンドでスキルを直接インストールできます。
/plugin marketplace add modelcontextprotocol/ext-apps
/plugin install mcp-apps@modelcontextprotocol-ext-apps
インストールが完了したら、/reload-plugins でプラグインをリロードします。
/reload-plugins
これだけで準備完了です。
MCP Appを作ってみる
スキルがインストールされた状態で、Claude Codeに以下のように指示しました。
これを使っていい感じのMCP Appsを作ってみてください
すると、Claude Codeが create-mcp-app スキルを認識し、参考コードのクローン、プロジェクト構造の生成、依存関係のインストール、ビルドまでを一気に実行してくれました。

今回は ポモドーロタイマー のMCP Appが生成されました。
生成されたプロジェクト構成
mcp-pomodoro-app/
├── server.ts ← MCPサーバー(バックエンド)
├── main.ts ← エントリーポイント(stdio / HTTP 切り替え)
├── mcp-app.html ← UIのHTMLシェル
├── src/
│ ├── mcp-app.tsx ← React App(フロントエンド・iframe内)
│ ├── global.css ← グローバルスタイル(ホストテーマ対応)
│ ├── mcp-app.module.css ← コンポーネントスタイル
│ └── vite-env.d.ts
├── package.json
├── tsconfig.json
├── vite.config.ts
└── .gitignore
前回の記事で解説した通り、MCP Appsの開発は 通常のWebアプリ開発と同じ構造 です。バックエンド(server.ts)とフロントエンド(src/mcp-app.tsx)に分かれています。
サーバー側:server.ts
サーバー側では、start-pomodoro というMCP Appsツールを登録しています。
registerAppTool(server,
"start-pomodoro",
{
title: "Pomodoro Timer",
description: "Start an interactive Pomodoro timer.",
inputSchema: {
type: "object",
properties: {
workMinutes: z.number().min(1).max(120).default(25),
breakMinutes: z.number().min(1).max(60).default(5),
task: z.string().default("Focus Time"),
},
},
+ _meta: { ui: { resourceUri } },
},
async ({ workMinutes, breakMinutes, task }) => {
const config = {
workMinutes: workMinutes ?? 25,
breakMinutes: breakMinutes ?? 5,
task: task ?? "Focus Time",
startedAt: new Date().toISOString(),
};
return {
content: [{ type: "text", text: JSON.stringify(config) }],
};
},
);
ポイントは _meta: { ui: { resourceUri } } です。このフィールドがあることで、ホストはツール結果をテキストとしてだけでなく、インタラクティブなUIとしてもレンダリング します。
ツールのパラメータとして workMinutes(作業時間)、breakMinutes(休憩時間)、task(タスク名)を受け取ります。これにより、Claudeに「25分の作業と5分の休憩でポモドーロを開始して」と指示すると、AIがパラメータを解釈してツールを呼び出してくれます。
App側:src/mcp-app.tsx
App側(iframe内で動くフロントエンド)では、React + useApp フックを使ってホストとの通信を行います。
import { useApp } from "@modelcontextprotocol/ext-apps/react";
function PomodoroApp() {
const [config, setConfig] = useState<PomodoroConfig>({
workMinutes: 25, breakMinutes: 5, task: "Focus Time",
});
const { app, error } = useApp({
appInfo: { name: "Pomodoro Timer", version: "1.0.0" },
capabilities: {},
onAppCreated: (app) => {
+ app.ontoolresult = async (result) => {
+ setConfig(parseConfig(result));
+ };
app.onhostcontextchanged = (params) => {
setHostContext((prev) => ({ ...prev, ...params }));
};
},
});
// ...
}
useApp()フックでAppインスタンスを作成し、ホストとの接続を確立app.ontoolresultでホストから転送されたツール結果(作業時間、休憩時間などの設定)を受け取るapp.onhostcontextchangedでホストのテーマ変更(ダーク/ライトモード)に対応
タイマーのセッションが完了すると、app.sendMessage() でホスト(Claude)に完了を通知します。
app.sendMessage({
role: "user",
content: [{
type: "text",
text: `Pomodoro session completed: "${config.task}" (${config.workMinutes} min)`,
}],
}).catch(console.error);
これにより、ポモドーロ完了後にClaudeが「お疲れさまでした!次のタスクは何にしますか?」のように応答できるようになります。App内でのユーザー操作がAIの理解を更新する というのが、MCP Appsの面白いところですね。
UIの特徴
生成されたポモドーロタイマーは以下の機能を備えています。
- 円形プログレスバー:SVGで描画された残り時間の視覚的な表示
- 作業/休憩モード:作業(赤)と休憩(緑)を自動で切り替え
- 操作ボタン:開始/一時停止、リセット、スキップ
- セッションカウンター:完了したポモドーロをドットで表示(4セッション)
- ダーク/ライトモード対応:ホストのテーマに自動適応
Claudeでテストしてみた
ローカルサーバーの起動
まず、ビルドしてサーバーを起動します。
npm install && npm run build && npm run serve
デフォルトで http://localhost:3001/mcp でMCPサーバーが起動します。
cloudflaredでトンネルを作成
Claude(Web版/Desktop版)からローカルサーバーにアクセスするには、インターネットに公開する必要があります。cloudflared を使ってトンネルを作成します。
npx cloudflared tunnel --url http://localhost:3001
実行すると、https://random-name.trycloudflare.com のようなURLが生成されます。
Claudeにカスタムコネクターを追加
- claude.ai または Claude Desktop を開く
- プロフィールアイコンをクリック → Settings → Connectors
- Add custom connector をクリック
- URLに
https://生成されたURL/mcpを入力 - 保存

動かしてみる
Claudeに「ポモドーロタイマーを開始して」と入力すると、start-pomodoro ツールが呼び出され、会話内にインタラクティブなタイマーUIが表示されました。


タイマーの開始/一時停止、モードの切り替え、セッションカウントなど、すべての操作がClaude内で完結します。テキストで「25分経過しました」と返されるのではなく、操作可能なタイマーそのもの が会話に埋め込まれるのが新鮮ですね。
create-mcp-app スキルを使ってみた感想
良かった点
- プロジェクト構成を考えなくていい:Viteの設定、
vite-plugin-singlefileによるバンドル、TypeScript設定など、MCP Apps特有の構成を自動で生成してくれる - ベストプラクティスが反映される:ハンドラーの登録順序(
connect()の前に登録)、テキストフォールバックの提供、ホストテーマへの対応など、スキルに組み込まれた知識が自動的に反映される - テスト環境もセットアップしてくれる:
cloudflaredによるトンネル作成やbasic-hostの起動など、テスト環境の構築まで案内してくれる
注意点
- スキルはあくまでAIエージェントへの「知識の注入」であり、生成されるコードの品質はAIの能力に依存する
- 複雑なMCP App(ネットワークリクエスト、CSP設定、バイナリリソースなど)を作る場合は、公式のパターンドキュメントも併せて確認するのがおすすめ
おわりに
今回は、create-mcp-app スキルを使ってMCP Appを実際に作ってみました。
前回の記事でMCP Appsの仕組みを理解した上で、スキルを使って実際にアプリを構築してみると、MCP Appsの開発体験は通常のWebアプリ開発と変わらない ことが実感できました。サーバー側でツールとリソースを登録し、クライアント側でUIを構築する。この構造はバックエンド/フロントエンドの分離そのものです。
create-mcp-app スキルのおかげで、MCP Apps特有のセットアップ(Vite設定、シングルファイルバンドル、ホストとの通信設定など)に悩むことなく、作りたいものの実装に集中 できました。
MCP Appsは現在も活発に開発が進んでいる仕組みです。「AIチャットの中にインタラクティブなUIを埋め込む」というコンセプトに興味がある方は、ぜひスキルをインストールして試してみてください。
以上、どなたかの参考になれば幸いです。
参考








