プラグインのユースケースで、最新のデータ、認証、権限に応じた操作、または自身が運用するインフラでのコード実行が必要な場合は、MCP サーバーを追加します。サーバーでは、ChatGPT と Codex が利用できるツールを定義します。カスタム UI を返す必要はありません。
まず、ユースケース一覧で、プラグインが対応するユーザーの目的を確認します。各ツールは、ユーザーの明確な目的を達成するためのものとし、その目的に必要なデータと操作だけを公開します。
まずはツールを構築します。カスタム UI なしでサーバーが動作することを確認したら、画面上での操作が必要なワークフロー向けに MCP サーバーに UI を追加できます。
MCP ソフトウェア開発キットの選択
公式のソフトウェア開発キットには、スキーマ定義を支援するヘルパー、サーバーのひな型、Streamable 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 のように、各ツールの役割を明確に分けます。
各ツールには次の要素が必要です。
- 操作の内容がわかる名前と、人が読んで理解できるタイトル
- どのような場合に使うかを示す説明
- 明示的な入力スキーマ
- 構造化データを返す場合は出力スキーマ
- 安全性に関する正確なアノテーション
- リクエストを認可し、操作を実行するハンドラー
モデルは、このメタデータを基に、ツールを呼び出すかどうかや、その呼び出し方を判断します。名前、説明、スキーマ、アノテーションは、ユーザーから見たプラグインの動作を決める要素として扱ってください。
// Local example data; replace this with your application data.
const projects = [
{ id: "project-1", name: "Website", status: "active" },
{ id: "project-2", name: "Launch", status: "archived" },
];
async function listProjects({ status }: { status?: string }) {
return projects.filter((project) => !status || project.status === status);
}
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 サーバーで認可を行い、ユーザーにアクセス権があるかどうかの判断をモデルに委ねないでください。
OAuth のディスカバリー、セキュリティスキーム、認可チャレンジについては、ユーザー認証を参照してください。
複数のアカウントを使いやすくするには、認証を必要とする読み取り専用のプロフィールツールを公開し、_meta["openai/profile"]: true を設定します。OpenAI はプロフィール情報を使って接続済みのアカウントを一貫した方法で識別し、ユーザーがアカウントを見分けられるようにします。リクエストの検証済み認証情報を基にプロフィールを特定し、各ツール呼び出しでアクセスできる範囲を、その認証情報で許可された範囲に限定します。プロフィールツールがなくても、ユーザーは複数のアカウントを接続できます。スキーマと実装例については、複数アカウントのサポートを参照してください。
ツールのアノテーションと追加入力のリクエスト
ツールの実際の動作に合わせてアノテーションを設定します。
readOnlyHint:ツールが状態を変更できない場合に限り、trueにします。destructiveHint:ツールによる操作を元に戻せない場合や、元に戻すのが難しい場合は、trueにします。openWorldHint:ツールが公開インターネットにアクセスする場合や、アクセス先が特定の外部リソースに限定されない場合は、trueにします。ウェブ検索などの読み取り専用の操作も対象です。アクセス先が特定の非公開アカウントやワークスペース内に限定されるツールでは、そのサービスが外部でホストされていてもfalseに設定できます。
ChatGPT と Codex は、アノテーションを基に、どのような確認や安全対策が適切かを判断します。サーバー側での認可、検証、確認は引き続き必要です。
最初のツール呼び出しに含まれていない構造化情報がサーバーで必要になった場合は、MCP の elicitation 機能を使ってユーザーに追加入力を求めます。求める情報は、ユーザーが無理なく提供できるものに絞ります。シークレットの収集や通常の認証の回避にこの機能を使わないでください。
社内ナレッジへの対応
社内ナレッジでは、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 で次の項目を確認します。
- 初期化が成功することを確認します。
- サーバーの指示と、サーバーが公開しているツールの一覧を確認します。
- すべてのツールを、代表的な入力と無効な入力の両方で呼び出します。
- スキーマ、結果、エラー、アノテーションを検証します。
- 非公開データへのアクセスと書き込み操作で認可が適用されることを確認します。
次に、カスタム MCP サーバーを ChatGPT に接続し、ユースケース一覧にある直接的な依頼、間接的な依頼、境界条件に該当する依頼、対応範囲外の依頼を試します。
エンドポイントのデプロイ
プラグインの公開申請を行う場合は、安定した URL で外部からアクセスできる HTTPS エンドポイントに MCP サーバーをデプロイします。Secure MCP Tunnel を使うと、ChatGPT から非公開の MCP サーバーに接続できますが、公開申請の要件は満たせません。
本番エンドポイントは、次の要件を満たす必要があります。
- MCP の Streamable HTTP トランスポートをサポートすること
- 安定した URL で応答すること(通常、末尾は
/mcp) - プラグインのワークフローに必要なレイテンシと可用性の要件を満たすこと
- 必要なサービスやデータストアにアクセスできること
- 認証と認可の境界を維持すること
- 初期化やツール呼び出しの失敗をログとメトリクスに記録すること
MCP サーバーを非公開に保つ必要がある場合は、MCP リクエストを非公開サーバーに転送する公開 HTTPS プロキシをデプロイします。OpenAI 管理の mTLS を使用して ChatGPT を MCP クライアントとして認証し、プラグインでユーザー認証が必要な場合は OAuth 2.1 を使用します。ネットワークで IP 許可リストが必要な場合は、公開されている ChatGPT コネクタの IP アドレス範囲を使用し、許可リストを自動更新します。IP 許可リストは認証や認可の代わりにはなりません。
プラグインの審査とドメインの検証のため、公開エンドポイントには継続してアクセスできる必要があります。公開申請には、Secure MCP Tunnel 単体、一時的なトンネル、ローカルエンドポイントを使用しないでください。
インフラの選択
MCP サーバーは、サーバーレス、コンテナ、エッジ、従来型のアプリケーションインフラにデプロイできます。次の点を考慮してプラットフォームを選択します。
- ランタイムと依存関係のサポート
- ストリーミング応答の動作
- コールドスタートにかかる時間とリクエストのレイテンシ
- 必要なサービスへのネットワークアクセス
- データレジデンシーとコンプライアンスの要件
- シークレット管理
- ログ記録、トレーシング、アラート通知
- ロールバックとバージョン管理のサポート
サーバーでオプションの UI アセットもホストする場合は、コンポーネントのコンテンツセキュリティポリシーで許可された安定したオリジンにアセットをデプロイします。
本番エンドポイントの設定
デプロイ前に次の作業を行います。
- ホストのシークレット管理システムを通じて本番用の認証情報を設定します。
- 認可サーバーを設定し、許可するリダイレクトの動作を定めます。
- 処理コストが高いツールや、操作が外部から見えるツールに、タイムアウトとレート制限を設定します。
- デバッグ用の応答と不要な個人データを削除します。
- ログにアクセストークンや機密情報を含むツール結果が記録されていないことを確認します。
デプロイ後、MCP Inspector で本番エンドポイントを呼び出します。初期化、サーバーの指示、ツール、スキーマ、アノテーション、認証、結果、エラーを検証します。
更新に向けた準備
公開済みのツール名とスキーマは後方互換性を維持します。フィールドやツールを追加する際は、既存の仕様との互換性を保ちます。メタデータを変更した場合は、申請前にカスタム MCP サーバーの接続を更新し、一連の評価を再実行します。
オプションの UI を提供している場合、HTML、JavaScript、CSS の変更によってキャッシュ済みのコンポーネントが動作しなくなる可能性があるときは、リソース識別子にバージョンを付けます。
MCP 拡張機能の追加
OpenAI の MCP 拡張機能を使うと、プラグインを ChatGPT のサイドバー、コンポーザー、ファイルビューアーと連携させることができます。使用例と設定方法については、プラグインの拡張機能を参照してください。
セキュリティ上の注意点
- ツールへの入力はすべて、信頼できないものとして扱います。
- サーバー側でパラメーターを検証し、認可を行います。
- 重大な影響を伴う書き込み操作では、実行前の確認を必須にします。
- ツールのメタデータや結果にシークレットや機密データを含めないでください。
- 障害の調査に必要な情報をログに残し、認証情報や不要な個人データは記録しないでください。
- 処理コストが高い操作や、外部から見える操作にはレート制限を設定します。