0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zoom MCP Serverの仕様を読み解く — ミニマムInspectorを自作して中身を覗いてみた

0
Posted at

6d989dd3-f103-4908-aa6e-6776587595f0.png

はじめに

Model Context Protocol(MCP) は、AIエージェントが外部ツールやデータソースとやり取りするための標準プロトコルとして急速に普及しています。

Zoomも自社のミーティングデータにアクセスするための MCP Server の公開を開始しました。AIクライアントからコネクタとして接続すると、ミーティングの検索やサマリー取得、録画のトランスクリプト取得などが可能になります。

本記事ではまず ChatGPTからZoom MCP Serverに実際に接続 し、何ができるのかを体験します。その上で、裏側で何が起きているのかを理解するために ミニマムなInspectorツールを自作 し、MCP仕様を深掘りしていきます。

対象読者

  • MCPの仕様を具体的な実装を通じて理解したい開発者
  • Zoom MCP Serverに独自クライアントから接続してみたい方
  • OAuth 2.1 + PKCE の実装パターンに興味がある方

Zoom MCP Serverの概要

Zoom MCP Serverの詳細は公式ドキュメントに記載されています。

接続先は2種類のトランスポートが用意されています。

トランスポート URL
Streamable HTTP https://mcp.zoom.us/mcp/zoom/streamable
SSE(レガシー) https://mcp.zoom.us/mcp/zoom/sse

提供されるツールと必要なScope、Rate Limitは以下の通りです。

ツール 説明 必要なScope Rate Limit
search_meetings キーワード・日付範囲でミーティングを検索 meeting:read:search
get_meeting_assets サマリー、ノート、録画等のアセット取得 meeting:read:assets, meeting:read:assets:admin LIGHT
get_recording_resource トランスクリプト・サマリー・再生URL取得 cloud_recording:read:content MEDIUM
recordings_list ユーザーの録画一覧 cloud_recording:read:list_user_recordings
create_new_file_with_markdown Markdownから新規Zoom Docsを作成 docs:write:import HEAVY

Screenshot 2026-04-28 at 17.46.14.png

各ツールの詳細なパラメータやレスポンス形式、ScopeやRate Limitは公式ドキュメントを参照してください。


ChatGPTからZoom MCP Serverに接続してみる

まずは実際のAIクライアントからZoom MCP Serverに接続し、何ができるのかを体験してみます。ChatGPTは2025年後半からMCPクライアント機能をサポートしており、カスタムMCPアプリとしてZoom MCP Serverを追加できます。

ちなみにですが、ZoomはChatGPTやClaude向けの公式アプリを公開しています。たとえばChatGPTであれば、アプリからZoomを検索し接続するだけで利用できます。以下の記事は、独自にMCPサーバを追加する手段としての説明になりますので、通常のご利用の場合は公式Zoomアプリをご利用ください。
Screenshot 2026-04-28 at 17.49.09.png

前提: Zoom Appの作成

