個人で開発しているサービスdodo-docに認可付きMCPサーバーを実装しました。
ホストしているドキュメントにCoding Agentから自然にアクセスして知識を取得することができるようになっています。
実装に必要な機能
Authorization - Model Context Protocol
modelcontextprotocol.io
公式のドキュメント2025-11-25を参考に実装が必要な要素をまとめました。
一応この記事を書くにあたってRFCに目を通していますが間違いなどあれば教えてください。
認可サーバー側に必要な実装
- OAuth2.1
- 認可サーバーに関するメタデータの提供 -> 以下のいずれか
- RFC8414
- OIDCディスカバリー1.0
OAuth2.1が要求されているのでPKCEのサポートが必須になっています。
またOAuth2.1ではcallback URL周りの検証も厳格化されているので、その辺の対応も必要になってきます。
認可サーバーのメタデータについては、MCPの仕様上はRFC8414またはOIDC Discovery 1.0の少なくともどちらか一方を提供する必要があります。
私はRFC8414を採用したので、
/.well-known/oauth-authorization-serverを実装しました。RFC本文の例は以下のようなものです。{
"issuer": "https://server.example.com",
"authorization_endpoint": "https://server.example.com/authorize",
"token_endpoint": "https://server.example.com/token",
"token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"],
"token_endpoint_auth_signing_alg_values_supported": ["RS256", "ES256"],
"userinfo_endpoint": "https://server.example.com/userinfo",
"jwks_uri": "https://server.example.com/jwks.json",
"registration_endpoint": "https://server.example.com/register",
"scopes_supported": ["openid", "profile", "email", "address", "phone", "offline_access"],
"response_types_supported": ["code", "code token"],
"service_documentation": "http://server.example.com/service_documentation.html",
"ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"]
}
issuer, authorization_endpoint, token_endpointあたりがRFC8414上最低限必要です。
またPKCEが必要なのでcode_challenge_methods_supportedも実質的に必須です。
後述するクライアント登録でDynamic Client Registration Protocolを採用する場合にはregistration_endpointも必要になります。
MCPサーバー側に必要な実装
- 保護されたエンドポイントに関するメタデータの提供 -> RFC9728
直感的にはMCP側で認可が必要なリソースの範囲や認可サーバーの場所に関するメタデータを追加で提供するイメージです。
RFC9728は保護されたリソースのメタデータを公開する仕様で、以下のようなレスポンスを返します。(RFC9728本文から引用)。RFC9728自体で必須なのは保護対象のリソースを表す
resourceです。authorization_serversはRFC9728上はoptionalですが、MCPのAuthorization仕様に従うHTTPサーバーとしては少なくとも1つ返す必要があります。scopes_supportedはrecommendedで、必要なスコープをクライアントに伝えるのに便利です。{
"resource": "https://resource.example.com",
"authorization_servers": [
"https://as1.example.com",
"https://as2.example.net"
],
"bearer_methods_supported": ["header", "body"],
"scopes_supported": ["profile", "email", "phone"],
"resource_documentation": "https://resource.example.com/resource_documentation.html"
}
クライアント登録
OAuthの認可フローを始めるにあたってMCPクライアント(Claude CodeやCodex)などは予めClient IDを知っておく必要があります。
クライアントIDをMCPクライアントに告知する方法には大きく以下の3通りがあります。
- Client ID Metadata Document(CIMD)を使う。 ただし少なくとも私が調べた時点ではClaude Codeはサポートしていない。
- 手動でClient IDを取得してMCPクライアント側に事前に登録しておく。
- RFC7591の動的クライアント登録(DCR)を使ってクライアントIDを発行する。
仕様上は事前登録が第一候補で、その次にCIMD、さらに後方互換のための選択肢としてDCRがあります。
そのため仕様書的にはDCRは必須ではありません。
ただし現実のクライアント実装には差があり、少なくとも私が調べた時点ではClaude CodeはCIMDをサポートしていませんでした。
またCodex系クライアントでは事前登録の扱いが見えにくく、DCRを実装しておいた方が相性が良いケースがありそうです。
そのため一般に多く使われているコーディングエージェントを幅広くサポートしたいのであれば、DCRは有力な選択肢になります。
動的クライアント登録は管理が大変なので、今回私は事前クライアント登録を選択しました。
Claude Codeでは
claude mcp add --client-id xxxというオプションがあるので、事前登録の実装はユーザーにコマンドをコピペするだけで完結します。認可フローの全体像
公式の仕様からの画像ですが以下の通りです。基本的にOAuth2のような流れで前後に各種情報を取得するためのメタデータ取得が存在しています。
Goでの実装
上でも紹介しましたが最低限以下の実装が必要になります。
今回はGo言語で私が運用するdodo-docにMCPを開発して組み込んでみました。
- OAuth2.1
- RFC8414
- RFC9728
- クライアント登録方法のいずれか -> 実装の簡潔さの観点で事前登録方式を採用
OAuth2.1の実装
セキュリティ的に一番クリティカルな箇所なので可能な限り自前実装を避ける方針が好ましいです。
実装する際に軽くサーベイした候補は以下の通りでした。
- hydra OSSのOAuth2サーバー。基本的な機能はサーバー側が提供しつつ、認証認可処理周りには自作のアプリケーションコードを組み込めそう。
- fosite hydraの内部で使われているgoのOAuth2ライブラリ。
- 自前実装
今回はfositeを使うことにしました。
hydraをセットアップするのが大変そうだったのと、既存のバックエンド実装があったのでそれと統合して管理したかったのが背景です。
あまりドキュメントが充実していませんがコード読めばわかるのと、コーディングエージェントがその辺を自動化してくれるので割となんとかなります。
RFC8414
愚直にjsonを返すエンドポイントを実装しました。
RFCをちゃんと読む必要はありますが、結局のところ静的なJSONを返すエンドポイントなので実装は簡単でした。
RFC9728
go mcp sdkを使っているのであれば、標準でutilが存在します。
わかりやすいドキュメントが存在する上にExampleが充実しているので、この通り実装すれば自然にwellknown URLを生やすことができます。
go-sdk/examples/auth/server/main.go at 16d990b0416f63ca4948a5d8ba8f54ac6114b5a9 · modelcontextprotocol/go-sdk
github.com
まとめ
dodo-docに認可機能付きMCPを実装しました。
主に大変な箇所はOAuthの2.1周りだけで、それ以外の実装周りはそこまで面倒ではありません。
クライアント登録まわりはもう少しコーディングエージェント界隈全体として共通化が進むと嬉しいですね。