Skip to main content
Glama
beaconfire-projects

mcp-oauth-test

FastMCP OIDC Server

これは FastMCP を使用して作成された、OIDC ログインで保護された MCP server です。FastMCP の OIDCProxy を使用しており、MCP クライアントはサーバーが公開する OAuth メタデータを通じて認証を完了し、実際のログインと token exchange は QA OIDC provider に転送されます。

現在は QA MGT OpenAPI に接続されており、trainee、注文、商品、顧客、キャンパス採用、国際採用に関連する MCP tools が生成されています。

OIDC discovery アドレスはデフォルトで次のように設定されています:

https://auth-qa.drillinsight.com/.well-known/openid-configuration

認証の準備

まず auth-qa.drillinsight.com で OAuth アプリを登録し、次のコールバックアドレスをホワイトリストに追加してください:

http://localhost:8000/auth/callback

他のアドレスにデプロイする場合は、http://localhost:8000 を BASE_URL の値に置き換えてください。コールバックアドレスは FastMCP の BASE_URL と完全に一致している必要があります。

ローカルでの実行

cp .env.example .env
# 编辑 .env,至少填写 OIDC_CLIENT_ID 和 OIDC_CLIENT_SECRET
uv sync
uv run mcp-oidc-server

モジュールを直接実行することもできます:

uv run python -m oidc_mcp_server.server

サービスはデフォルトで http://127.0.0.1:8000 をリッスンします。MCP クライアントが他のマシンやコンテナで実行されている場合は、クライアントからアクセス可能な BASE_URL と適切な HOST(例:0.0.0.0)を設定してください。

Claude Code プラグイン

リポジトリにはプライベートな Claude Code marketplace と MCP プラグインが含まれています:

.claude-plugin/marketplace.json
└── plugins/mcp-oauth-test/
    ├── .claude-plugin/plugin.json
    ├── .mcp.json
    └── README.md

プラグインは Claude Code をすでにデプロイ済みのリモート MCP サービスに接続するだけのもので、ローカルで Python サービスを起動することはありません。開発テスト時には直接ロードできます:

claude --plugin-dir ./plugins/mcp-oauth-test

プラグインは開発環境の MCP Server に固定接続されています:

https://api-mcp-oauth-dev.beaconfireinc.com/mcp

プライベート marketplace からインストールすることもできます:

/plugin marketplace add /path/to/mcp-oauth-test
/plugin install mcp-oauth-test@authsome-internal

現在の marketplace のルートディレクトリはリポジトリのルートです。この marketplace は会社のプライベート GitHub リポジトリに保持し、公開 marketplace にはコミットしないでください。共有環境では HTTPS アドレスを使用し、会社の IdP と MCP Server 側で会社ユーザーのみにアクセスを制限してください。

OIDCProxy を使用する理由

上流の auth-qa.drillinsight.com は DCR や CIMD をサポートする必要はありません。OIDCProxy はまさにこのようなシナリオのためにあります:

ChatGPT ── MCP OAuth / CIMD ──> FastMCP OIDCProxy
                                      │
                                      └── 固定 client_id/client_secret ──> auth-qa.drillinsight.com

上流に事前登録が必要なのは FastMCP という OAuth アプリのみで、${BASE_URL}/auth/callback を設定します。ChatGPT が使用する CIMD は FastMCP プロキシ層で処理され、上流の OAuth server には転送されません。

ChatGPT CIMD 設定

ChatGPT でカスタム MCP を作成する際、OAuth 詳細設定の「クライアント登録」では次を選択してください:

客户端标识元数据文档(CIMD)

現在の ChatGPT コネクタが生成する情報は次のとおりです:

CIMD Client ID / 客户端元数据 URL:
https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json

ChatGPT Callback URL:
https://chatgpt.com/connector/oauth/0Buhw3sHVv1-

CIMD URL 自体が、ChatGPT が FastMCP OAuth プロキシにアクセスする際に使用する client_id です。これは上流の auth-qa.drillinsight.com に登録する必要はなく、登録すべきでもありません。

このプロジェクトには 2 層の異なる OAuth Client ID が存在します:

OAuth リンク

client_id

設定場所

ChatGPT → FastMCP OIDCProxy

https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json

ChatGPT が自動提供、CIMD 選択後は手動入力不要

FastMCP OIDCProxy → auth-qa.drillinsight.com

