トンネルのイラスト

OpenAI の Secure MCP Tunnel を試してみた

OpenAI の Secure MCP Tunnel を利用すると、プライベートな MCP サーバーをパブリックなインターネットに公開することなく OpenAI のプロダクトに接続できるようになります。この記事では Secure MCP Tunnel を試してみた様子を紹介します。

MCP(Model Context Protocol)にはローカルで実行される stdio トランスポートと、リモートで実行される Streamable HTTP トランスポートの 2 種類があります。MCP の登場当初は stdio トランスポートが主に使用されていましたが、以下のような理由から Streamable HTTP トランスポートの利用が増えてきています。

  • セットアップ手順が手軽でエンジニア以外も利用しやすい
  • クラウド上で動くクライアントからでも利用できる
  • OAuth 認証の仕組みが整えられている

一方で Streamable HTTP トランスポートの場合は stdio トランスポートと比較して、HTTP リクエストを受け付けるためのサーバーを立てる必要があり、プライベートに配布する手段が限られているといった課題がありました。

OpenAI の提供する Secure MCP Tunnel は、プライベートな MCP サーバーをパブリックなインターネットに公開することなく OpenAI のプロダクトに接続するためのトンネルサービスです。これにより MCP サーバーがオンプレミスやファイアウォールの内側にあっても、ChatGPT や Codex などから呼び出すことが可能になります。

この記事では OpenAI の Secure MCP Tunnel を試してみた様子を紹介します。

Secure MCP Tunnel の事前準備

事前準備として OpenAI プラットフォームの Tunnel 設定 から tunnel_id を取得します。もともと付与されている Owner ロールではトンネルの作成ができないため、新しいロールを作成する必要がありました。People & Permissions > Roles の「Create role」から新しいロールを作成し「Permissions」から「Tunnels」の「Read」「Manage」を有効にします。

作成したロールは Members タブ からユーザーに割り当てることができます。ロールを割り当てたユーザーでログインした状態で Tunnel 設定 にアクセスすると「Create Tunnel」ボタンが有効になります。

Tunnel を作成するダイアログが表示されるので「Name」と「Description」を入力して「Create」をクリックします。Organization Ids はあらかじめ入力されているものをそのまま利用すれば問題ありません。

Tunnel を作成したら ID が発行されるので、これを控えておきます。

続いて API キーを作成します。API キーは API Keys から「Create new secret key」をクリックして作成できます。API キーの権限には、「Tunnels」の「Read」「Use」が必要になります。

Tunnel クライアントのセットアップ

Secure MCP Tunnel は tunnel-client という CLI ツールを使用して、手元で動いている MCP サーバーと OpenAI のプロダクトを接続します。tunnel-client は以下の URL からインストールできます。必要に応じて最新のバージョンを確認してください。

ダウンロードが完了したら、tunnel-client コマンドが実行できるようにパスを通します。ターミナルで以下のコマンドを実行して、tunnel-client が正しくインストールされていることを確認しましょう。

tunnel-client --version

次に説明する tunnel-client init では、トンネルが転送する先のローカル MCP サーバーを指定します。そのため、事前に MCP サーバーを 1 つ用意しておきましょう。ここでは簡単な計算をするだけの stdio トランスポートの MCP サーバーを TypeScript で実装しました。

stdio トランスポートで動く MCP サーバーの例
server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
 
const server = new McpServer({
  name: "calculator",
  version: "1.0.0",
});
 
server.registerTool(
  "add",
  {
    description: "2つの数値を足し算します",
    inputSchema: {
      a: z.number().describe("1つ目の数値"),
      b: z.number().describe("2つ目の数値"),
    },
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: `${a} + ${b} = ${a + b}` }],
  }),
);
 
// ... 他のツール
 
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Calculator MCP サーバーが起動しました");
}
 
main().catch((err) => {
  console.error("サーバーエラー:", err);
  process.exit(1);
});
 

環境変数 CONTROL_PLANE_API_KEY に先ほど作成した API キーを設定し、tunnel-client init を実行します。--tunnel-id には先ほど控えた Tunnel の ID を指定します。ローカルの MCP サーバーを起動するコマンドは --mcp-command オプションで指定します。もしリモートの MCP サーバーを指定したい場合は --mcp-server-url http://example.com/mcp のように指定します。

export CONTROL_PLANE_API_KEY="sk-xxxxxx" # 先ほど作成した API キーを設定
 
tunnel-client init \
  --sample sample_mcp_stdio_local \
  --profile local-stdio \
  --tunnel-id tunnel_xxxx \
  --mcp-command "bun /path/to/server.ts"

tunnel-client init コマンドを実行すると、~/tunnel-client/local-stdio.yaml というプロファイルファイルが作成されます。今後はこのプロファイルを指定して tunnel-client コマンドを実行することで、毎回コマンドライン引数を指定する必要がなくなります。

~/tunnel-client/local-stdio.yaml
config_version: 1
control_plane:
  base_url: "https://api.openai.com"
 
  tunnel_id: "tunnel_xxx"
  api_key: "env:CONTROL_PLANE_API_KEY"
health:
  # Keep a fixed port when you want a stable local admin URL.
  # For concurrent or clean-room runs, switch listen_addr to "127.0.0.1:0" and
  # set url_file so another process can discover the resolved /healthz, /readyz,
  # /metrics, and /ui base URL.
  listen_addr: "127.0.0.1:8080"
  # url_file: "/tmp/tunnel-client-health.url"
admin_ui:
  open_browser: false
log:
  level: info
  format: json
mcp:
  commands:
    - channel: main
      command: "bun /path/to/server.ts"

Tunnel クライアントの起動

プロファイルの設定が完了したら、tunnel-client run コマンドを実行してトンネルを起動します。先ほど作成したプロファイルを引数に指定して実行します。