Zoom App Marketplace(https://marketplace.zoom.us/)で General App を作成し、Client IDとClient Secretを取得しておきます。

Scopesの設定: Zoom App MarketplaceのApp設定画面で、必要なScopeを追加します。上のツール一覧を参考に、利用したいツールに対応するScopeを追加してください。

Screenshot 2026-04-28 at 16.53.58.png

1. 開発者モードの有効化

ChatGPTの 設定アプリ高度な設定 から 開発者モード を有効にします。

Screenshot 2026-04-27 at 14.14.57.png

これにより、未検証のカスタムMCPコネクタを追加できるようになります。

開発者モードでは、OpenAIによるレビューを受けていないアプリを追加できるようになります。信頼できるMCPサーバーのみを接続してください。

2. アプリの作成

アプリを作成する をクリックし、以下の情報を入力します。

項目
名前 任意
MCPサーバーのURL https://mcp.zoom.us/mcp/zoom/streamable
認証 OAuth

Screenshot 2026-04-27 at 14.18.23.png

OAuth関連の高度な設定 が右側に展開されます。

クライアントの登録: 「ユーザー定義のOAuthクライアント」を選択し、Zoom App MarketplaceのGeneral Appで作成したClient IDとClient Secretを入力します。

コールバックURL: ChatGPT側が生成するコールバックURLが表示されます。このURLを Zoom AppのOAuth Redirect URL にそのまま設定してください。

トークンエンドポイントの認証方法: client_secret_basic を選択します。

スコープ: 前述のツール一覧に対応するScopeにチェックを入れます。

ChatGPT側の「コールバックURL」とZoom App側の「OAuth Redirect URL」が一致していないとOAuthフローが失敗します。ChatGPTのアプリ作成画面に表示されるURLをコピーして、Zoom App Marketplaceの設定に貼り付けてください。

設定が完了したら「理解したうえで、続行します」にチェックを入れ、作成する をクリックします。

3. OAuth認可

アプリの作成後、初回利用時にZoomのOAuth同意画面が表示されます。

Screenshot 2026-04-27 at 14.28.13.png

Allow をクリックして認可を完了します。

4. アプリの有効化を確認

認可が完了すると、ChatGPTの 設定アプリ に作成したアプリが「有効化されたアプリ」として表示されます。

Screenshot 2026-04-27 at 14.28.46.png

5. チャットで使ってみる

開発者モードのチャットを開き、作成したアプリを指定して質問します。

Screenshot 2026-04-27 at 14.30.06.png

例えば「過去3ヶ月に実施された会議についての情報をまとめて」と質問すると、ChatGPTがZoom MCP Server経由で search_meetings ツールを呼び出し、実際のミーティングデータを取得して回答を生成します。

Screenshot 2026-04-28 at 16.59.26.png

「ツールが呼び出されました」という表示とともに、取得されたミーティング一覧がリッチなUIで表示され、全体サマリまで生成してくれます。

では、この裏側で何が起きているのでしょうか?ここからはMCPの通信仕様を、自作Inspectorで可視化していきます。


Inspectorを自作して仕様を可視化する

ChatGPTがZoom MCP Serverと通信している裏側では、OAuth認証、JSON-RPCハンドシェイク、ツール呼び出しという3段階の通信が行われています。これを可視化するために、ミニマムなInspectorツールをNext.jsで自作しました。

GitHubリポジトリ:

全体アーキテクチャ

Next.jsのAPI Routesが「MCPクライアント兼OAuthリレー」として機能し、ブラウザからは直接MCP Serverにアクセスしません。この構成にしている理由は以下の通りです。

  • ブラウザからの直接アクセスではCORSで弾かれる
  • OAuth 2.1のトークン交換でClient Secretを使うため、サーバーサイドに置く必要がある
  • MCP Session IDの管理をCookie(iron-session)で行う

プロジェクト構成

zoom-mcp-inspector/
├── src/
│   ├── lib/
│   │   ├── session.ts          # iron-session 暗号化Cookie
│   │   ├── oauth.ts            # PKCE生成、メタデータディスカバリ、トークン交換
│   │   └── mcp-client.ts       # JSON-RPC 2.0 クライアント
│   └── app/
│       ├── api/
│       │   ├── discovery/route.ts   # Well-knownメタデータ取得
│       │   ├── auth/route.ts        # OAuth認可開始
│       │   ├── callback/route.ts    # OAuth コールバック
│       │   └── mcp/
│       │       ├── initialize/route.ts  # MCP初期化
│       │       ├── tools/route.ts       # ツール一覧取得
│       │       └── call/route.ts        # ツール実行
│       ├── page.tsx                # ランディングページ
│       └── inspector/page.tsx      # Inspector UI
├── .env.example
└── package.json

第1段階: OAuth 2.1 + PKCE 認証

MCP仕様における認証の位置づけ

MCP仕様(2025-03-26)では、Streamable HTTP上のMCPサーバーに対する認証として OAuth 2.1 が推奨されています。認可コードフローに加え、PKCE(Proof Key for Code Exchange) が必須とされています。

ディスカバリの流れ

MCP仕様では、未知のMCPサーバーに接続する汎用クライアントを想定して、以下のディスカバリ手順が定義されています。

Step 1の「POSTして401を受け取る」ステップは、接続先のMCPサーバーが認証を要求するかどうか不明な場合の検知手段です。Zoom MCP Serverのように認証が必要なことがわかっている場合は、Step 2のディスカバリから開始するか、Zoomの既知のOAuthエンドポイントに直接アクセスしても問題ありません。本Inspectorでは「MCP仕様上の全フローを体験する」目的で3段階すべてを実装しています。

PKCE の実装

PKCEはOAuth 2.1で必須となったセキュリティ機構で、認可コードの横取り攻撃を防ぎます。

  1. code_verifier: crypto.randomBytes(32) → base64url エンコード(43〜128文字)
  2. code_challenge: code_verifier を SHA-256 ハッシュ → base64url エンコード
  3. 認可リクエストに code_challenge + code_challenge_method=S256 を付与
  4. トークン交換時に元の code_verifier を送信
// oauth.ts — PKCE生成

export function generateCodeVerifier(): string {
  return crypto.randomBytes(32)
    .toString("base64url");
}

export async function generateCodeChallenge(verifier: string): Promise<string> {
  const hash = crypto.createHash("sha256")
    .update(verifier)
    .digest();
  return hash.toString("base64url");
}

メタデータディスカバリの実装

Inspectorでは、well-knownが取得できなかった場合のフォールバックとして、Zoomの既知エンドポイントに直接アクセスする3段階戦略をとっています。

// oauth.ts — メタデータディスカバリ(3段階フォールバック)

export async function discoverAuthMetadata(mcpServerUrl: string) {
  const base = new URL(mcpServerUrl);

  // Step 1: oauth-protected-resource
  try {
    const prUrl = `${base.origin}/.well-known/oauth-protected-resource`;
    const prRes = await fetch(prUrl);
    if (prRes.ok) {
      const prData = await prRes.json();
      // authorization_servers[0] を使って次のディスカバリへ
    }
  } catch {}

  // Step 2: oauth-authorization-server
  try {
    const asUrl = `${base.origin}/.well-known/oauth-authorization-server`;
    const asRes = await fetch(asUrl);
    if (asRes.ok) {
      const asData = await asRes.json();
      return {
        authorizationEndpoint: asData.authorization_endpoint,
        tokenEndpoint: asData.token_endpoint,
      };
    }
  } catch {}

  // Step 3: Zoomの既知エンドポイントにフォールバック
  return {
    authorizationEndpoint: "https://zoom.us/oauth/authorize",
    tokenEndpoint: "https://zoom.us/oauth/token",
  };
}

セッション管理

OAuthフローは複数のHTTPリクエストにまたがるため、状態を保持する仕組みが必要です。ここでは iron-session を使い、暗号化されたCookieに以下を保存しています。

// session.ts

export interface SessionData {
  oauthState?: string;       // CSRF対策用ランダム文字列
  codeVerifier?: string;     // PKCEのverifier(トークン交換で使用)
  accessToken?: string;      // 認証完了後のアクセストークン
  refreshToken?: string;     // リフレッシュトークン
  mcpSessionId?: string;     // MCP初期化後のセッションID
  authMetadata?: {
    authorizationEndpoint: string;
    tokenEndpoint: string;
  };
}

第2段階: MCP Streamable HTTP

Streamable HTTPとは

MCP仕様 2025-03-26 で導入された Streamable HTTP は、従来のSSEトランスポートを置き換える新しい通信方式です。主な特徴は以下の通りです。

  • 単一エンドポイント: 1つのHTTPパスが GETPOSTの両方を受け付ける
  • セッション管理: Mcp-Session-Id ヘッダーでステートフルなセッションを維持
  • レスポンス形式の柔軟性: Content-Type に応じてJSONレスポンスまたはSSEストリームを返す

JSON-RPC 2.0 メッセージフォーマット

MCPの全通信は JSON-RPC 2.0 に準拠しています。リクエストの基本構造は以下の通りです。

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-26",
    "capabilities": {},
    "clientInfo": {
      "name": "zoom-mcp-inspector",
      "version": "1.0.0"
    }
  }
}