app_74a4b555-5b87-4212-9dda-d584fa78caf8

MCP Server の OIDC_CLIENT_ID

対応するデータフローは次のとおりです:

ChatGPT
  │ client_id=https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json
  ▼
FastMCP OIDCProxy
  │ client_id=app_74a4b555-5b87-4212-9dda-d584fa78caf8
  ▼
auth-qa.drillinsight.com

MCP Server の環境変数設定:

OIDC_CLIENT_ID=app_74a4b555-5b87-4212-9dda-d584fa78caf8
OIDC_CLIENT_SECRET=<上游 OAuth Server 颁发的客户端密钥>

上流の OAuth Server は、その app_... アプリに対して FastMCP のコールバックアドレスのみを設定すればよいです:

https://heroic-verbally-crawdad.ngrok-free.app/auth/callback

ChatGPT のコールバックアドレス https://chatgpt.com/connector/oauth/... を上流の OAuth Server に設定しないでください。このアドレスは FastMCP プロキシ層が認証完了後に使用します。

認証開始時、正常なログにはまず ChatGPT の CIMD Client ID が表示されるはずです:

CIMD document fetched and validated
GET /authorize?client_id=https://chatgpt.com/oauth/.../client.json ... 302

その後、FastMCP が app_74a4b555-... を使用して上流の OAuth Server にリダイレクトします。

MCP クライアント設定

MCP アドレスを次のように設定します:

http://localhost:8000/mcp

FastMCP は次の認証 discovery アドレスを提供します:

http://localhost:8000/.well-known/oauth-authorization-server
http://localhost:8000/.well-known/oauth-protected-resource/mcp

クライアントはこれらの MCP/OAuth discovery endpoint を自動的に読み取る必要があります。ログイン成功後、2 つの保護されたツールを呼び出すことができます:

  • ping:ヘルスチェック。

  • who_am_i:FastMCP が現在の認証 token から抽出した client_id、scope、claims を返します。

設定項目

環境変数

必須

デフォルト値

説明

OIDC_CLIENT_ID

はい

-

上流 OIDC クライアント ID

OIDC_CLIENT_SECRET

どちらか一方

-

confidential client secret

JWT_SIGNING_KEY

どちらか一方

-

public PKCE client または本番環境の FastMCP token 署名キー

OIDC_CONFIG_URL

いいえ

QA discovery URL

OIDC discovery アドレス

BASE_URL

いいえ

http://localhost:8000

MCP server のパブリックアドレス

OIDC_REQUIRED_SCOPES

いいえ

openid

OAuth 認可リクエスト/デフォルト広告の scope。access token の scope 検証には使用しない

OIDC_TOKEN_ISSUER

いいえ

OIDC discovery issuer

JWT iss 検証値。auth middleware が issuer を書き換える場合に設定

OIDC_JWKS_URI

いいえ

https://auth-qa.drillinsight.com/oauth/jwks

カスタム token issuer の JWKS アドレス

OIDC_TOKEN_AUDIENCE

いいえ

-

オプションの JWT aud 検証値

HOST

いいえ

127.0.0.1

リッスンアドレス

PORT

いいえ

8000

リッスンポート

MGT_API_BASE_URL

いいえ

QA MGT アドレス

MGT API の実際の呼び出し Base URL

MGT_OPENAPI_SPEC_PATH

いいえ

specs/mgt-qa-openapi.json

ローカル OpenAPI spec パス

本番環境ではランダムな JWT_SIGNING_KEY を明示的に設定し、HTTPS の BASE_URL を使用してください。.env や client secret を Git にコミットしないでください。

カスタム Token Issuer

token の iss が OIDC discovery が返す issuer ではなく、auth middleware によってテナントアドレスに書き換えられている場合、例えば:

实际 token iss:
https://api-authsome-qa.drillinsight.com/auth-middleware/t_adecdb63-afab-4346-a1aa-b50bbbae7aee/

次のように設定します:

OIDC_TOKEN_ISSUER=https://api-authsome-qa.drillinsight.com/auth-middleware/t_adecdb63-afab-4346-a1aa-b50bbbae7aee/
OIDC_JWKS_URI=https://auth-qa.drillinsight.com/oauth/jwks

