プラグインのユースケースでライブデータ、認証、実行を制御できるアクション、または自分で運用するインフラ上で動くコードが必要な場合は、MCP サーバーを追加します。サーバーは、ChatGPT と Codex が利用できるツールを定義します。カスタム UI を返す必要はありません。
まず、 ユースケース一覧でサポート対象として挙げた目標を確認します。各ツールは、 ユーザーの明確な目標の達成を支援し、 その目標に必要なデータとアクションだけを公開するようにします。
最初にツールを構築します。カスタム UI なしでサーバーが動作するようになったら、 視覚的な操作が必要なワークフロー向けに、MCP サーバーに UI を追加できます。
MCP ソフトウェア開発キットの選択
公式のソフトウェア開発キットには、スキーマヘルパー、サーバーのひな形、ストリーミングに対応した HTTP トランスポートが用意されています。
- TypeScript SDK:
@modelcontextprotocol/sdkとして公開 - Python SDK:
mcpとして公開
サーバーの技術スタックに合った SDK をインストールします。
# TypeScript
npm install @modelcontextprotocol/sdk zod
# Python
pip install mcp
サーバーの作成
安定した名前とバージョンを持つ MCP サーバーを作成します。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({
name: "acme-projects",
version: "1.0.0",
});
MCP サーバーは、初期化時に
instructions フィールドを返すこともできます。
ChatGPT と Codex は、これらの指示を
ツールのメタデータと併せて使用します。
サーバーの指示には、必須のツール実行順序や共通のレート制限など、ツール全体に適用されるガイダンスを記載します。最も重要な情報は、最初の 512 文字以内に収めてください。すべてのツールの説明を繰り返したり、モデルのパーソナリティを変更しようとしたりしないでください。
const server = new McpServer(
{ name: "acme-projects", version: "1.0.0" },
{
instructions:
"Before updating a project, call get_project to confirm its ID and current status.",
}
);
ユーザーの目標に基づくツールの定義
プラグインがサポートする必要のある個々のアクションごとに、ツールを 1 つ作成します。
無関係なモードを多数備えた 1 つのツールよりも、list_projects、get_project、
update_project のように、操作を絞ったツールを優先します。
各ツールには、次の要素が必要です。
- アクションを表す名前と、人が読んで理解しやすいタイトル
- 使用すべき場面を示す説明
- 明示的な入力スキーマ
- 構造化データを返す場合は、出力スキーマ
- 正確な安全性アノテーション
- リクエストを認可し、操作を実行するハンドラー
モデルは、このメタデータを使ってツールを呼び出すかどうか、またどのように呼び出すかを判断します。名前、説明、スキーマ、アノテーションは、ユーザーにとってのプラグインの動作を構成する要素として扱ってください。
import { z } from "zod";
server.registerTool(
"list_projects",
{
title: "List projects",
description:
"Use this when the user wants to find or review projects in their Acme workspace.",
inputSchema: {
status: z.enum(["active", "archived"]).optional(),
},
outputSchema: {
projects: z.array(
z.object({
id: z.string(),
name: z.string(),
status: z.string(),
})
),
},
annotations: {
readOnlyHint: true,
openWorldHint: false,
destructiveHint: false,
},
},
async ({ status }) => {
const projects = await listProjects({ status });
return {
structuredContent: { projects },
content: [
{
type: "text",
text: `Found ${projects.length} projects.`,
},
],
};
}
);
UI なしで有用な結果を返す方法
ツールの結果には、次の要素を含めることができます。
structuredContent:モデルが確認し、 後続の呼び出しで使用できる簡潔なデータcontent:モデルがユーザーに回答する際に役立つテキストやその他の MCP コンテンツ_meta:モデルには公開されない、クライアント固有のデータ
モデルがコンポーネントなしでワークフローを完了できるだけの情報を返します。後続のツールが同じレコードを参照できるように、構造化された結果には安定した識別子を使用してください。
ツールの結果には、シークレット、アクセストークン、不要な個人データを
含めないでください。_meta はモデルには公開されませんが、
認可や安全なストレージの代わりにはなりません。
MCP サーバーからのスキルのインポート
スキルの指示や補助ファイルをサーバーと一緒にバージョン管理し、デプロイするには、 MCP サーバーがスキルを提供するように設定します。プラグインの申請時に、 ツールをスキャン でスキルの静的なスナップショットを ドラフトにインポートします。
OpenAI は現在、 SEP-2640 スキル拡張のドラフトのうち、範囲を限定した静的なサブセットをサポートしています。 この提案は、まだ安定版の MCP 仕様には含まれていません。
拡張機能への対応の宣言
サーバーの初期化時に通知する機能情報で、
io.modelcontextprotocol/skills を宣言します。
{
"capabilities": {
"extensions": {
"io.modelcontextprotocol/skills": {}
}
}
}
宣言は capabilities.extensions の下に配置する必要があります。
OpenAI は、以前の experimental による宣言を認識しません。
スキルとリソースの一覧の提供
ページネーションに対応した skills/list メソッドをサポートします。各エントリには、次の要素を含める必要があります。
- スキルの
SKILL.mdを指すuri - 解析した
SKILL.mdのフロントマターの全エントリを含むfrontmatter。nameとdescriptionのエントリも含めてください。 SKILL.mdとすべての補助ファイルを含む、完全なresourcesリスト- 各リソースの SHA-256 ダイジェスト(
sha256:<64 lowercase hexadecimal characters>形式)
URI には skill:// の規約を使用します。SKILL.md を含むディレクトリの名前は、
スキル名と一致する必要があります。次に例を示します。
{
"skills": [
{
"uri": "skill://dice-roller/tabletop-dice/SKILL.md",
"frontmatter": {
"name": "tabletop-dice",
"description": "Roll one or more dice and report each result and the total."
},
"resources": [
{
"uri": "skill://dice-roller/tabletop-dice/SKILL.md",
"digest": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
{
"uri": "skill://dice-roller/tabletop-dice/references/notation.md",
"digest": "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
}
]
}
],
"nextCursor": "optional-next-page-cursor"
}
例のダイジェストは、必要な形式を示しています。テキストリソースの場合は、
content.text の UTF-8 バイト列のハッシュを計算します。blob リソースの場合は、
content.blob を base64 デコードしてから、デコードしたバイト列のハッシュを計算します。
一覧にある各 SKILL.md の URI に対して、skills/get もサポートします。
skills/list と同じ完全なエントリ構造を持つ skill オブジェクトを返してください。
リクエストパラメーターは、次のように扱います。
- 最初の
skills/listリクエストでは、空のオブジェクト({})を受け付けます。 - 以降の各
skills/listリクエストでは、{ "cursor": "next-page-cursor" }のように、返されたカーソルを受け付けます。 skills/getでは、{ "uri": "skill://dice-roller/tabletop-dice/SKILL.md" }のように、カタログの URI を受け付けます。
一覧にあるすべてのリソースの返却
マニフェスト内のすべての URI に対して、resources/read をサポートします。
リクエストと URI が一致するコンテンツ項目を、必ず 1 つだけ返してください。OpenAI は、
UTF-8 テキストまたは base64 エンコードされた blob を受け付けます。
インポート時に、OpenAI は次の点を検証します。
- OpenAI が一覧にあるすべてのリソースを取得し、ダイジェストを確認できること
- 取得した
SKILL.mdのフロントマターが、カタログのエントリと完全に一致すること - リソースのパスが安全かつ一意で、正規化による競合がないこと
- スキル全体がインポートの制限内に収まること
インポーターは、10 ページのカタログ全体で、名前が重複しないスキルを最大 5 つ受け付けます。各スキルには最大 100 個のファイルを含めることができ、次のサイズ制限が適用されます。
| コンテンツ | 上限 |
|---|---|
SKILL.md | 256 KiB |
| 各補助ファイル | 1 MiB |
| 1 つのスキルの全リソース | 5 MiB |
| 1 回のスキャンで生成されるスキルアーカイブ | 8 MiB |
アーカイブの合計サイズの上限には、ZIP 形式にまとめる際のオーバーヘッドも含まれます。
いずれかのエントリが検証に失敗するか、上限を超えた場合でも、 ツールをスキャン は サーバーのツールを返しますが、下書きにインポート済みのスキルは更新しません。 サーバーを修正して、もう一度スキャンしてください。
MCP からインポートしたスキルは提出時点のスナップショットであり、 実行時に最新の状態を取得するリソースではありません。スキルを変更したら、 ツールをスキャン を再実行し、 インポートされたスキルをレビューして、プラグインの新しいバージョンを提出してください。手順全体については、 プラグインの提出を参照してください。
リクエストの認証と認可
ツールが非公開データを読み取る場合や、ユーザーに代わってアクションを実行する場合は、認証を追加してください。すべてのリクエストに対して、MCP サーバーで必ず認可を行ってください。ユーザーにアクセス権があるかどうかの判断をモデルに任せてはいけません。
OAuth ディスカバリー、セキュリティスキーム、認可チャレンジについては、 ユーザーの認証を参照してください。
複数のアカウントを使いやすくするために、認証を必要とする
読み取り専用のプロファイルツールを公開し、_meta["openai/profile"]: true を付けてください。
OpenAI はプロファイル情報を使って接続済みアカウントを一貫して識別し、
ユーザーがアカウントを区別しやすくします。リクエストの検証済み認証情報から
プロファイルを特定し、すべてのツール呼び出しの範囲をその認証情報に限定してください。
プロファイルツールがなくても、ユーザーは複数のアカウントを接続できます。スキーマと実装例については、
複数アカウントのサポート
を参照してください。
ツールのアノテーションと誘発
実際の動作に応じて、アノテーションを設定してください。
readOnlyHint:ツールが状態を変更できない場合にのみtrueを設定します。destructiveHint:ツールが元に戻せない、または元に戻すことが難しい結果を 引き起こす可能性がある場合は、trueを設定します。openWorldHint:ツールが公開インターネットや、対象範囲が限定されていない外部リソースにアクセスする場合は、trueを設定します。 ウェブ検索などの読み取り専用アクションによるアクセスも含まれます。 アクセス範囲が特定の非公開アカウントやワークスペースに限られるツールでは、 サービスが外部でホストされていても、この値をfalseに設定できます。
アノテーションは、ChatGPT と Codex が適切な確認手順や安全な動作を選ぶために役立ちます。ただし、サーバー側で行う認可、検証、確認の代わりにはなりません。
元のツール呼び出しで提供されなかった構造化情報をサーバーが必要とする場合は、MCP の誘発を使用してください。求める情報は、ユーザーが無理なく提供できるものに限定してください。シークレットの収集や通常の認証の回避に使用してはいけません。
社内ナレッジとの互換性
社内ナレッジでは、MCP サーバーの読み取り専用ツールを使用できます。
プラグインを社内ナレッジの情報源として利用可能にするには、
search ツールと fetch ツールの標準入力スキーマを実装し、その他の読み取り専用ツールには
readOnlyHint: true を設定してください。
モデルが引用する情報源には、ユーザーが開ける絶対 URL を返してください。
内部のドキュメント識別子は、結果の id フィールドに格納してください。
必要なスキーマと結果の形式については、
ChatGPT と API 連携向けの MCP サーバーの構築を参照してください。
ローカルでの実行とテスト
Streamable HTTP エンドポイントを通常は /mcp に公開し、
MCP Inspector で検査してください。
npx @modelcontextprotocol/inspector
Inspector の UI で Streamable HTTP を選択し、
http://localhost:3000/mcp を入力してください。
Inspector を使って、次の項目を確認してください。
- 初期化が成功することを確認します。
- サーバーの指示と、サーバーが提示するツール一覧をレビューします。
- 代表的な入力と無効な入力を使って、すべてのツールを呼び出します。
- スキーマ、結果、エラー、アノテーションを検証します。
- 非公開データへのアクセスと書き込みアクションに対して、認可が必ず行われることを確認します。
次に、開発者モードでサーバーを ChatGPT に接続し、 ユースケース一覧にある直接的なリクエスト、間接的なリクエスト、 エッジケース、対象範囲外のリクエストを実行してください。
エンドポイントのデプロイ
プラグインの一般公開を申請するには、MCP サーバーを、外部からアクセス可能な 安定した HTTPS エンドポイントにデプロイしてください。セキュア MCP トンネルを使うと、 開発者モードで非公開の MCP サーバーに接続できますが、 一般公開の申請要件は満たしません。
本番環境のエンドポイントは、次の要件を満たす必要があります。
- MCP の Streamable HTTP トランスポートに対応していること
- 安定した URL で応答すること(通常は末尾が
/mcpの URL) - プラグインのワークフローに必要なレイテンシと可用性の要件を満たすこと
- 必要なサービスとデータストアにアクセスできること
- 認証と認可の境界を維持すること
- 初期化やツール呼び出しが失敗した際のログとメトリクスを出力すること
MCP サーバーを非公開のままにする必要がある場合は、 MCP リクエストを非公開サーバーに転送する公開 HTTPS プロキシをデプロイしてください。 OpenAI が管理する mTLS を使って、 ChatGPT を MCP クライアントとして認証してください。プラグインでユーザー認証が必要な場合は、 OAuth 2.1 を使用してください。ネットワークで IP 許可リストが必要な場合は、 公開されている ChatGPT コネクタの IP アドレス範囲を使用し、 許可リストを自動更新してください。IP 許可リストは、 認証や認可の代わりにはなりません。
公開エンドポイントは、プラグインのレビューと ドメイン検証のために、引き続きアクセス可能な状態にしておく必要があります。 一般公開の申請では、セキュア MCP トンネル単独、一時的なトンネル、 ローカルエンドポイントを使用しないでください。
インフラの選定
MCP サーバーは、サーバーレス、コンテナ、エッジ、または従来型のアプリケーションインフラにデプロイできます。次の点を基準にプラットフォームを選んでください。
- ランタイムと依存関係への対応
- ストリーミングレスポンスの動作
- コールドスタートとリクエストのレイテンシ
- 必要なサービスへのネットワークアクセス
- データレジデンシーとコンプライアンスの要件
- シークレット管理
- ログ記録、トレース、アラート
- ロールバックとバージョン管理への対応
サーバーでオプションの UI アセットもホストする場合は、 コンポーネントのコンテンツセキュリティポリシーで許可された 安定したオリジンに、それらのアセットをデプロイしてください。
本番環境のエンドポイントの設定
デプロイ前に、次の作業を行ってください。
- ホストのシークレット管理システムを通じて、本番環境の認証情報を設定します。
- 認可サーバーと、許可するリダイレクトの動作を設定します。
- 実行コストが高いツールや、外部から見える操作を行うツールに、タイムアウトとレート制限を適用します。
- デバッグ用のレスポンスと不要な個人データを削除します。
- ログにアクセストークンや機密性の高いツールの結果が含まれていないことを確認します。
デプロイ後、MCP Inspector を使って 本番環境のエンドポイントを呼び出してください。 初期化、サーバーの指示、ツール、スキーマ、アノテーション、 認証、結果、エラーを検証してください。
更新への備え
公開済みのツール名とスキーマの後方互換性を維持してください。既存の仕様との互換性を損なわずにフィールドやツールを追加してください。メタデータを変更した場合は、開発者モードの接続を更新し、提出前に評価セットを再実行してください。
オプションの UI で、キャッシュされたコンポーネントの動作に支障をきたす可能性のある HTML、JavaScript、CSS の変更を行う場合は、リソース識別子にバージョンを付けてください。
オプションの UI の追加
ツールが一連の処理を最後まで実行できるようになったら、視覚的な操作が必要なユースケースがあるかを判断してください。表、地図、編集可能なスケジュール、比較ビューには UI が役立つ場合があります。情報の検索、ステータスの確認、バックグラウンドでのアクションには、多くの場合 UI は必要ありません。
続いてMCP サーバーへの UI の追加を参照し、 MCP Apps リソースを登録して、選択したツールに関連付けます。
セキュリティ上の注意事項
- すべてのツール入力を信頼できないものとして扱います。
- サーバー側でパラメータを検証し、認可を徹底します。
- 重大な影響を及ぼす書き込み操作には確認を必須にします。
- ツールのメタデータや結果にシークレットや機密データを含めないようにします。
- 認証情報や不要な個人データをログに記録せず、障害の調査に十分な状況情報を記録します。
- コストの高い操作や外部から確認できる操作にレート制限を適用します。