MCP初期化ハンドシェイク

MCPセッションの確立には2段階のハンドシェイクが必要です。

Step 1: initialize リクエスト — クライアントがプロトコルバージョンと自身の能力を宣言します。サーバーはサポートするプロトコルバージョン・能力・サーバー情報を返します。レスポンスの Mcp-Session-Id ヘッダーを以降のリクエストで使い続けます。

Step 2: notifications/initialized 通知 — クライアントが初期化完了を通知します。これは id フィールドを持たないJSON-RPC通知で、サーバーは 202 Accepted を返します。

notifications/initializedid を持たない点に注意してください。JSON-RPC 2.0仕様では、id がないメッセージは「通知(notification)」であり、レスポンスを期待しません。id を含めて送信するとプロトコル違反となる可能性があります。

// mcp-client.ts — 初期化フロー

export async function mcpInitialize(
  serverUrl: string,
  accessToken: string,
  existingSessionId?: string
) {
  // Step 1: initialize
  const initRes = await fetch(serverUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json, text/event-stream",
      Authorization: `Bearer ${accessToken}`,
      ...(existingSessionId && { "Mcp-Session-Id": existingSessionId }),
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "initialize",
      params: {
        protocolVersion: "2025-03-26",
        capabilities: {},
        clientInfo: { name: "zoom-mcp-inspector", version: "1.0.0" },
      },
    }),
  });

  const sessionId = initRes.headers.get("mcp-session-id");
  const result = await initRes.json();

  // Step 2: notifications/initialized(id なし = 通知)
  await fetch(serverUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
      ...(sessionId && { "Mcp-Session-Id": sessionId }),
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      method: "notifications/initialized",
      // id は含めない
    }),
  });

  return { result, sessionId };
}

第3段階: ツール呼び出し

tools/list — 利用可能なツールの取得

MCPサーバーが提供するツールの一覧を取得します。

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list"
}

レスポンスには各ツールの名前、説明、入力スキーマ(JSON Schema)が含まれます。ChatGPTの「ツールが呼び出されました」の裏側では、まさにこの tools/list でスキーマを取得し、ユーザーの質問に合ったツールと引数をLLMが選定しています。