OIDC_CONFIG_URL は引き続き OAuth ログインと認可エンドポイントの discovery に使用されます。OIDC_TOKEN_ISSUER は JWT access token の iss 検証のみに使用されます。両者は異なっていても構いません。OIDC_TOKEN_ISSUER は末尾の / を含め、token 内の iss と完全に一致している必要があります。

現在のプロジェクトでは access token の scope や scp claim は検証されません。MGT の過去の Token が非標準の scope 形式を使用しているためです。OIDC_REQUIRED_SCOPES は引き続き OAuth 認可リクエストに使用されますが、標準の scope claim を持たない有効な Token をブロックすることはありません。署名、issuer、audience、有効期限、JWKS の検証は引き続き行われます。

QA MGT OpenAPI の統合

QA OpenAPI spec は次の場所に固定保存されています:

specs/mgt-qa-openapi.json

MCP は実行時にオンラインの /api-docs にアクセスしないため、将来本番環境で API ドキュメントを公開しなくても動作に影響はありません。MGT_API_BASE_URL で実際の API アドレスを切り替えるだけです。Docker イメージは specs/mgt-qa-openapi.json を /app/specs/mgt-qa-openapi.json にコピーし、MGT_OPENAPI_SPEC_PATH を自動設定します。

最初のバージョンで公開される API 範囲:

/api/v1/user/current
/course/list
/batch/list
/batch/trainee/list
/equity/userequity/give
/api/v1/order/**
/api/v1/item/**
/api/v1/open/getSku*
/api/v1/customers
/api/v1/campus-recruitment/**(排除 export)
/api/v1/recruitment-info/**(排除 export)

注文支払いリンク API は現在の要件に応じて統合されています:

/api/v1/order/queryPayLink
/api/v1/order/reGenaratePayLink

返金、支払いコールバック、顧客データエクスポート API は引き続き除外されています:

/mall/v1/order/refund
/alipay/**
/stripe/**
/weixin/refund/**
/api/v1/customers/export
/api/v1/campus-recruitment/export
/api/v1/recruitment-info/export

MGT を呼び出すたびに、OpenAPI client は現在の FastMCP リクエストからユーザーの上流 OAuth access token を取得し、次のものを送信します:

Authorization: Bearer <user access token>
X-Application-Id: <token.app_id>

ここで X-Application-Id は追加設定は不要で、検証済み JWT の app_id claim から直接読み取られます。app_id を持たない Token は拒否され、不完全なリクエストが MGT に送信されるのを防ぎます。

したがって、MGT は auth-qa.drillinsight.com が発行したユーザー Token を信頼し、ユーザー ID に基づいて権限制御を実行する必要があります。

ChatGPT CIMD timeout の調査

ログに次の内容が含まれる場合:

CIMD fetch failed for https://chatgpt.com/.../client.json: Timeout fetching
Unregistered client_id=https://chatgpt.com/.../client.json

FastMCP が ChatGPT がホストするクライアントメタデータに直接アクセスできないことを意味します。現在のマシンが信頼できる送信プロキシ経由で外部ネットワークにアクセスする必要がある場合は、次のように設定してください:

FASTMCP_SSRF_TRUST_PROXY=true
HTTPS_PROXY=http://127.0.0.1:7897

その後、サービスを完全に停止して再起動してください。プログラムは FastMCP をインポートする前にプロジェクトルートの .env を自動的にロードします。FastMCP はデフォルトで CIMD/JWKS リクエストに対して DNS 検証と IP 固定を行うため、通常のプロキシ環境変数は自動的に使用されません。このオプションを有効にすると、SSRF 防御の責任が指定したプロキシに委ねられ、NO_PROXY は無視されます。信頼できるプロキシに対してのみ有効にしてください。

ログ内の最初の POST /mcp 401 は、クライアントが認証前に保護リソースをプローブしているものです。複数の /.well-known/... アドレスへの 404 も ChatGPT の互換性プローブです。/.well-known/oauth-authorization-server が 200 を返していれば、これらは失敗の原因ではありません。

ログに Unregistered client_id=app_... や他の非 URL client ID が表示される場合、ChatGPT がサーバー側ストレージから失われた古い DCR 登録をキャッシュしていることを意味します。JWT_SIGNING_KEY を固定し、サービスを再起動してから、ChatGPT でそのカスタム MCP を削除して再作成し、/register を再呼び出しさせてください。ログインを再試行するだけでは、サーバー側で不明な古い client ID は復元されません。

テスト

uv run pytest

Related MCP Connectors