はじめに
以前の記事で、OutSystems の REST API を MCP サーバー経由で公開し、Claude Desktop から自然言語でサービスを呼び出す方法を紹介しました。
その後、MCP の新しい拡張仕様として 2026年1月に MCP Apps が公開されました。
MCP Apps を使うと、ツール呼び出しの結果としてテキストだけでなく UI(View) をチャット内に表示できます。
前回はタスクの作成結果をテキストで受け取るだけでしたが、一覧表示・フォーム入力・ボタン操作といったインタラクションをチャット内に埋め込むことができます。
実際に試してみたので、実装の概要を紹介します。
実装内容については、以下の記事を参考にしました。
And then there were MCP Apps
本記事は上記の記事の実装を参考に、日本語で一部解説を加えたものです。
内容の一部変更も含めた記事の掲載については、ご本人に許可をいただいております。
個人的な学習・技術向上の一環として取り組んだ内容です。
MCP Apps とはなにか
MCP Apps は、MCP の拡張仕様として定義された仕組みで、ツール呼び出し結果に UI リソースを紐付けてチャット内に表示できる機能です。
MCP Apps で追加された機能
従来の MCP Server では、ツールの呼び出し結果はすべて テキスト で返されていました。
MCP Apps という拡張仕様により、ツールの呼び出し結果に UI(View) を紐付けてチャット内に表示できるようになりました。
本記事のゴール
OutSystems の REST API をバックエンドに利用して、タスク一覧表示・追加・更新を Claude Desktop のチャット内 UI で操作できる MCP Apps を作成します。
手順
手順 1:OutSystems 側の準備
今回使用する API エンドポイントは以下の 4 つです。
| メソッド | エンドポイント | 説明 |
|---|---|---|
| GET | /rest/mcp/tasks |
タスク一覧を取得 |
| GET | /rest/mcp/tasks/:id |
タスクを取得 |
| POST | /rest/mcp/tasks |
新しいタスクを作成 |
| PUT | /rest/mcp/tasks |
タスクを更新 |
※「Tasks」モデルの詳細は前回の記事を参照してください。
手順 2:プロジェクトのセットアップ
プロジェクトを新規作成し、ツール群をインストールします。
md mcp-apps-outsystems-task-server && cd mcp-apps-outsystems-task-server
npm init -y
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk \
express cors zod react react-dom
npm install -D typescript vite vite-plugin-singlefile \
@tailwindcss/vite @vitejs/plugin-react \
@types/express @types/cors @types/node @types/react @types/react-dom \
tsx concurrently cross-env
次に、OutSystems API のベース URL を設定するため、自身の環境にあわせて.env ファイルを作成します。
# .env
OUTSYSTEMS_BASE_URL=https://your-environment.outsystems.app/rest/mcp
その他、必要なファイルを作成していきます。
最終的なディレクトリ構成は以下です。
mcp-apps-outsystems-task-server/
├── main.ts # stdio / HTTP サーバーの起動
├── server.ts # MCP ツールとリソースの定義
├── mcp-app.html # UI のエントリ HTML
├── src/
│ ├── mcp-app.tsx # View(React)
│ ├── constants.ts # 型・定数
│ └── global.css # スタイル
├── tsconfig.json
├── tsconfig.server.json
└── vite.config.ts
以降、主要なファイルのみ抜粋して説明をします。
※PG の詳細は以下の GitHub リポジトリを参照ください。
mcp-apps-outsystems-task-server
手順 3:サーバー側の実装(server.ts / main.ts)
MCP Apps の肝は registerAppTool() と registerAppResource() です。従来の server.tool() との違いは _meta.ui.resourceUri で UI を紐付けられる点です。
以下の 6 つのツールを実装しています。
| ツール名 | 呼び出し元 | 説明 |
|---|---|---|
list-tasks |
Claude / UI | タスク一覧を取得して UI を表示 |
show-task-details |
Claude / UI | 指定タスクの詳細を表示 |
get-task-details |
UI のみ | 詳細データを取得(内部 API) |
refresh-tasks |
UI のみ | タスク一覧を再取得 |
create-task |
UI のみ | 新規タスクを作成 |
update-task |
UI のみ | タスクを更新 |
「Claude / UI」と「UI のみ」は visibility オプションで制御します。
// Claude からも呼べるツール(一覧・詳細表示)
registerAppTool(
server,
"list-tasks",
{
_meta: { ui: { resourceUri } },
},
handler,
);
// UI からのみ呼べるツール(データ変更)
registerAppTool(
server,
"create-task",
{
_meta: { ui: { resourceUri, visibility: ["app"] } },
},
handler,
);
visibility: ["app"] を付けたツールは LLM からは呼べず、View 側の callServerTool() からのみ実行されます。Claude が勝手にデータを変更しないようにするためです。
registerAppResource() では、Vite でバンドルした dist/mcp-app.html を返します。
registerAppResource(
server,
resourceUri,
resourceUri,
{ mimeType: RESOURCE_MIME_TYPE },
async () => {
const html = await fs.readFile(
path.join(DIST_DIR, "mcp-app.html"),
"utf-8",
);
return {
contents: [
{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
],
};
},
);
main.ts は起動モードの振り分けのみを担います。--stdio フラグが付いていれば Claude Desktop 向けの stdio モード、なければ Streamable HTTP モードで Express サーバーを起動します。
手順 4:UI 側の実装(src/mcp-app.tsx)
View は React + Tailwind CSS で構築し、@modelcontextprotocol/ext-apps の App クラスでホストと通信します。
画面は一覧・詳細・編集・新規作成の 4 状態で管理しています。
list(一覧)→ detail(詳細)→ edit(編集)
→ create(新規作成)
MCP Apps SDK との接点は以下の通りです。
// React フックを使用して MCP App に接続
const { app, error: appError } = useApp({
appInfo: { name: "Task Manager", version: "1.0.0" },
capabilities: {},
onAppCreated: (instance) => {
// ① 初回ツール結果の受け取り(Claude が list-tasks を呼んだ結果)
instance.ontoolresult = async (result) => {
handleToolResult(result);
};
// その他のライフサイクルハンドラ
},
});
// ② UI からツールを呼び出す(create / update / refresh ...)
const result = await app.callServerTool({
name: "create-task",
arguments: { ... }
});
手順 5:ビルドと Claude Desktop への登録
npm run build
dist/mcp-app.html が生成されたら、%APPDATA%\Claude\claude_desktop_config.json に以下を追加して Claude Desktop を再起動します。
{
"mcpServers": {
"task-app": {
"command": "npx",
"args": [
"tsx",
"--env-file=YOUR-PROJECT-PATH\\.env",
"YOUR-PROJECT-PATH\\main.ts",
"--stdio"
]
}
}
}
動作確認
Claude Desktop を起動し、「Connectors」から task-app のツール(List Tasks)が認識されていることを確認します。

「タスクをみせてください」などと自然言語で指示してみます。
Claude がツールの実行を許可すると、チャット内にタスク一覧の UI が表示されます。

「New Task」ボタンをクリックすると新規作成画面が表示され、タイトル・優先度・期日などを入力してタスクを作成できます。

「Create Task」後に、一覧に表示されました。

さらに、タスクを選ぶと詳細・編集画面に遷移し編集が可能です。

↓

テキストチャットだけでは実現しづらかった、直感的なインタラクションが可能になりました 🎉
まとめ・感想
MCP Apps を使うことで、従来のテキストチャットだけの MCP Server に インタラクティブな UI を追加できるようになりました。
前回の記事では「タスクを作成して」→「成功しました」というテキストのやり取りでしたが、MCP Apps であれば一覧画面から直接操作できるので、UX が向上しそうです。
構成自体も「Tool + UI Resource の 2 パート登録」を理解すればシンプルでした。
ただし、MCP Apps のホスト側から UI を呼び出す仕組みの関係上、デバッグは少し手間がかかりました。
(Claude Desktop 側のログを何度も見ることになりました...)
また、動的とはいえ HTML 他の構成要素やロジックはこちらで用意しているので、UI の変更にはコストがかかりそうです。
もう少し LLM 側に UI もまかせられると良いなと感じました。
