build-mcp-use-server について
mcp-use で MCP サーバーや ChatGPT アプリを構築する開発者向けスキルです。プロジェクトの確認から実装、検証までを案内し、導入済み API の型と OpenAI Apps SDK UI への準拠を重視します。
主な機能
- 01プロジェクト構成と mcp-use の確認
- 02サーバーとビューの実装ガイド
- 03ChatGPT 拡張機能と認証への対応
- 04型に基づくステートレス設計
- 05CLI によるツール呼び出しとビューの検証
活用シーン
- 01mcp-use の ChatGPT アプリの新規作成
- 02MCP サーバーへのインタラクティブなビュー追加
- 03旧 API や移行時の問題の解決
MCP 対応
mcp-use の MCPServer を使った MCP サーバー構築を案内し、mcp-use client で実際のツール呼び出しを行って実装を検証します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yigitkonur/ skills-by-yigitkonur --skill build-mcp-use-serverインストール後、Claude Code で/build-mcp-use-serverを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdagents/openai.yamlreferences/advanced-features.mdarchestra-and-multi-host.mdauth.mdchatgpt-apps-ui.mdchatgpt-extensions.mdmigration.mdserver.mdskills-over-mcp.mdverification.mdviews.mdscripts/audit-server-readiness.shaudit-server-readiness.sh.mdcheck-mcp-use-version.shcheck-mcp-use-version.sh.mdscaffold-mcp-use-server.shscaffold-mcp-use-server.sh.md詳細
mcp-use と ChatGPT Apps で構築する
インストール済みの mcp-use パッケージ、そのエクスポートされた型、生成された宣言ファイル(mcp-env.d.ts)、および docs.mcp-use.com の公式ドキュメントを、信頼できる唯一の情報源として扱ってください。
このスキルは、公式の MCPServer API サーフェスを基盤とし、公式の mcp-use スキルの簡潔なアーキテクチャを採用するとともに、mcp-use で構築されるすべての ChatGPT アプリが、デフォルトで OpenAI Apps SDK UI(@openai/apps-sdk-ui)に準拠することを保証します。
ワークフロー
- プロジェクトを調査する:
package.json、サーバーのエントリーポイント、エクスポートされたツール参照、views/、skills/、およびインストール済みのmcp-useのバージョンを確認します(Node.js >= 22.22.2 が必要です)。 - スキャフォールドを作成する、または初期化する:新規プロジェクトの場合は、公式 CLI でスキャフォールドを作成します。
(ヘッドレスのツールサーバーにはnpx create-mcp-use-app@latest my-app --template mcp-apps--template mcp-serverを使用します)。 - 関連するリファレンスを読む:
- サーバー:ツール、リソース、プロンプト、ミドルウェア、リクエストコンテキスト、結果エンベロープ、
ServerConfig。 - ビュー:MCP Apps、React フック、インタラクションチャネル、ホスト機能、CSP。
- OpenAI Apps SDK UI ガイド:29 種類すべてのコンポーネント、フック、Tailwind 4 のセットアップ、デザイントークン、テーマ同期。
- ChatGPT プラグインの拡張機能:起動エントリーポイント(
global、thread、file)、ネイティブのserver.settings()、ツールアイコン、useDeepLink()、useModelContext()。 - 認証:OAuth プロバイダーアダプター、
mixedAuth: true、ツールレベルのsecuritySchemes、およびctx.authでの検証済み ID。 - MCP を使ったスキル:再利用可能なエージェントのワークフローと
skills/配下の規約。 - 高度な機能:OpenAPI(
fromOpenAPI)、プロキシ、通知、サブスクリプション、インタラクティブな情報要求(inputRequired)。 - マルチホストと Archestra:ChatGPT、Claude Desktop、Archestra へのデプロイ(インストール済みサーバーのルートと所有アプリ)。
- 移行:レガシーな v1 パターン、非推奨のインポート、古いヘルパーを v2 にアップグレードする方法。
- 検証:
mcp-use typecheck、mcp-use dev、mcp-use client、mcp-use screenshotによる検証ループ。
- サーバー:ツール、リソース、プロンプト、ミドルウェア、リクエストコンテキスト、結果エンベロープ、
- インストール済みの型に基づいて実装する:必ず静的なツール参照をエクスポートし、サーバーインスタンスをデフォルトエクスポートします。
- 決定論的に検証する:
mcp-use clientで実際のツール呼び出しを実行し、mcp-use screenshotでビューのスクリーンショットを取得します。
コア不変条件
- ルートサーバーのインポート: 必ず
"mcp-use"からMCPServerとサーバープリミティブをインポートしてください(レガシーの"mcp-use/server"サブパスは廃止されています)。 - ツール参照の静的エクスポート: すべてのツール参照を必ずエクスポートしてください(
export const myTool = server.tool(...))。TypeScript コンパイラーは、エクスポートされたツール名からmcp-env.d.ts内のビューとクライアントのコントラクトを導出します。 - サーバーのデフォルトエクスポート: サーバーインスタンスを必ずデフォルトエクスポートしてください(
export default server;)。CLI ランナー(mcp-use dev、build、start)が HTTP リスナーと Vite ビューのパイプラインを管理します。 - 定義優先のツールと標準スキーマ:
server.tool(definition, callback)でツールを定義してください。ツール引数にはinputSchemaを、モデルのプロンプトにはschemaを使用します。モデルが値を入力するスキーマフィールドは、必ず説明してください。 - 生の結果エンベロープ: ツールのコールバックは、生の
CallToolResultエンベロープ({ content: [...], structuredContent?: ... })を返す必要があります。非推奨の v1 ヘルパー(text()、object()、widget())は使用しないでください。予期される失敗は{ isError: true, content: [...] }を返し、予期しない失敗はスローしてください。 - ビューのアーキテクチャ: 各ビューは
views/<name>/view.tsxに配置してください。各ビューを所有するツールは必ず1つだけとし、view: { name: "<name>" }と一致させ、必須のoutputSchemaを指定してください。一致するstructuredContentを返してください。 - OpenAI Apps SDK UI への準拠: ChatGPT アプリはすべて
@openai/apps-sdk-uiをインストールし、@openai/apps-sdk-ui/cssをインポートし、@sourceで Tailwind 4 を設定して、インタラクティブコンポーネントを<AppsSDKUIProvider linkComponent="a">でラップする必要があります。 - ステートレスなリクエストモデル: サーバーインスタンスのスコープはリクエスト単位です。複数のリクエストにまたがる状態管理や認証に、モジュールグローバルやインメモリのセッションレジストリを使用しないでください。
ctx.requestStateまたは外部データベースを使用してください。 - 厳密な CLI フラグ構文: v2.7.2 以降では、ブール値の CLI フラグにインライン値を指定するとエラーになります(
--tunnel=falseは失敗します。--tunnelを使用するか、省略してください)。
ガードレールとよくあるアンチパターン
- 存在しない
@openai/apps-sdk-uiコンポーネントは決してインポートしないでください:Cardは@openai/apps-sdk-uiに存在しません。サーフェスカードは、Tailwind のユーティリティクラス(rounded-2xl border border-default bg-surface p-4 shadow-sm)でスタイルを設定した標準の<div>要素です。AvatarGroupとCopyTooltipは、公式の@openai/apps-sdk-uiコンポーネントです(@openai/apps-sdk-ui/components/Avatarと@openai/apps-sdk-ui/components/Tooltipからインポートします)。CartとCheckmarkは@openai/apps-sdk-ui/components/Iconに存在しません。Check、CheckCircle、Search、Plus、Settingsなどを使用してください。
<AppsSDKUIProvider>でlinkComponentを決して省略しないでください:TypeScript の契約でlinkComponentが必須です(例:<AppsSDKUIProvider linkComponent="a">)。useToolContext().toolOutputを型を絞り込まずに決して読み取らないでください:useToolContext()は判別共用体です。status === "pending"の場合、toolOutputはundefinedです。toolOutputにアクセスする前に、必ずstatus === "pending"とstatus === "error"を処理してください。キャンセルされたアクションについては、error instanceof ToolCancelledErrorで型を絞り込んでください。websiteUrl、icons、loggingはMCPServerのトップレベルで設定してください:icons?: Icon[]、websiteUrl?: string、logging?: LoggingOptionsはServerConfigのトップレベルプロパティです。架空のbranding: { ... }オブジェクトに入れ子にしないでください(server.brandingは読み取り専用ゲッターです)。- サーバーのログレベルは、
logging: { enabled: true, level: "info" }で設定するか、環境変数MCP_USE_LOG_LEVELで上書きします。
- レガシーな v1 の
ctx.elicit()は決して使用しないでください:ステートレスなリクエストモデルへの移行に伴い、v2.0.0 で削除されました。公式のプロトコルプリミティブであるinputRequired、inputRequired.elicit、inputRequired.elicitUrl、inputResponse、acceptedContentを使用してください。 - 誤った CLI コマンドは決して使用しないでください:
- クライアント接続の構文:
mcp-use client connect <name> <url> [options](例:npx mcp-use client connect dev http://localhost:3000/mcp)。 - スクリーンショットの構文:
mcp-use screenshot (--server <name> | --mcp <url>) --tool <name> [args...] [options]。
- クライアント接続の構文:
MCPServer の基本アーキテクチャ
MCPServer とサーバーのドキュメントに基づいてください:
import { MCPServer } from "mcp-use";
const server = new MCPServer({
name: "catalog-server",
version: "1.0.0",
title: "Inventory & Catalog Manager",
description: "Manage product catalog, stock levels, and search items.",
instructions: "Use lookup-item before creating an order. Confirm stock availability.",
basePath: "/mcp", // default route
host: "127.0.0.1", // "0.0.0.0" for public hosting
port: 3000,
legacy: "stateless", // "stateless" (default) or "reject"
websiteUrl: "https://example.com",
icons: [
{ src: "icons/logo.svg", mimeType: "image/svg+xml", sizes: ["20x20"] }
],
logging: { enabled: true, level: "info" }, // Overridden by MCP_USE_LOG_LEVEL env var
skills: true, // Auto-discover conventional skills/ directory
cors: {
origin: "*",
credentials: true,
},
});
export default server;
ツール定義と結果エンベロープ
ドキュメント:サーバーツール
import { MCPServer } from "mcp-use";
import { z } from "zod";
const server = new MCPServer({ name: "catalog", version: "1.0.0" });
export const getProduct = server.tool(
{
name: "get-product",
title: "Get Product Details",
description: "Fetch product metadata, pricing, and live inventory by SKU.",
icons: [
{ src: "icons/product.svg", mimeType: "image/svg+xml", sizes: ["20x20"] }
],
inputSchema: z.object({
sku: z.string().describe("Stock Keeping Unit identifier (e.g. 'PROD-100')"),
}),
outputSchema: z.object({
sku: z.string(),
title: z.string(),
price: z.number(),
inStock: z.boolean(),
}),
annotations: {
readOnlyHint: true,
openWorldHint: false,
},
visibility: "model", // "model" (default) or "app" (hidden from LLM, visible to UI views)
view: {
name: "product-card", // binds to views/product-card/view.tsx
description: "Interactive product display card",
},
},
async ({ sku }, ctx) => {
// Expected operational failure
if (!sku.startsWith("PROD-")) {
return {
isError: true,
content: [{ type: "text", text: `Invalid SKU format: ${sku}` }],
};
}
const data = { sku, title: "Precision Screwdriver", price: 29.99, inStock: true };
return {
content: [{ type: "text", text: `Product ${data.title} (${sku}) is in stock at $${data.price}` }],
structuredContent: data,
_meta: { internalId: "db_98213" }, // Private to view, hidden from LLM
};
}
);
export default server;
ChatGPT Apps と OpenAI Apps SDK UI への準拠
ドキュメント:
1. インストールとスタイルの設定
npm install @openai/apps-sdk-ui tailwindcss @tailwindcss/vite
views/main.css(またはビュー固有の CSS)で:
@import "tailwindcss";
@import "@openai/apps-sdk-ui/css";
@source "../node_modules/@openai/apps-sdk-ui";
2. Apps SDK UI とテーマ同期を使ったビューコンポーネント
views/product-card/view.tsx を作成します:
import "./main.css";
import React, { useEffect } from "react";
import { useToolContext, useModelContext, useViewTheme, ToolCancelledError, type ViewConfig } from "mcp-use/react";
import { AppsSDKUIProvider } from "@openai/apps-sdk-ui/components/AppsSDKUIProvider";
import { Button } from "@openai/apps-sdk-ui/components/Button";
import { Badge } from "@openai/apps-sdk-ui/components/Badge";
import { applyDocumentTheme } from "@openai/apps-sdk-ui/theme";
import { Check, Plus } from "@openai/apps-sdk-ui/components/Icon";
export const viewConfig = {
autoResize: true,
displayModes: ["inline", "fullscreen"],
preferredDisplayMode: "inline",
} satisfies ViewConfig;
export default function ProductCardView() {
const { status, toolInput, toolOutput, error } = useToolContext<"get-product">();
const theme = useViewTheme();
const { add } = useModelContext();
// Synchronize host theme with Apps SDK UI data-theme attribute
useEffect(() => {
if (theme) applyDocumentTheme(theme);
}, [theme]);
if (status === "pending") {
return <div className="p-4 text-secondary text-sm">Loading SKU {toolInput?.sku}…</div>;
}
if (status === "error") {
if (error instanceof ToolCancelledError) {
return <div className="p-4 text-secondary text-sm">Action cancelled: {error.reason}</div>;
}
return <div className="p-4 text-danger text-sm">Error: {error.message}</div>;
}
return (
<AppsSDKUIProvider linkComponent="a">
{/* Standard surface card styled with semantic tokens */}
<div className="w-full max-w-md rounded-2xl border border-default bg-surface p-4 shadow-sm">
<div className="flex items-start justify-between">
<div>
<h2 className="heading-md">{toolOutput.title}</h2>
<p className="text-secondary text-sm">SKU: {toolOutput.sku}</p>
</div>
<Badge color={toolOutput.inStock ? "success" : "warning"}>
{toolOutput.inStock ? "In Stock" : "Backorder"}
</Badge>
</div>
<div className="mt-4 flex items-center justify-between border-t border-subtle pt-3">
<span className="text-lg font-semibold">${toolOutput.price.toFixed(2)}</span>
<Button
color="primary"
onClick={() => {
// Attach verified evidence to next ChatGPT model turn (synchronous void)
add("selected:product", {
type: "text",
text: `Selected SKU ${toolOutput.sku}: $${toolOutput.price}`,
});
}}
>
<Plus className="size-4" />
Add to Chat
</Button>
</div>
</div>
</AppsSDKUIProvider>
);
}
3. Apps SDK UI コンポーネント一覧(29 コンポーネント)
すべて @openai/apps-sdk-ui/components/<Component> からインポートします。
| カテゴリ | コンポーネント | 用途 |
|---|---|---|
| フィードバックとステータス | Alert, Badge, Indicator, EmptyMessage, ShimmerText | ステータスバナー、ライブバッジ、空の状態、読み込み中のシマー表示。 |
| アイデンティティとアイコン | Avatar, Image, Icon | ユーザープロフィール、画像のフォールバック、700 種類以上のモノクロ SVG。 |
| アクション | Button, ButtonLink(および CopyButton) | プライマリ、セカンダリ、ソフト、アウトラインのアクションボタンとコピー操作。 |
| フォーム入力 | Input, Textarea, Checkbox, Switch, RadioGroup, Slider, SegmentedControl, Select, SelectControl, TagInput | アクセシブルで検証機能を備えた入力とグループ化されたコントロール。 |
| 日付ピッカー | DatePicker, DateRangePicker | カレンダーポップアップと日付範囲セレクター。 |
| オーバーレイ | Menu, Popover, Tooltip | ドロップダウンメニュー、アンカー付きポップオーバー、コンテキストに応じたツールチップ。 |
| タイポグラフィとコンテンツ | Markdown, CodeBlock, TextLink | スタイル付きの Markdown レンダラー、シンタックスハイライト付きコードブロック、テーマに合わせたハイパーリンク。 |
| モーションとトランジション | Transition(Animate, AnimateLayout, SlotTransitionGroup, TransitionGroup) | iframe 用の滑らかな高さ、フェード、レイアウト変形トランジション。 |
| プロバイダー | AppsSDKUIProvider | ルーターの linkComponent 設定を提供するルートコンテキスト。 |
4. 公式フックの一覧(@openai/apps-sdk-ui/hooks)
useBreakpoint(size: BreakpointName): レスポンシブブレークポイントに一致するかどうかを示すブール値(例:useBreakpoint("md"))。useAutoGrowTextarea(): テキストエリアの高さを動的に自動拡張します。useAnimatedScrollTo(): キャンセル可能な、プログラムによる滑らかなスクロール。useDocumentVisible(): iframe の表示状態を追跡します。useIsMounted(),useLatestValue(),usePrevious(),useScrollable(),useSimulatedProgress(),useStableCallback(),useTextSelection(),useTrailingValue()。
ChatGPT プラグイン拡張機能
ドキュメント:ChatGPT プラグイン拡張機能 | 拡張機能リファレンス
1. 型付き起動エントリーポイント
tool.view.entrypoints で起動サーフェスを宣言します。
global:ChatGPT のトップレベルランチャーからアプリを起動します(入力スキーマは{}を受け付ける必要があります)。thread:会話タブ内でアプリを起動します(入力スキーマは{}を受け付ける必要があります)。file:一致する拡張子のファイルビューアーとしてアプリを起動します(入力スキーマは{ file: { name: string, resourceUri: string } }を受け取る必要があります)。
export const openCsvViewer = server.tool(
{
name: "open-csv",
title: "CSV Table Viewer",
inputSchema: z.object({
file: z.object({ name: z.string(), resourceUri: z.string() }),
}),
outputSchema: z.object({ file: z.object({ name: z.string(), resourceUri: z.string() }) }),
view: {
name: "csv-viewer",
entrypoints: [{ type: "file", extensions: [".csv", ".tsv"] }],
},
},
async ({ file }) => ({ content: [], structuredContent: { file } })
);
2. ネイティブプラグイン設定(server.settings)
型付きフィールドとレイアウトセクションを使って、ChatGPT のネイティブプラグイン設定を登録します。
server.settings({
fields: {
theme: { type: "string", label: "Theme", default: "system" },
autoSync: { type: "boolean", label: "Automatic Sync", default: true },
},
layout: [
{
title: "Preferences",
fields: ["theme", "autoSync"],
},
],
read: async (ctx) => ({ theme: "system", autoSync: true }),
update: async (patch, ctx) => ({ theme: "system", autoSync: true, ...patch }),
});
3. 受信ディープリンク(useDeepLink)
グローバルエントリーポイントに送信された受信 URL を読み取ります。
import { useDeepLink } from "mcp-use/react";
export function AppRouter() {
const { url } = useDeepLink(); // e.g. "/items?tag=hardware"
}
4. 選択中のモデルコンテキスト(useModelContext)
次のユーザーモデルターンとともに送信するエビデンス添付ファイルを管理します(attachments は Record<string, ModelContextBlock>、add は同期的な void です)。
import { useModelContext } from "mcp-use/react";
const { attachments, add, remove, clearAttachments } = useModelContext();
// Attach text summary (synchronous void, no title)
add("summary", { type: "text", text: "Order Summary: Total $120.00" });
// Attach image with Base64 data (PNG/JPEG/GIF/WebP, max 10MB)
add("receipt", { type: "image", data: receiptBase64, mimeType: "image/png" });
// Enumerate attached blocks
Object.entries(attachments).map(([key, block]) => (
<span key={key}>{key}: {block.type}</span>
));
認証と混合認証
MCPServer で mixedAuth: true を設定すると、1 つのエンドポイントからパブリックな検出、任意認証、保護されたツールを提供できます。
import { MCPServer } from "mcp-use";
import { oauthAuth0Provider } from "mcp-use/oauth/auth0";
import { z } from "zod";
const server = new MCPServer({
name: "store",
version: "1.0.0",
oauth: oauthAuth0Provider({ domain: process.env.AUTH0_DOMAIN! }),
mixedAuth: true, // Enables unauthenticated tools/list discovery
});
// Public tool: Anyone may call without token
export const searchCatalog = server.tool(
{
name: "search-catalog",
inputSchema: z.object({ query: z.string() }),
securitySchemes: [{ type: "noauth" }],
},
async ({ query }, ctx) => {
return { content: [{ type: "text", text: `Results for ${query}` }] };
}
);
// Protected tool: Requires sign-in with specific scopes
export const checkout = server.tool(
{
name: "checkout",
inputSchema: z.object({ cartId: z.string() }),
securitySchemes: [{ type: "oauth2", scopes: ["orders:write"] }],
},
async ({ cartId }, ctx) => {
const userId = ctx.auth.user.id;
return { content: [{ type: "text", text: `Order placed for user ${userId}` }] };
}
);
対応プロバイダー:Clerk(mcp-use/oauth/clerk)、Auth0(mcp-use/oauth/auth0)、WorkOS(mcp-use/oauth/workos)、Supabase(mcp-use/oauth/supabase)、Keycloak(mcp-use/oauth/keycloak)、Better Auth(mcp-use/oauth/better-auth)、Scalekit(mcp-use/oauth/scalekit)、Convex(mcp-use/oauth/convex)、およびカスタム(oauthCustomProvider)。
インタラクティブな情報要求
ステートレスなリクエストモデルへの移行に伴い、v2.0.0 で ctx.elicit() は削除されました。公式のプロトコルプリミティブを使用してください。
import { MCPServer, inputRequired, inputResponse, acceptedContent } from "mcp-use";
import { z } from "zod";
const confirmSchema = z.object({ approve: z.boolean().describe("Confirm action") });
server.tool(
{
name: "delete-resource",
inputSchema: z.object({ id: z.string() }),
},
async ({ id }, ctx) => {
const response = inputResponse(ctx.inputResponses, "delete-approval");
if (response.kind === "elicit" && response.action !== "accept") {
return { isError: true, content: [{ type: "text", text: "Action cancelled" }] };
}
const confirmation = acceptedContent(ctx.inputResponses, "delete-approval", confirmSchema);
if (!confirmation) {
return inputRequired({
inputRequests: {
"delete-approval": inputRequired.elicit({
message: `Permanently delete resource ${id}?`,
requestedSchema: confirmSchema,
}),
},
});
}
if (!confirmation.approve) {
return { isError: true, content: [{ type: "text", text: "Action rejected" }] };
}
// Side-effects execute only after confirmed, validated input
await db.delete(id);
return { content: [{ type: "text", text: `Deleted ${id}` }] };
}
);
外部認証には、inputRequired.elicitUrl({ message: "...", url: authUrl }) を使用してください。
MCP を介したスキル(SEP-2640)
ドキュメント:MCP を介したスキル | スキルリファレンス
ツールとともに、再利用可能な指示ワークフローを提供します。
- スキルを
skills/<skill-name>/SKILL.mdの下に配置します。 - 自動検出はデフォルトで有効です。
MCPServerでskills: trueを指定して必須にするか、skills: falseを指定して無効にします。 - ホストは
skills/listとskills/getを使用してスキルのメタデータを取得します。
CLI とツールのリファレンス
ドキュメント:CLI リファレンス | クライアント CLI | 検証
# Scaffold new v2 project
npx create-mcp-use-app@latest my-server --template mcp-apps
# Start development with hot reload, Vite views pipeline, and Inspector
mcp-use dev
# Start development with authenticated public tunnel for live ChatGPT testing
mcp-use dev --tunnel
# Typecheck server and views
mcp-use typecheck
# Build for production
mcp-use build
# Start production server
mcp-use start
# Connect live client session straight from terminal
mcp-use client connect dev http://localhost:3000/mcp
# List tools and call with arguments
mcp-use client dev tools list
mcp-use client dev tools call get-product sku=PROD-100
# Capture automated screenshot of view preview
mcp-use screenshot --server dev --tool get-product sku=PROD-100
# Deploy to mcp-use cloud (requires GitHub repo)
mcp-use deploy
公式ドキュメントへのリンク
- API ベース:MCPServer クラスリファレンス
- 公式ドキュメント:docs.mcp-use.com
- OpenAI Apps SDK UI: