mcpField
H
How does it work

How does it work

ローカル
github.com/pomeriumセキュリティ5146 週間前に更新

How does it work について

How does it work は、Pomerium とコンテキストアクセスポリシーを用いて、リモート MCP サーバーとクライアントを保護する方法を示すデモチャットアプリケーションです。内部の MCP サーバーを安全に公開したい開発者を想定しています。OpenAI を利用したチャット画面を備え、認証には OAuth 2.1 フローを使用します。

主な機能

  1. 01Pomerium によるMCP サーバーのプロキシ保護
  2. 02OAuth 2.1 認証と上流トークンの管理
  3. 03ユーザー ID に基づくアクセスポリシー
  4. 04MCP ツール呼び出しの監査ログ設定
  5. 05OpenAI を利用したチャット画面

活用シーン

  1. 01社内データベースの MCP サーバーを外部クライアントに安全に共有
  2. 02社内アプリケーションから保護された MCP サーバーを LLM 経由で呼び出し
  3. 03上流 OAuth を必要とする MCP サーバーへのサインインの統一

MCP 対応

Pomerium の背後にリモート MCP サーバーを配置し、クライアントがそれらに接続して一覧取得やツール利用を行う流れを示します。ツール呼び出しは mcp-* フィールドで記録されます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"app-demo": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"compose"
]
}
}
}
起動コマンドdocker run -i --rm compose

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
ocicompose

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

Pomerium Chat へようこそ。これは、Pomerium で保護されたリモートの Model Context Protocol サーバーを紹介する、シンプルなチャットアプリケーションです。

Discord Docker

https://github.com/user-attachments/assets/50a17106-a471-4f64-be81-982c09d4689e

前提条件

  1. Linux または MacOS のホスト
  2. Docker と Docker Compose
  3. お使いのマシンでは、LetsEncrypt から TLS 証明書を取得し、OpenAI が MCP サーバーのエンドポイントを呼び出せるように、ポート 443 をインターネットに公開する必要があります。
  4. OpenAI API キー

クイックスタート

Pomerium MCP Client Demo Setup

環境変数

ルートディレクトリに .env-mcp-app-demo ファイルを作成し、次の環境変数を追加します。

OPENAI_API_KEY=your_api_key_here
Pomerium の設定

pomerium-config.yaml を更新し、YOUR-DOMAIN をご自身が管理するサブドメインに置き換えます。該当するホスト(または *.YOUR-DOMAIN)の A レコードを DNS に作成します。

デフォルトでは、アクセス ポリシーにより、メールアドレスが YOUR-DOMAIN に属するユーザーにアクセスが制限されます。調整が必要な場合は、ポリシー言語のリファレンスをご覧ください。

Docker Compose

このリポジトリの docker-compose.yaml ファイルをご覧ください。

docker compose up -d

これにはデモ用 SQLite サーバーが含まれており、デモデータベースが必要です。たとえば https://github.com/jpwhite3/northwind-SQLite3 を sqlite.db として保存してください。

テスト

これで https://mcp-app-demo.YOUR-DOMAIN/ にアクセスできるはずです。 サインインページが開きます。サインインすると、アプリケーションにリダイレクトされます。

デモ用データベースサーバー(Database)が利用可能で、「接続済み」の状態になっているはずです。会話で使用するには、これをクリックします。

「年ごとの売上はいくらでしたか?」などの質問をして、OpenAI の LLM 推論が、お使いのコンピューターで実行中の MCP データベースサーバーとどのように連携して回答を取得するかを確認できます。

仕組み

詳しくは、Model Context Protocol の機能概要をご覧ください。

トークンの用語

  • 外部トークン(TE): Pomerium が発行する、ユーザーのセッションを表す外部向けトークンです。このトークンは、外部クライアント(Claude.ai、OpenAI、自作アプリなど)が Pomerium で保護された MCP サーバーへのリクエストを認証するために使用します。 例:LLM API またはエージェント型フレームワークに渡し、MCP サーバーを呼び出せるようにするトークンです。

  • 内部トークン(TI): Pomerium がユーザーに代わってアップストリームの OAuth2 プロバイダー(Notion、Google Drive、GitHub など)から取得する内部認証トークンです。このトークンが外部クライアントに公開されることはありません。Pomerium は、MCP サーバーへのリクエストをプロキシする際に、このトークンを使ってアップストリームサービスへのリクエストを認証します。

