Skip to content

MCP エンドポイントのブラウザアクセス対策 ​

第1版作成 最終更新 (日本時間)
確認バージョン1.5.8.1

本体の標準機能ではありません

1.5.8.1 では、ブラウザで /mcp を開くと MCP の SDK が返す JSON-RPC のエラーがそのまま表示されます。このページは、ブラウザからのアクセスにプリザンターのエラー画面を返すための改修メモです。

前提にした 1.5.8.1 の実装 ​

原因の詳細は プリザンターの MCP の「ブラウザで /mcp を開くと JSON のエラーが出る」を参照してください。

  • McpServer.json の Enabled が true のとき、ModelContextProtocol.AspNetCore(1.4.1)の AddMcpServer().WithHttpTransport() を登録し、MapMcp("/mcp") でエンドポイントを追加します(Startup.cs#L368-L376、Startup.cs#L795-L798)。パスは McpConstants.BasePath(/mcp)です。
  • パイプラインは UseStatusCodePages → UseStaticFiles → UseRouting → UseCors → (MCP が有効なら)MCP 用レート制限・UseMcpContextMiddleware → UseSession → UseAuthentication → UseAuthorization → UseEndpoints の順です(Startup.cs#L528-L669)。
  • UseStatusCodePages は本文が空の 400・405 などを /errors/badrequest に、404 を /errors/notfound にリダイレクトします。ErrorsController のアクションは HtmlTemplates.Error() で、テーマや表示言語が反映された標準のエラー画面を返します。SDK は本文に JSON を書いて返すため、このリダイレクトは働きません。

対策の候補 ​

対策 A: ミドルウェアで既存のエラー画面へリダイレクトする ​

UseMcpContextMiddleware() より前に、/mcp への GET で Accept に text/html を含むもの(ブラウザの通常の遷移)だけを /errors/badrequest にリダイレクトするミドルウェアを置きます。MCP のクライアントは Accept: text/event-stream などで接続するため影響しません。

csharp
// Startup.Configure 内。既存の if (Parameters.McpServer?.Enabled == true) の中、
// レート制限と UseMcpContextMiddleware() より前に置く
app.Use(async (context, next) =>
{
    if (context.Request.Path.StartsWithSegments(McpConstants.BasePath)
        && HttpMethods.IsGet(context.Request.Method)
        && context.Request.GetTypedHeaders().Accept
            .Any(a => a.MediaType.Equals("text/html", StringComparison.OrdinalIgnoreCase)))
    {
        context.Response.Redirect("/errors/badrequest");
        return;
    }
    await next();
});

図を読み込み中…

  • 独自の HTML を持たずに済み、テーマと表示言語(App_Data/Displays の定義)がそのまま反映されます。
  • パスは McpConstants.BasePath を使い、Parameters.McpServer?.Enabled == true の条件の中に置きます。

対策 B: 静的ファイルとフォールバックのルート ​

wwwroot に案内の HTML を置き、/mcp への GET で text/html を受け付けるときだけ返すルートを足します。MapMcp と同じパスに MapGet を登録することになり、ルーティングが競合するおそれがあります。

対策 C: リバースプロキシで振り分ける ​

nginx などで Accept に text/html を含むリクエストだけ静的な案内ページを返し、それ以外をプリザンターに渡します。本体を改修せずに済みます。

nginx
location /mcp {
    if ($http_accept ~* "text/html") {
        rewrite ^ /mcp-landing.html break;
    }
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_buffering off;
    proxy_cache off;
}

対策 D: MapMcp の前に最小のエンドポイントを置く ​

MapMcp より先に /mcp の MapGet を登録してブラウザ向けの HTML を返します。同じパスに複数の MapGet を登録すると競合し、ブラウザ以外の GET を MCP のハンドラへ渡せないため、勧めません。

比較 ​

対策方式実装の手間仕様への適合保守推奨
Aミドルウェア + 既存のエラー画面小高高◎
B静的ファイル + フォールバック中中(ルートの競合)中○
Cリバースプロキシ中高(本体は無改修)中○
D最小のエンドポイント小低(ルートの競合)中△

MCP の接続方法やツールの一覧をブラウザに表示したくなった場合は、ErrorsController に専用のアクションを足すか、Error.Types に MCP 用の種類を足して Displays に説明文を定義し、対策 A のリダイレクト先を変えます。

関連ページ ​

変更履歴

第1版CodeDefiner のデータベース作成・更新とパラメータの引き継ぎ、画面でのパラメータ管理、MCP エンドポイントのブラウザアクセスの解説と、関連する改修・設計メモを追加