tunnel-client run --profile local-stdio

コマンドのログに「🟢 tunnel-client started」と表示されていれば OK です。http://localhost:8080 にアクセスすると、管理画面が表示されます。

ChatGPT から MCP サーバーを呼び出す

ChatGPT のコネクタの設定を開き、カスタムコネクタを追加します。カスタムコネクタを追加するためには「高度な設定」から「開発者モード」を有効にする必要があります。

「開発者モード」を有効にするとコネクタの設定画面に「アプリを作成する」というボタンが表示されるので、これをクリックして新しいコネクタを作成します。

「接続」の項目では「トンネル」を選択します。「利用可能なトンネル」がドロップダウンで表示されます。作成したトンネルが読み込まれないこともあるので、その際には「代わりにトンネル ID を入力」をクリックして、先ほど控えた Tunnel の ID を直接入力してみてください。「認証」は「認証なし」を選択します。

「作成する」ボタンをクリックすると新しいアプリが追加されるので、これを ChatGPT に接続しましょう。アプリの情報を確認すると、ローカルで動いている MCP サーバーのツールの一覧が表示されていることがわかりますね。

実際にチャットからツールを呼び出せるか試してみましょう。チャットの開始前に「+」ボタンをクリックして、先ほど作成したアプリを選択します。これでチャットから MCP サーバーのツールを呼び出すことができるようになります。

以下のようなプロンプトを入力してみてください。

計算ツールを使って 5 + 3 を計算してみて。

作成した「tunnel-test」アプリの add ツールを呼び出して、正しく計算結果が返ってきていることがわかりますね。

今回の例ではローカルの MCP サーバーをトンネルに接続しましたが、tunnel-client はプライベートの MCP サーバーにアクセス可能な同じ信頼境界内で実行される必要があります。本番環境で利用する際には、Kubernetes クラスター内の Pod として tunnel-client を実行する、あるいは VM 内で tunnel-client を実行して同じネットワーク内の MCP サーバーに接続する、といった方法が考えられます。

まとめ

  • OpenAI の Secure MCP Tunnel を利用すると、プライベートな MCP サーバーをパブリックなインターネットに公開することなく OpenAI のプロダクトに接続できる
  • Secure MCP Tunnel を利用するためには、OpenAI プラットフォームでトンネルを作成し、API キーを発行し、tunnel-client CLI をセットアップする必要がある。トンネルを作成するためには、ユーザーに適切なロールを割り当てる必要がある点に注意
  • tunnel-client CLI を使用して、ローカルで動いている MCP サーバーと OpenAI のプロダクトを接続する
  • ChatGPT のカスタムコネクタを利用して、ChatGPT から MCP サーバーのツールを呼び出すことができる

参考

記事の理解度チェック

以下の問題に答えて、記事の理解を深めましょう。

記事で説明されている OpenAI の Secure MCP Tunnel の主な役割として正しいものはどれですか?

  • MCP サーバーを Web サーバーとしてパブリックに公開するための CDN サービス

    もう一度考えてみましょう

    Secure MCP Tunnel はパブリックに公開するためのものではなく、むしろパブリックに公開せずに接続する仕組みです。

  • stdio トランスポートで動く MCP サーバーをローカルから ChatGPT に登録するためのインストーラ

    もう一度考えてみましょう

    tunnel-client はインストーラではなく、ローカルで動かして OpenAI のプロダクトとプライベート MCP サーバーを中継するクライアントです。

  • プライベートな MCP サーバーをパブリックなインターネットに公開することなく OpenAI のプロダクトに接続するためのトンネルサービス

    正解!

    記事の冒頭・まとめで明記されている通り、これが Secure MCP Tunnel の役割です。オンプレミスやファイアウォール内の MCP サーバーを ChatGPT や Codex から呼び出せるようにします。

  • MCP プロトコル自体を高速化するための新しいトランスポート仕様

    もう一度考えてみましょう

    記事では新しいトランスポートではなく、既存の MCP サーバーを安全に OpenAI のプロダクトに接続するための仕組みとして説明されています。

記事によると、OpenAI プラットフォームでトンネルを作成するためのロールには、Permissions の Tunnels でどの権限を有効にする必要がありますか?

  • Read と Use

    もう一度考えてみましょう

    Read と Use は API キーに必要な権限として記事で説明されているもので、ロールの権限ではありません。

  • Manage と Use

    正解!

    記事で明記されている通り、Owner ロールではトンネル作成ができないため、新しいロールを作って Tunnels の Manage と Use を有効にする必要があります。

  • Owner ロールにあらかじめ含まれているため設定は不要

    もう一度考えてみましょう

    記事では「もともと付与されている Owner ロールではトンネルの作成ができない」と明記されています。

  • Admin と Write

    もう一度考えてみましょう

    記事中にそのような権限名は登場しません。必要なのは Manage と Use です。

記事の手順に従って tunnel-client init を実行した場合、生成されるプロファイルファイルのパスはどれですか?

  • /etc/tunnel-client/config.yaml

    もう一度考えてみましょう

    記事ではシステム配下ではなくユーザーのホーム配下に生成されると説明されています。

  • ~/.openai/tunnels.yaml

    もう一度考えてみましょう

    記事中にこのパスは登場しません。OpenAI 関連の設定ですが、tunnel-client は専用ディレクトリを使います。

  • ~/tunnel-client/local-stdio.yaml

    正解!

    記事で示されている通り、`--profile local-stdio` を指定して init を実行すると `~/tunnel-client/local-stdio.yaml` というプロファイルが作成されます。

  • カレントディレクトリの tunnel.yaml

    もう一度考えてみましょう

    記事ではカレントディレクトリではなく、ホーム配下の `~/tunnel-client/` 以下に生成されると説明されています。