Pomerium は、Model Context Protocol(MCP)のクライアントとサーバーの間で安全なゲートウェイとして機能します。OAuth 2.1 フローを使用して、ローカル HTTP MCP サーバーの認証と認可を提供します。この構成は、MCP サーバーから OAuth トークンを必要とするアップストリーム API(Notion、Google Drive、GitHub など)にアクセスする場合に特に役立ちます。

また、このリポジトリで紹介しているように、エージェント型フレームワークや、MCP サーバーを呼び出せる LLM API を利用する内部アプリケーションを構築できます。

この構成を理解するために、Pomerium で保護された MCP サーバーと MCP クライアントがどのように通信するかを見ていきましょう。

1. 内部 MCP サーバーをリモートクライアントに公開する

https://github.com/user-attachments/assets/c7a809ad-6f0a-4ea3-94a2-6cf22ea6d27b

Claude.ai などの外部 MCP クライアントが内部 MCP サーバーにアクセスできるようにしつつ、安全性を保ちたい場合を考えてみましょう。Pomerium をサーバーの前段に配置し、すべての受信リクエストの認証と認可を管理します。

これにより、データベースなどの内部リソースをインターネットに直接公開せずに、外部クライアントと安全に共有できます。

通常どおり Pomerium Route を設定し、追加で mcp プロパティを指定します。これにより、このルートが Model Context Protocol サーバーのルートであることを示します。

routes:
  - from: https://my-mcp-server.your-domain.com
    to: http://my-mcp-server.int:8080/mcp
    name: My MCP Server
    mcp:
      server: {}
sequenceDiagram
  actor U as User
  participant C as MCP Client
  participant P as Pomerium
  participant S as MCP Server
  U ->> C: Adds a server URL
  C ->> P: Registers client, initiates auth
  P ->> C: Sign-in URL
  C ->> U: Redirect to sign-in URL
  U ->> P: Sign-in
  P ->> C: Redirect to client
  C ->> P: Obtain Token
  C ->> P: GET https://mcp-server Authorization: Bearer Token
  P ->> S: Proxy request to MCP Server

2. MCP サーバーがアップストリーム OAuth を必要とする場合

MCP サーバーが GitHub や Google Drive など、OAuth を必要とするアップストリームサービスにアクセスする必要がある場合、Pomerium が OAuth フローを処理できます。プロセスは次のとおりです。

  1. ユーザーがクライアント(例:Claude.ai)に MCP サーバーの URL を追加します。
  2. クライアントが Pomerium に登録し、認証を開始します。
  3. Pomerium がクライアントにサインイン URL を渡し、ユーザーに表示します。
  4. ユーザーが Pomerium にサインインすると、アップストリームの OAuth プロバイダーにリダイレクトされます。
  5. ユーザーがアップストリームプロバイダーで認証します。プロバイダーは Pomerium に 内部トークン(TI) を返します。
  6. Pomerium がサインインを完了し、ユーザーをクライアントにリダイレクトします。
  7. クライアントは Pomerium から 外部トークン(TE) を受け取ります。
  8. クライアントは TE を使用して MCP サーバーにリクエストを送信します。
  9. Pomerium は必要に応じてアップストリームトークン(TI)を更新し、Authorization ヘッダーに TI を含めてリクエストを MCP サーバーにプロキシします。

主な利点:

  • Claude.ai などの外部クライアントにアップストリーム OAuth トークンが見えることはありません。
  • MCP サーバーは常に有効なアップストリームトークンを受け取ります。
  • MCP サーバーはステートレスなまま運用でき、OAuth フローやトークンを管理する必要がありません。

ルート設定:

routes:
  - from: https://github.your-domain
    to: http://github-mcp.int:8080/mcp
    name: GitHub
    mcp:
      server:
        upstream_oauth2:
          client_id: xxxxxxxxxxxx
          client_secret: yyyyyyyyy
          scopes: ['read:user', 'user:email']
          endpoint:
            auth_url: 'https://github.com/login/oauth/authorize'
            token_url: 'https://github.com/login/oauth/access_token'
sequenceDiagram
  actor U as User
  participant C as MCP Client
  participant O as Upstream OAuth
  participant P as Pomerium
  participant S as MCP Server
  U ->> C: Adds a server URL
  C ->> P: Registers client, initiates auth
  P ->> C: Sign-in URL
  C ->> U: Redirect to sign-in URL
  U ->> P: Sign-in
  P ->> U: Redirect to upstream OAuth
  U ->> O: Authenticate with upstream OAuth
  O ->> P: Return Internal Token (TI)
  P ->> C: Redirect to client
  C ->> P: Obtain External Token (TE)
  C ->> P: GET https://mcp-server Authorization: Bearer (TE)
  P ->> O: Refresh (TI) if necessary
  P ->> S: Proxy request to MCP Server, Bearer (TI)
