約 3,302 字 · 読むのに 6 分ほど
WordPress に MCP プラグイン (Royal MCP) を入れて claude.ai のカスタムコネクタや Claude Code から操作できるようにしようとしたところ、サブディレクトリ設置 (example.com/blog のような構成) が原因で OAuth 認証が通らない問題に当たりました。原因の切り分けから解決までにやったことをまとめます。
環境と症状
- WordPress はドメン直下ではなくサブディレクトリ (
example.com/blog) に設置 - プラグインは Royal MCP。エンドポイントは
https://example.com/blog/wp-json/royal-mcp/v1/mcp - claude.ai のカスタムコネクタに登録しても認証が始まらず失敗する
- Claude Code (
claude mcp add --transport http) では「Dynamic Client Registration rejected (HTTP 404)」で接続失敗
切り分け: プラグイン側は正常だった
curl で確認すると、プラグインが自力で用意できる範囲はすべて正常に動いていました。
# MCP エンドポイント (未認証なら 401 の JSON-RPC エラーが正常)
curl -i https://example.com/blog/wp-json/royal-mcp/v1/mcp
# OAuth メタデータ (どちらも 200 で JSON が返る)
curl https://example.com/blog/.well-known/oauth-authorization-server
curl https://example.com/blog/.well-known/oauth-protected-resource
# 動的クライアント登録 (201 が返る)
curl -X POST -H "Content-Type: application/json" \
-d '{"client_name":"test","redirect_uris":["https://claude.ai/api/mcp/auth_callback"]}' \
https://example.com/blog/register
つまり /blog/ 配下のディスカバリー、クライアント登録、認可、トークン発行はすべて機能しています。それでも Claude からは繋がりません。
原因: Claude はドメインルートしか探さない
OAuth のディスカバリー仕様 (RFC 8414 / RFC 9728) では、メタデータの正式な置き場所はドメインルートの /.well-known/ にパスを挿入した形式です。そして Claude のクライアントは、MCP の URL にパスが付いていてもルートしか探しに行きません。
- Claude が探す場所:
example.com/.well-known/oauth-authorization-serverなど (→ ルートサイトの 404 ページ) - プラグインが用意できる場所:
example.com/blog/.well-known/...(WordPress プラグインは自分の設置ディレクトリの外に URL を作れない) - ディスカバリーに失敗した Claude はルート直下の
/registerにフォールバックし、これも 404 → 接続失敗
この挙動は Claude Code のリポジトリに Issue #52954 として報告されていますが、「Closed as not planned」で修正予定はありません。Royal MCP のドキュメントにもこのケースの対処は載っていませんでした。
対処: ルートの .htaccess で /blog へ転送する
ルートサイト側も自分の管理下だったので、ドメインルートの public_html/.htaccess に転送ルールを追加して解決しました。# BEGIN WordPress ブロックより前に置きます (マーカーの外なので WordPress に上書きされません)。
# BEGIN Royal MCP subdirectory fix
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^\.well-known/oauth-authorization-server(/blog)?/?$ /blog/.well-known/oauth-authorization-server [R=302,L]
RewriteRule ^\.well-known/openid-configuration(/blog)?/?$ /blog/.well-known/oauth-authorization-server [R=302,L]
RewriteRule ^\.well-known/oauth-protected-resource(/blog.*)?/?$ /blog/.well-known/oauth-protected-resource [R=302,L]
RewriteRule ^authorize/?$ /blog/authorize [R=302,L]
RewriteRule ^(register|token)/?$ /blog/$1 [R=308,L]
</IfModule>
# END Royal MCP subdirectory fix
ポイントは 2 つあります。
registerとtokenは POST で呼ばれるため、メソッドと本文を保持する 308 を使う (302 だと GET に化けるクライアントがある)- 対象はいずれも現状 404 の URL だけなので、ルートサイトの通常ページには影響しない
結果の確認
# ルートのディスカバリーが 1 回の転送で JSON に到達する
curl -L -i https://example.com/.well-known/oauth-authorization-server/blog
# POST も 308 経由で 201 が返る
curl -L -X POST -H "Content-Type: application/json" \
-d '{"client_name":"test","redirect_uris":["https://claude.ai/api/mcp/auth_callback"]}' \
https://example.com/register
適用後、Claude Code の接続状態は「Failed to connect」から「Needs authentication」に変わり、/mcp からのブラウザ認証で連携が完了しました。claude.ai のカスタムコネクタも同じ経路を使うため、再認証すれば繋がります。
まとめ
- サブディレクトリ設置の WordPress + MCP プラグインで OAuth が失敗するのは、Claude 側のディスカバリーがパスを無視するため
- プラグインの設定では解決できない。ルートの .htaccess に転送ルールを 5 行足せば標準準拠の応答になり解決する
- ルート側を触れない場合の代替は、Claude Code 限定で API キーヘッダー (
X-Royal-MCP-API-Key) 方式。claude.ai コネクタはカスタムヘッダーを設定できないため不可 - サブドメイン設置 (
blog.example.com) ならこの問題自体が起きない