tools/call — ツールの実行

取得したツール名と引数を指定してツールを呼び出します。

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "search_meetings",
    "arguments": {
      "q": "sprint planning",
      "from": "2026-04-01T00:00:00Z",
      "to": "2026-04-28T00:00:00Z"
    }
  }
}

実際に取れるデータ

search_meetings のレスポンス(抜粋)

{
  "meetings": [
    {
      "meeting_uuid": "57ABBB84-8847-430D-A963-F9164FC5B32A",
      "topic": "Sprint Planning",
      "meeting_start_time": "2026-04-27T03:44:33Z",
      "host_name": "Michitaka Sugi",
      "attendees": [
        { "user_name": "Michitaka Sugi" },
        { "user_name": "Guest" }
      ],
      "has_summary": true,
      "has_transcript": true,
      "has_recording": true
    }
  ]
}

取得した meeting_uuid を使って get_recording_resource を呼ぶと、AI生成サマリーとタイムスタンプ付きのトランスクリプトが得られます。

get_recording_resource のレスポンス(抜粋)

{
  "summaries": [
    {
      "overall_summary": "Michitaka led a debate on the paradox of modern cognitive optimization...",
      "recording_start": 1777261515000
    }
  ],
  "transcripts": [
    {
      "timeline": [
        {
          "display_name": "Michitaka Sugi",
          "text": "Welcome to the debate.",
          "ts": "00:00:12.420",
          "end_ts": "00:00:14.319"
        }
      ]
    }
  ],
  "play_urls": [
    {
      "urls": ["https://ssrweb.zoom.us/replay02/..."]
    }
  ]
}

トランスクリプトでは 誰が(display_name)・いつ(ts/end_ts)・何を言ったか(text がタイムスタンプ付きで構造化されています。これにより、「誰がどのトピックについて発言したか」をプログラム的に分析できます。


Inspectorのセットアップ

前提条件

  • Node.js
  • ngrok(OAuthリダイレクト用)
  • Zoom開発者アカウント(前述のGeneral Appを流用可能)

同じアプリを流用する場合、Zoom Marketplaceのアプリ画面にて、Basic Information > OAuth Information > OAuth Redirect URL をngrokのURLに書き換え、忘れずに再度アプリ追加を行ってください。
Screenshot 2026-04-28 at 18.12.31.png
Screenshot 2026-04-28 at 18.13.27.png

1. セットアップ

git clone https://github.com/YOUR_USER/zoom-mcp-inspector.git
cd zoom-mcp-inspector
npm install
cp .env.example .env.local

.env.local を編集します。

ZOOM_CLIENT_ID=your_client_id
ZOOM_CLIENT_SECRET=your_client_secret
ZOOM_MCP_SERVER_URL=https://mcp.zoom.us/mcp/zoom/streamable
PUBLIC_URL=https://your-ngrok-url.ngrok-free.app
SESSION_SECRET=ランダムな32文字以上の文字列

2. ngrokの起動

ngrok http 3000

表示されたURLを .env.localPUBLIC_URL と、Zoom AppのOAuth Redirect URLの両方に設定してください。

3. 起動

npm run dev

4. 使い方

  1. http://localhost:3000 にアクセス
  2. Run Discovery → OAuthメタデータの取得を確認
  3. Login with Zoom → Zoomの認可画面でアプリを承認
  4. 認可後、Inspector画面にリダイレクト
  5. Initialize → MCPセッションの確立
  6. List Tools → 利用可能なツール一覧の取得
  7. ツールを選択 → 引数をJSON入力 → Call Tool → レスポンスを確認

アクセストークンの有効期限は約1時間です。期限切れの場合は再度ログインしてください。本ツールは開発・検証目的であり、本番環境での使用は想定していません。


まとめ

本記事ではまずChatGPTからZoom MCP Serverに接続して体験した上で、自作Inspectorで裏側の通信を可視化しました。

  • OAuth 2.1 + PKCE: Well-knownメタデータディスカバリ → 認可コード取得 → PKCE付きトークン交換
  • MCP Streamable HTTP: JSON-RPC 2.0 による initializenotifications/initialized の2段階ハンドシェイク、Mcp-Session-Id によるセッション管理
  • ツール呼び出し: tools/list でスキーマ取得 → tools/call で実行 → 構造化されたレスポンスの取得

ChatGPTから「過去の会議をまとめて」と聞いたときに裏側で動いているのは、まさにこのJSON-RPCの通信です。MCPは、AIエージェントが外部データソースと対話するための「標準化されたインターフェース」を提供しています。Zoomのミーティングデータという具体的なデータソースを通じて、その仕組みを体感していただければ幸いです。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?