3. アプリから内部 MCP サーバーを呼び出す

OpenAI API や Claude API など、一部の推論 API は MCP サーバーの直接呼び出しに対応しています。この傾向は今後さらに広がると予想され、多くのエージェント型フレームワークが MCP サーバー呼び出しに対応しつつあります。また、LLM の関数呼び出し機能を使って、アプリ内で MCP ツール呼び出しを手動で実装することもできます。いずれの方法でも、Pomerium を経由してリクエストを安全にルーティングするために、MCP サーバー用の Authorization: Bearer 外部トークン(TE) を提供する必要があります。

独自の内部アプリケーションを構築しており、そのようなトークンを取得する必要がある場合、Pomerium ではルートに対して クライアント MCP モード を利用できます。mcp.pass_upstream_access_token オプションを設定すると、Pomerium は現在のユーザーセッションを表す Authorization: Bearer 外部トークン(TE) をアップストリームアプリケーションに渡します。このトークンを外部 LLM やエージェント型フレームワークに渡すことで、認可ポリシーに従って Pomerium 配下の MCP サーバーにアクセスさせることができます。

次のフローは、ユーザーがすでに Pomerium で認証されている場合のプロセスを示しています。

sequenceDiagram
  actor U as User
  participant P as Pomerium
  participant C as Your App Backend
  participant S as MCP Server
  participant I as LLM API
  U ->> P: GET https://mcp-app-demo.your-domain.com
  P ->> C: GET http://mcp-app-demo:3000 Authorization: Bearer (TE)
  C ->> I: call tool https://mcp-server.your-domain Authorization: Bearer (TE)
  I ->> P: GET https://mcp-server.your-domain Authorization: Bearer (TE)
  P ->> S: GET https://mcp-server

ルート設定の例:

routes:
  - from: https://mcp-app-demo.your-domain.com
    to: http://mcp-app-demo:3000
    mcp:
      client: {}
    policy:
      domain:
        is: your-domain.com
  - from: https://mcp-server.your-domain.com
    to: http://mcp-server.int:8080/mcp
    name: My MCP Server
    mcp:
      server: {}
    policy:
      domain:
        is: your-domain.com
4. アプリから利用可能な MCP サーバーを一覧表示する

アプリケーションと同じ Pomerium インスタンスで保護されている MCP サーバーの動的な一覧を、ユーザーに提供できます。そのためには、バックエンドが受け取ったものと同じ Authorization: Bearer トークンを使って、アプリのバックエンドに HTTP リクエストを送信します。レスポンスには、この Pomerium クラスターで利用可能な各上流 MCP サーバーの一覧と接続状態が含まれます。

connected プロパティは、現在のユーザーが上流 OAuth に必要なすべての内部トークンを持っているかどうか(必要な場合)を示します。

  • true – ユーザーは、上流 OAuth プロバイダーから必要なすべての内部トークンを取得済みか、このサーバーではトークンが不要です。
  • false – この MCP サーバーにアクセスする前に、ユーザーは上流 OAuth プロバイダーで認証する必要があります。

後述のセクションでは、ユーザーが必要なすべての内部トークンを確実に取得する方法を説明します。

GET https://mcp-demo-app.yourdomain.com/.pomerium/mcp/routes HTTP/1.1
Accept: application/json
Authorization: Bearer (TE)

Content-Type: application/json
{
    "servers": [
        {
            "name": "DB",
            "url": "https://db-mcp.your-domain.com",
            "connected": true
        },
        {
            "name": "GitHub",
            "url": "https://github-mcp.your-domain.com",
            "connected": false
        }
    ]
}

5. 現在のユーザーが上流 OAuth2 プロバイダーで認証済みであることを確認する

