@discourse/mcp がサブフォルダインストールで失敗する — リードスラッシュ付きパスがサイトのサブパスを除去する
要約
サブパス(例: https://example.com/forum)でマウントされた Discourse インスタンスでは、@discourse/mcp が接続できません。すべての内部 API 呼び出しでサブパスが削除され、誤ったホストのルートにアクセスしてしまうためです。これにより、サイト検証が失敗し、--site 検証だけでなく、すべての後続のツール呼び出し(search、read_topic など)が機能しなくなります。
環境
@discourse/mcpv0.2.7(執筆時点の最新バージョン)npx -y @discourse/mcp@latestを通じた Node- ターゲット: nginx の背後でサブフォルダインストールされた Discourse (
https://<host>/forum)
再現手順
- サブパスに Discourse フォーラムを配置します(例:
https://example.com/forum)。https://example.com/forum/about.jsonが200を返すことを確認してください。 - 以下を実行します:
npx -y @discourse/mcp@latest --site https://example.com/forum --log_level debug - 任意の MCP リクエストを送信するか、起動時の検証を観察します。
期待される動作
サーバーが https://example.com/forum/about.json に対して検証を行い、ツールが https://example.com/forum/<endpoint> を呼び出すこと。
実際の動作
デバッグログによると、リクエストがホストのルートへ送信されており、サブパスには到達していません:
DEBUG HTTP GET https://example.com/about.json
DEBUG HTTP GET https://example.com/about.json -> 403 Forbidden
ERROR Failed to validate --site https://example.com/forum: HTTP 403 Forbidden
/forum セグメントが静かに削除されています。
根本原因
dist/http/client.js:42(および :85 の対応する書き込みパス)において:
const url = new URL(path, this.base).toString();
WHATWG URL 仕様によると、/ で始まる path 引数は絶対パスとして扱われ、ベース URL のパス名を置換します。コードベース全体で、パスは先頭にスラッシュを付けて渡されています。例:
dist/index.js:230—client.get('/about.json')dist/tools/builtin/select_site.js:15—client.get('/about.json')- すべての組み込みツールが同様の
'/...'パターンに従っています。
結果: new URL('/about.json', 'https://example.com/forum') は https://example.com/about.json となり、すべての呼び出しでサブフォルダが失われます。
推奨される修正
(a) HTTP クライアント内で正規化し、path から先頭のスラッシュを削除し、解決前に this.base に末尾のスラッシュを付ける:
const base = this.base.toString().replace(/\/?$/, '/');
const rel = path.replace(/^\//, '');
const url = new URL(rel, base).toString();
あるいは、(b) すべての呼び出し元を相対パス(先頭のスラッシュなし)に変更します。オプション (a) の方が安全です。変更点は単一であり、他の場所で予期せぬ動作を引き起こすリスクがありません。
影響を受けるユーザー向けの回避策
Discourse インストールに、サブパスへ 301 リダイレクトするサブドメインがある場合(例: forum.example.com → example.com/forum/...)、--site にそのサブドメインを指定してください。MCP HTTP クライアントはリダイレクトに従うため、各呼び出しが正しいサブパス URL に解決され、認証も遷移を生き延びます。v0.2.7 において initialize、search などがエンドツーエンドで動作することを確認済みです。
そのようなサブドメインが存在しない場合、現在クライアント側の回避策はありません。このバグはパッケージ内で修正する必要があります。