対象の MCP サーバーに connected: false と表示されている場合、ユーザーは必要な上流 OAuth2 プロバイダーで認証する必要があります。 そのためには、ユーザーのブラウザーを MCP サーバーのルート上にある特別な /.pomerium/mcp/connect パスにリダイレクトします(例:https://db-mcp.your-domain.com/.pomerium/mcp/connect)。 アプリケーションのページを指す redirect_url クエリパラメーターを指定します。認証後にユーザーが戻るページであり、MCP サーバーの一覧と接続状態を再読み込みするページです。

注: セキュリティ上、redirect_url のホストは、いずれかの MCP クライアントルートと一致する必要があります。

ユーザーが認証を完了すると、MCP サーバーの connected ステータスは true になります。

6. ユーザー詳細の取得

認証済みユーザーの ID とクレームにアクセスするには、MCP クライアントアプリケーションと MCP サーバーの両方で、X-Pomerium-Assertion HTTP ヘッダーを読み取ります。 このヘッダーにはユーザー情報を含む署名済み JWT が格納されています。これをデコードして検証すると、ユーザーのメールアドレスや名前、その他のクレームなどの詳細を取得できます。

MCP ツール呼び出しの監視と監査

Pomerium は、すべての MCP ツール呼び出しに対して包括的な監査ログを提供し、外部クライアントが MCP サーバーとどのようにやり取りしているかを監視、分析できます。

Pomerium を使った MCP ツール呼び出しとポリシーの監査を実演

監査ログの表示

次のコマンドを使って、MCP ツール呼び出しをリアルタイムで監視できます。

生ログ形式

フィルターされていない監査ログを発生順に表示します。

docker compose logs -f pomerium \
  | grep --line-buffered "authorize" \
  | grep --line-buffered "mcp-"
整形済み JSON 形式

構文強調表示付きで整形された監査ログを表示します。

docker compose logs -f pomerium \
  | grep --line-buffered "authorize" \
  | grep --line-buffered "mcp-" \
  | sed -u 's/^[^{]*//' \
  | jq -C

注:JSON の整形には jq をインストールしておく必要があります。

監査ログエントリーの例

MCP ツール呼び出しの監査ログエントリーの例を示します。

{
  "level": "info",
  "server-name": "all",
  "service": "authorize",
  "request-id": "b9375fd5-e220-9c1d-8f27-9b42249c2de5",
  "user": "google-oauth2|110679203791094235151",
  "email": "[email protected]",
  "mcp-method": "tools/call",
  "mcp-tool": "read_query",
  "mcp-tool-parameters": {
    "query": "SELECT SupplierID, CompanyName, Country FROM Suppliers WHERE Country IN ('Austria', 'Belgium', 'Denmark', 'Finland', 'France', 'Germany', 'Ireland', 'Italy', 'Netherlands', 'Norway', 'Portugal', 'Spain', 'Sweden', 'Switzerland', 'UK');"
  },
  "host": "northwind.mcp.pomerium.com",
  "allow": true,
  "allow-why-true": ["domain-ok"],
  "deny": false,
  "deny-why-false": [],
  "time": "2025-06-27T20:56:44Z",
  "message": "authorize check"
}
MCP 監査ログの設定

包括的な MCP 監査ログを有効にするには、次のグローバルセクションを Pomerium の設定に追加します。mcp-* フィールドは、MCP をサポートするために特別に設計された新しい authorize_log_fields です。

# for MCP observability
authorize_log_fields:
  - request-id
  - user
  - email
  - mcp-method
  - mcp-tool
  - mcp-tool-parameters
  - host

この設定により、MCP 固有の情報がすべて監査ログに含まれ、ツール呼び出しとそのパラメーターを完全に把握できます。

開発

このアプリケーションを開発モードで実行するには、次のコマンドを使います。

npm install
npm run dev

これにより、ホットリロードを有効にした開発サーバーが起動します。

本番環境

このアプリケーションを本番環境向けにビルドして実行するには、次のコマンドを使います。

npm run build
npm run start

機能

  • OpenAI を使用した AI 搭載チャットインターフェース
  • shadcn/ui によるモダンな UI コンポーネント
  • TanStack Router による型安全なルーティング
  • TanStack Query によるデータ取得

テスト

このプロジェクトでは、テストに Vitest を使用しています。次のコマンドでテストを実行できます。

npm run test

スタイリング

このプロジェクトでは、スタイリングにTailwind CSS、事前構築済みのコンポーネントにshadcn/uiを使用しています。UI コンポーネントは Radix UI プリミティブの上に構築されています。

新しいコンポーネントの追加

次のコマンドで shadcn コンポーネントを追加します。

npx shadcn@latest add [component-name]

リントとフォーマット

このプロジェクトでは、リントとフォーマットにeslintとprettierを使用しています。Eslint はtanstack/eslint-configを使って設定されています。次のスクリプトを利用できます。

npm run lint
npm run format
npm run check

Shadcn

Shadcnの最新バージョンを使ってコンポーネントを追加します。

pnpx shadcn@latest add button

ルーティング

このプロジェクトではTanStack Routerを使用しています。初期設定ではファイルベースのルーターを採用しています。つまり、ルートはsrc/routes内のファイルとして管理されます。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。