MCPサーバーをローカルではなくリモート公開する方法|認証・HTTPSの基本

MCPサーバーをローカルではなくリモート公開する方法|認証・HTTPSの基本 AI開発

MCPサーバーをstdioでローカル利用しているだけなら、Claude CodeやClaude Desktopが自分のPC上で子プロセスを起動し、そのプロセスと直接通信できます。

しかし、

リモート公開が必要になる場面
社内の複数ユーザーから使いたい
クラウド上のデータベースへ接続したい
自作MCPサーバーをSaaSとして提供したい
PCごとにMCPサーバーをインストールしたくない

といった用途では、MCPサーバーをネットワーク越しに公開する必要があります。この場合に利用するのがStreamable HTTPです。現在のMCP Python SDKでも、stdioはローカル向け、Streamable HTTPはデプロイするMCPサーバー向けとして案内されています。stdioとStreamable HTTPの基本的な違いはMCPのstdioとStreamable HTTPの違いで解説しているため、この記事ではリモート公開に必要なHTTPSと認証にしぼって解説します。

ただし、ローカルで動いているMCPサーバーを、

危険な変更例
mcp.run(
    transport="streamable-http",
    host="0.0.0.0"
)

へ変更しただけでインターネットへ公開するのは危険です。Remote MCPでは、

Remote MCPで必要になる要素
HTTPS
認証
Host・Origin検証
TokenのAudience確認
Reverse Proxy
Rate Limit
秘密情報管理

など、通常のWeb APIと同じSecurity設計が必要になります。2026年7月28日のMCP仕様ではProtocol CoreがStateless化され、Remote MCPは一般的なHTTPインフラへ載せやすくなりました。一方でAuthorizationも強化され、OAuthやOpenID Connectに沿ったSecurity設計がより重要になっています。

この記事では、ローカルMCPサーバーをRemote MCPとして公開する基本構成と、Streamable HTTP、HTTPS、OAuth 2.1 Bearer Token、Reverse Proxy、Host Allowlistなどを解説します。OAuth 2.1の認可フロー全体やToken Passthrough禁止などのルールはClaude Code + MCP セキュリティ設計完全ガイドで体系的に解説しているため、この記事ではPython SDKでの実装手順を中心にします。

スポンサーリンク
  1. リモートMCPではstdioではなくStreamable HTTPを使う
  2. 0.0.0.0でListenするだけではRemote公開は完成しない
  3. 本番URLは公開Domainの形にする
  4. HTTPS終端後のX-Forwarded-*を正しく扱う
  5. HTTPSからHTTPへのRedirectはMCP Clientに拒否される
  6. Remote MCPではHost Allowlistを設定する
  7. TransportSecuritySettingsで公開Domainを許可する
  8. OriginとHostは別物
  9. Internet公開するMCPは認証なしにしない
  10. MCPサーバー自身でログイン画面を作る必要はない
  11. Bearer TokenをAuthorizationヘッダーで送る
  12. TokenVerifierでTokenを検証する
  13. Tokenの署名だけではなくAudienceも確認する
  14. ScopeでToolへの権限を分ける
  15. 「認証」と「認可」は分けて考える
  16. 401のWWW-AuthenticateからAuthorization Serverを発見できる
  17. resource_server_urlは実際の公開URLと合わせる
  18. APIキー1本だけでもいいのか
  19. Authorization ServerをMCP Serverへ自作しない
  20. 2026-07-28ではAuthorizationも強化されている
  21. 2026-07-28ではRemote MCPを通常のHTTP APIに近く運用できる
  22. Mcp-MethodとMcp-NameをGatewayで利用できる
  23. Reverse ProxyでMCP Endpointだけを公開する
  24. nginxでHTTPS終端する場合のイメージ
  25. Browserから使うならCORSも必要
  26. 独自HTTP Routeは自動的に認証されるとは限らない
  27. Health Checkには秘密情報を返さない
  28. Error Responseに秘密情報を含めない
  29. APIキーをTool Argumentに渡さない
  30. ユーザーごとの外部サービスTokenはServer側で関連付ける
  31. Rate Limitも入れておく
  32. TimeoutとRequest Sizeも制限する
  33. Tool自体にもAuthorizationを入れる
  34. Read系ToolとWrite系Toolを分ける
  35. 破壊的操作にはユーザー確認も検討する
  36. まずVPNやPrivate Networkだけで公開する方法もある
  37. IP制限だけを認証の代わりにしない
  38. Public URLへAPIキーをQuery Stringで付けない
  39. 本番ではDebug Modeを無効にする
  40. ログにはUserとToolを追跡できる情報を残す
  41. OpenTelemetryも利用できる
  42. 最初は1台でも2026-07-28を前提にしておくと拡張しやすい
  43. ローカルMCPをRemote化するときに変わるのはTransportだけではない
  44. 最初に作るならReverse Proxy + HTTPS + OAuthの構成が分かりやすい
  45. MCPサーバーのリモート公開に関するよくある質問
  46. リモートMCPでは「URLを公開する」より「Web APIとして守る」ことが重要

リモートMCPではstdioではなくStreamable HTTPを使う

stdioはMCP HostがServer Processを直接起動する方式です。概念的には次の構成です。

stdioの構成
Claude Code
↓
python server.py
↓
stdin / stdout
↓
MCP Server

この方式ではServerがClientと同じPC上に存在するため、URLやPortは必要ありません。Remote MCPではServerが別Machine上に存在します。

Remote MCPの構成
Claude Code
↓
HTTPS
↓

https://mcp.example.com/mcp
↓ MCP Server

そこでStreamable HTTPを利用します。Python SDKなら、ローカルでまず次のように動作確認できます。

server.py
from mcp.server import MCPServer

mcp = MCPServer("RemoteExample")


@mcp.tool()
def search(query: str) -> str:
    return f"Search result: {query}"


if __name__ == "__main__":
    mcp.run(
        transport="streamable-http",
        port=8000
    )

デフォルトでは、http://127.0.0.1:8000/mcpへMCP Endpointが公開されます。この状態ではまだローカルServerです。本番ではこれをDomainとHTTPSの背後へ配置します。

0.0.0.0でListenするだけではRemote公開は完成しない

DockerやVPSへ配置するとき、

server.py
mcp.run(
    transport="streamable-http",
    host="0.0.0.0",
    port=8000
)

のようにすれば、Network InterfaceからRequestを受け取れるようになります。しかし、http://203.0.113.10:8000/mcpのようなEndpointをそのままInternetへ公開するのは避けたほうがよいでしょう。

HTTPではBearer TokenやTool ArgumentなどがNetworkを流れるため、本番ではHTTPSを基本にします。一般的にはMCP Server自身へTLS証明書を持たせるより、

推奨される構成
Internet
↓
HTTPS
↓
Reverse Proxy / Load Balancer
↓
HTTP
↓
MCP Application

という構成にします。Reverse Proxyにはnginx、Caddy、Cloud Load Balancer、Cloudflare、AWS ALBなどを利用できます。MCP Python SDK自身はProtocol Implementationを提供するもので、TLSやProcess Manager、Load BalancerなどのProduction InfrastructureはASGI Serverや周辺Infrastructure側で管理する設計になっています。

本番URLは公開Domainの形にする

Remote MCPでは、Clientが接続するPublic URLを明確に決めます。たとえば、https://mcp.example.com/mcpです。内部的には、127.0.0.1:8000で動いていても構いません。外部Clientから見えるのは、https://mcp.example.com/mcpだけにします。概念的には次の構成です。

公開URLと内部URL
Claude Code
↓

https://mcp.example.com/mcp
↓ Reverse Proxy ↓
http://127.0.0.1:8000/mcp
↓ MCP Server

TLS証明書はReverse ProxyやCloud Load Balancerで管理します。Let’s Encryptを使える環境ならCaddyやCertbotなどで自動更新する構成も作れます。

HTTPS終端後のX-Forwarded-*を正しく扱う

HTTPSをReverse Proxyで終端すると、内部MCP Serverへ届く通信はHTTPになることがあります。

TLS終端の構成
Client
↓ HTTPS
Reverse Proxy
↓ HTTP
Uvicorn

このときApplication側が「自分はhttp://で公開されている」と誤認すると、Redirect URLまでhttp://で生成する場合があります。Python SDKのDeployガイドでは、TLSをProxyで終端する場合にUvicornへProxy Headerを信頼させるよう案内されています。たとえば、

uvicornの起動例
uvicorn server:app \
  --proxy-headers \
  --forwarded-allow-ips='127.0.0.1'

のようにします。Proxyが別Hostなら、そのProxyのIPを指定します。安易に--forwarded-allow-ips='*'とすると、直接Uvicornへ到達できる環境では偽造したX-Forwarded-* Headerを信頼する可能性があります。Proxy以外からApplication Portへ直接接続できないNetwork構成にしておくことも重要です。

HTTPSからHTTPへのRedirectはMCP Clientに拒否される

たとえばClientがhttps://mcp.example.com/mcpへ接続したとします。ServerがSlash付きURLへRedirectする際に、http://mcp.example.com/mcp/を返してしまうと問題です。Python SDKのClientはHTTPS EndpointからHTTPへのDowngrade Redirectを拒否します。そのため「HTTPSで入ってきたRequestはHTTPSとして認識させる」ことが必要です。

Reverse Proxy構成では、X-Forwarded-Proto: httpsなどが正しくApplicationへ渡され、ASGI Serverがそれを信頼する設定になっているか確認してください。

Remote MCPではHost Allowlistを設定する

MCP Python SDKのStreamable HTTPにはDNS Rebinding対策があります。ローカルではデフォルトで、localhost127.0.0.1::1などのHostだけが許可されます。これは悪意あるWebページからLocal MCP ServerへRequestを送られるDNS Rebinding Attackを防ぐためです。

そのため、ローカルで動いたServerをhttps://mcp.example.comへDeployすると、何も変更していない場合、

拒否されるレスポンス例
403 Forbidden
Invalid Host header

になることがあります。これはMCP Toolの問題ではありません。MCP ApplicationへRequestが到達する前のTransport Securityで拒否されています。

TransportSecuritySettingsで公開Domainを許可する

Python SDKでは、実際に公開するHostを明示できます。概念的には次のようになります。

server.py
from mcp.server import MCPServer
from mcp.server.transport_security import TransportSecuritySettings

mcp = MCPServer("RemoteExample")

security = TransportSecuritySettings(
    allowed_hosts=[
        "mcp.example.com"
    ],
    allowed_origins=[
        "https://app.example.com"
    ],
)

app = mcp.streamable_http_app(
    transport_security=security
)

Port付きHostも利用するなら、運用環境に合わせて許可範囲を設定します。Python SDKのDeployガイドでは、本番Hostを利用する場合にtransport_security=でHost Allowlistを明示することが推奨されています。単純にSecurity Checkそのものを無効化するより、可能であれば実際のDomainをAllowlistへ指定するほうが安全です。

OriginとHostは別物

Web開発ではHostとOriginを混同しやすいので注意してください。HostはRequestの宛先です。

Hostヘッダーの例
Host: mcp.example.com

Originは、そのRequestを発生させたWeb Originです。

Originヘッダーの例
Origin: https://app.example.com

Claude CodeやDesktop ApplicationなどBrowser以外のClientでは、Origin Headerが送られない場合があります。一方、BrowserからMCP Endpointへ接続するならOriginやCORS設定も必要になります。Python SDKのASGIガイドでも、Browser ClientではMCP用Headerを許可するCORS設定が必要になると説明されています。Remote MCPを通常のDesktop Clientだけから使う場合と、Browser Applicationから直接使う場合では、必要なSecurity設定が異なります。

Internet公開するMCPは認証なしにしない

Local MCPでは、「自分のPCからしか起動できない」こと自体がSecurity Boundaryとして機能します。Remote MCPの場合、認証なしでInternetへ公開すると、URLを知っている第三者がToolを実行できる可能性があります。

たとえばToolが、

認証なしで危険なTool例
データベース検索
ファイル操作
GitHub操作
メール送信
顧客情報取得

などを提供していれば大きな問題になります。したがってRemote MCPでは、原則として認証・認可を入れます。現在のMCP Python SDKでは、Streamable HTTPのAuthorizationにOAuth 2.1 Bearer Tokenを利用する構成が用意されています。MCP ServerはOAuth上のResource Serverとして動作し、Tokenを発行するのではなく、RequestのAuthorization Headerに含まれるTokenを検証します。OAuth 2.1の認可フロー自体やPKCE、Token Passthrough禁止といったルールはClaude Code + MCP セキュリティ設計完全ガイドで詳しく解説しています。

MCPサーバー自身でログイン画面を作る必要はない

OAuthというと、ログイン画面・パスワード入力・Token発行・Refresh TokenまですべてMCP Serverへ実装するイメージがあるかもしれません。しかしMCP Serverは基本的にResource Serverです。構成は次のようになります。

OAuthの役割分担
User
↓
MCP Client
↓
Authorization Server
↓
Access Token
↓
MCP Server

Authorization Serverには、Auth0、Microsoft Entra ID、Keycloak、既存の社内Identity Providerなどを利用できます。MCP Serverが行うのは、Authorization: Bearer <token>を受け取り、そのTokenが有効か確認する処理です。Python SDKの現行Authorizationガイドでも、Server側はTokenを発行せず、TokenVerifierで検証するResource Serverとして実装する形が基本になっています。

Bearer TokenをAuthorizationヘッダーで送る

認証済みClientからは、概念的に次のRequestが送られます。

リクエスト例
POST /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJ...
Content-Type: application/json

MCP ServerはTokenを検証します。Tokenが存在しない、期限切れ、署名が不正などの場合は、401 Unauthorizedを返します。Python SDKの認証MiddlewareもAuthorization HeaderからBearer Tokenを取り出し、Token Verifierへ渡す構造になっています。Bearer Tokenを使うからこそ、HTTPSが重要です。HTTPのまま通信すればTokenをNetwork上で保護できません。

TokenVerifierでTokenを検証する

Python SDKではTokenVerifierを実装してToken Validationを組み込めます。概念的には次の構成です。

server.py
from mcp.server import MCPServer
from mcp.server.auth.provider import AccessToken, TokenVerifier
from mcp.server.auth.settings import AuthSettings
from pydantic import AnyHttpUrl


class MyTokenVerifier(TokenVerifier):
    async def verify_token(
        self,
        token: str
    ) -> AccessToken | None:
        result = verify_with_identity_provider(token)

        if not result.valid:
            return None

        return AccessToken(
            token=token,
            client_id=result.client_id,
            scopes=result.scopes,
            resource=result.resource,
        )


mcp = MCPServer(
    "RemoteExample",
    token_verifier=MyTokenVerifier(),
    auth=AuthSettings(
        issuer_url=AnyHttpUrl(
            "https://auth.example.com/"
        ),
        resource_server_url=AnyHttpUrl(
            "https://mcp.example.com/mcp"
        ),
        required_scopes=[
            "tools:read"
        ],
        validate_token_resource=True,
    ),
)

実際のVerifierではJWT Signatureを検証したり、Authorization ServerのToken Introspection Endpointへ問い合わせたりします。SDK自身が「どのTokenなら正しいか」を決めるわけではなく、その部分をTokenVerifierへ実装します。

Tokenの署名だけではなくAudienceも確認する

JWTの署名が正しいだけで、「このMCP Server向けのToken」とは限りません。同じIdentity Providerが、API A・API B・MCP ServerへTokenを発行している場合、別API用TokenをMCP Serverが受け付けないようにする必要があります。そこでAudienceやResourceを確認します。

MCP Python SDKでは、validate_token_resource=Trueを利用すると、TokenのResourceがresource_server_urlと一致するか検証できます。たとえば、https://mcp.example.com/mcp向けに発行されたTokenだけを許可します。Auth0やEntra IDなどが独自のAudience Identifierを利用する場合は、TokenVerifier側でaud Claimを検証します。「署名が正しいJWTなら何でも許可する」という実装にしないことが重要です。

ScopeでToolへの権限を分ける

Tokenが有効でも、すべての利用者へすべての操作を許可する必要はありません。たとえば、notes:readnotes:writeadminのようなScopeを設計できます。読み取り専用ユーザーにはnotes:readだけを発行します。書き込み操作にはnotes:writeを要求します。

Python SDKのAuthSettingsにはrequired_scopesがあり、必要なScopeを持っていないTokenを拒否できます。ただし、Server全体で1つのScopeだけを見るのではなく、Toolごとに権限が異なる場合はApplication LayerでもAuthorizationを確認する設計が必要です。

「認証」と「認可」は分けて考える

Tokenが有効であることを確認するのが認証です。しかし「誰なのか分かった」だけでは、「その人がこのToolを実行してよい」とは限りません。たとえばget_customerは一般ユーザーも利用できる一方、delete_customerは管理者だけにしたいケースがあります。この場合、

認証と認可の流れ
Tokenが有効
↓
User / Clientを特定
↓
Scope・Role・Tenantを確認
↓
Tool実行

という流れにします。MCP Serverを公開するときは、URLを隠すことをAccess Controlの代わりにしないでください。

401のWWW-AuthenticateからAuthorization Serverを発見できる

MCPのOAuth対応では、Clientが「このMCP ServerはどのAuthorization Serverを使うのか」を発見できる仕組みがあります。Python SDKでAuthを構成すると、RFC 9728に基づくProtected Resource Metadataを/.well-known/oauth-protected-resource/mcpのようなEndpointへ公開でき、認証されていないRequestへの401 UnauthorizedWWW-Authenticate HeaderからこのMetadataへ誘導できます。この発見の仕組み自体はClaude Code + MCP セキュリティ設計完全ガイドで解説している認可フローの一部です。Python SDKでは概念的に次の情報が返ります。

Protected Resource Metadataの例
{
  "resource": "https://mcp.example.com/mcp",
  "authorization_servers": [
    "https://auth.example.com/"
  ],
  "scopes_supported": [
    "tools:read"
  ],
  "bearer_methods_supported": [
    "header"
  ]
}

ClientはこのMetadataを確認し、適切なAuthorization ServerからTokenを取得できます。

resource_server_urlは実際の公開URLと合わせる

ローカルではhttp://127.0.0.1:8000/mcpだったServerを本番でhttps://mcp.example.com/mcpへ公開したとします。この場合、Authorization設定にもPublic URLを使います。

本番設定
resource_server_url=AnyHttpUrl(
    "https://mcp.example.com/mcp"
)

とします。内部URLであるhttp://127.0.0.1:8000/mcpをそのまま残すと、Clientが認識しているResourceとToken Audienceの検証が一致しない可能性があります。Python SDKのAuthorizationガイドでも、resource_server_urlはClientが実際に接続するPublic URLと一致させることが重要とされています。

APIキー1本だけでもいいのか

小規模な社内MCPなどでは、Authorization: Bearer <固定APIキー>のような簡易方式を使いたくなることがあります。限られたNetwork内で少人数だけが使うServerなら、実装上は固定Tokenを検証することもできます。実際、Python SDKのAuthorization例でも仕組みを説明するためStatic Tokenが利用されています。

ただしInternetへ公開する本番Serverでは、

管理しにくい設計
全ユーザーで1つのToken
有効期限なし
スコープなし
失効機能なし
利用者識別なし

という設計は管理しにくくなります。複数ユーザーや外部提供を想定するなら、OAuthベースのAccess Tokenを使ったほうがToken失効、Scope、ユーザー識別、Audit Logなどを実装しやすくなります。

Authorization ServerをMCP Serverへ自作しない

OAuthを導入すると、「MCP Server内部にログイン機能も作ろう」と考えるかもしれません。しかし現在のMCP Python SDKでは、MCP ServerをResource Serverとして扱い、Tokenを発行するAuthorization Serverとは分離する構成が推奨されています。既存のIdentity Providerがあるなら、それを利用するほうがよいでしょう。ログイン、MFA、Password Reset、Token Rotationなどまで独自実装するとSecurity上の責任範囲が大きくなります。

2026-07-28ではAuthorizationも強化されている

2026年7月28日のMCP仕様ではAuthorization周辺にも変更が入りました。Authorization Responseのiss検証、IssuerごとのClient Credential分離、Scope Step-upの明確化などが行われています。またDynamic Client Registrationは非推奨となり、Client ID Metadata Documentsへ移行する方針が示されています。Client ID Metadata Documentの具体的な形式はClaude Code + MCP セキュリティ設計完全ガイドにサンプルがあります。古いMCP OAuth実装例をそのままコピーする場合は、どのProtocol Revision向けなのか確認してください。特に2025年以前の記事ではAuthorization Flowが現在のSDKと異なる可能性があります。

2026-07-28ではRemote MCPを通常のHTTP APIに近く運用できる

2026-07-28ではProtocol Level Sessionが削除されました。以前のStreamable HTTPでは、Mcp-Session-Id・Sticky Session・Session Storeなどが問題になるケースがありました。現在のProtocolでは各RequestがSelf-containedになり、通常のRound-robin Load Balancerへ載せやすくなっています。つまり、

通常のHTTP構成に近づく
Internet
↓
Load Balancer
↓
MCP Server A
MCP Server B
MCP Server C

という一般的なHTTP Serviceの構成へ近づいています。Remote MCPをProduction運用するうえで大きなメリットです。

Mcp-MethodとMcp-NameをGatewayで利用できる

2026-07-28のStreamable HTTPでは、RequestにMCP-Protocol-VersionMcp-MethodMcp-NameなどのHeaderが利用されます。たとえばTool Callなら、

リクエストヘッダーの例
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search_products

のようになります。これによりAPI GatewayやWAF、Rate LimiterがJSON Bodyを解析しなくても、どのMCP Methodか、どのToolかを判断しやすくなります。たとえば高コストToolだけRate Limitを厳しくするなど、Gateway Layerでの制御にも利用できます。

Reverse ProxyでMCP Endpointだけを公開する

MCP Server本体のPortをInternetへ直接Exposeするより、Reverse ProxyだけをPublicにする構成が扱いやすくなります。たとえば、

Reverse Proxy構成
Internet
↓
443
↓
nginx / Caddy / Load Balancer
↓
127.0.0.1:8000

とします。Firewallでは8000番Portを外部から遮断します。これにより、TLS・Access Log・Rate Limit・IP制限・Request Size制限・WAFなどをProxy側へ集約できます。MCP Python SDK自身も、TLSやConnection LimitなどはMCPServerではなくASGI ServerやDeployment Infrastructure側で設定する設計です。

nginxでHTTPS終端する場合のイメージ

たとえば概念的には次のような構成にできます。

nginx.conf
server {
    listen 443 ssl;
    server_name mcp.example.com;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location /mcp {
        proxy_pass http://127.0.0.1:8000;

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

実際には利用するMCP SDKの通信形式、Timeout、Request Size、Streaming要件などに合わせてProxy設定を調整します。またUvicorn側でも、信頼できるProxyからのForwarded Headerを認識する設定が必要です。

Browserから使うならCORSも必要

Claude CodeのようなDesktop / CLI Clientから接続するだけなら、一般的なBrowser CORSは問題になりません。しかしWeb Application内のJavaScriptからRemote MCPへ直接アクセスする場合はCORS設定が必要です。Python SDKのASGIガイドでも、Browser Clientを利用する場合はMCPのRequest HeaderをCORS Allow Headerへ追加する必要があるとされています。

ただし、

避けたい設定
Access-Control-Allow-Origin: *

を無条件に指定し、Credential付きRequestまで広く許可するような構成は避けます。Browserから利用するFrontend Originが決まっているなら、https://app.example.comだけに限定するほうが安全です。

独自HTTP Routeは自動的に認証されるとは限らない

MCP Serverへ/health/status/callbackなど独自HTTP Routeを追加する場合も注意してください。高レベルAPIのFastMCPレイヤーには@mcp.custom_route()のように通常のHTTP Routeを簡単に追加できる機能がありますが、こうして追加したRouteはMCPのOAuth Auth Middlewareの対象外になります。低レベルAPIでASGI Routeを直接追加する場合も同様に、MCP Endpoint用のAuth Middlewareは自動的には適用されません。

Health Checkなら認証不要でも問題ない場合があります。しかし、内部状態・ユーザー情報・管理機能を返すRouteを同じ感覚で追加すると情報漏えいにつながる可能性があります。MCP Endpointが認証済みだからといって、同じApplicationのすべてのURLが自動で保護されるとは考えないでください。

Health Checkには秘密情報を返さない

Load Balancer向けに/healthを作る場合は、

health応答の例
{
  "status": "ok"
}

程度にします。次のような内容まで返す必要はありません。

返してはいけない情報
Database Password
API Key
内部Host名
Token
Detailed Stack Trace
Environment Variable

Health Checkは認証なしで外部Infrastructureから利用されることがあるため、公開されても問題ない最小情報だけ返します。

Error Responseに秘密情報を含めない

Remote MCPではServer ErrorがNetwork越しにClientへ返ります。たとえば例外処理を、

避けたい実装
except Exception as e:
    return str(e)

だけにすると、Database接続文字列や内部File Pathなどが含まれる可能性があります。Clientへは必要最小限のError Messageを返し、詳細なStack TraceはServer Logへ記録します。特にOAuth TokenやAPI KeyをTool Argumentとして扱う場合は、Loggingにもそのまま出力しないよう注意してください。

APIキーをTool Argumentに渡さない

Remote MCPが外部APIへ接続する場合、

避けたい設計
{
  "api_key": "sk-..."
}

のように毎回Tool ArgumentへAPI Keyを渡す設計は避けたほうがよいでしょう。API CredentialはServer側のSecret ManagerやEnvironment Variableへ保存します。Clientからは、

渡すべき最小限の引数
{
  "query": "example"
}

だけを送ります。Serverが自分のCredentialを使って外部APIへ接続します。これならLLM ContextやTool Call履歴へ秘密情報が混入する範囲を減らせます。

ユーザーごとの外部サービスTokenはServer側で関連付ける

GitHubやGoogleなどユーザーごとのAccess Tokenが必要な場合もあります。この場合、

ユーザー別Tokenの取得フロー
MCP Clientが認証
↓
ServerがUser Identityを取得
↓
Server側Storageから対象UserのCredentialを取得
↓
外部APIを呼ぶ

という構成にします。MCP Tool Argumentへ生のRefresh Tokenなどを入れてモデルに扱わせる必要はありません。Tokenは暗号化StorageやSecret Management Serviceなどで管理します。

Rate Limitも入れておく

Remote MCPはURLへ到達できるClientから大量にTool Callされる可能性があります。特にToolが高額な外部API・LLM API・検索API・Database・大量ファイル処理などを呼ぶ場合、Costや負荷が増える可能性があります。Reverse ProxyやAPI Gateway側で、Client単位・User単位・Tool単位・IP単位などのRate Limitを検討します。2026-07-28ではMcp-MethodMcp-Name Headerが使えるため、GatewayでTool単位のRoutingや制御もしやすくなっています。

TimeoutとRequest Sizeも制限する

MCP Serverだからといって、巨大なRequestを無制限に受け取る必要はありません。Tool Argumentに大量の文字列を渡される可能性もあります。Application ServerやReverse Proxyで、最大Request Size・最大実行時間・Connection Timeoutを設定します。MCP Python SDKにもStreamable HTTPのRequest Body Sizeに関連する設定がありますが、ProductionではProxyやASGI Serverを含む複数層で制限する構成も検討します。

Tool自体にもAuthorizationを入れる

Network Layerが安全でも、Toolが危険な操作を無制限に許可していれば問題です。たとえばread_filedelete_filerun_sqlsend_emaildeployなどです。認証済みUserだからといってすべてを許可するのではなく、Tool側でも対象Resource・Tenant・Role・Scope・操作内容を確認します。特に複数企業が同じMCP Serverを利用するMulti-tenant構成では、UserAがTenant BのデータへアクセスできないことをServer側で保証する必要があります。モデルへのInstructionだけに依存しないでください。

Read系ToolとWrite系Toolを分ける

Remote MCPではTool設計自体もSecurityに影響します。たとえばmanage_customerという1つのToolで検索・追加・更新・削除をすべて行わせるより、get_customercreate_customerupdate_customerdelete_customerのように分けたほうがAuthorizationを設定しやすくなります。読み取りToolにはRead Scopeだけを許可し、破壊的操作にはより強いScopeを要求できます。AIモデルにとってもToolの副作用が分かりやすくなります。

破壊的操作にはユーザー確認も検討する

たとえば本番DB削除・ユーザー削除・メール送信・デプロイ・課金処理などは、Tokenを持っているだけで即実行するのではなく、追加確認を挟む設計も検討できます。2026-07-28 MCPではMulti Round-Trip Requestsが導入され、追加情報や確認が必要な処理をStateless HTTP上で扱いやすくなっています。ただし確認画面だけをSecurity Boundaryにせず、Server側のAuthorizationも必ず行います。

まずVPNやPrivate Networkだけで公開する方法もある

社内利用だけなら、いきなりInternetへ公開する必要はありません。たとえばTailscale・WireGuard・Cloud VPC・社内VPN・Private Networkの内側だけでMCP Serverを公開できます。この場合もHTTPSや認証を省略してよいとは限りませんが、Attack Surfaceをかなり狭められます。特に初期段階では、VPN内+HTTPS+Bearer Token程度から始めると、Public Internetへ直接Exposeするより安全に検証できます。

IP制限だけを認証の代わりにしない

社内IPだけ許可するAllow 203.0.113.0/24といった制限はDefense in Depthとして有効です。しかしIP AddressだけをUser Identityの代わりにはしないほうがよいでしょう。NATやVPN環境では複数Userが同じIPを共有します。IP AllowlistとOAuth Authenticationは目的が異なります。

目的の違い
IP制限 = 接続元Networkを制限
OAuth = 利用者・Clientを識別

として組み合わせます。

Public URLへAPIキーをQuery Stringで付けない

認証Tokenを、https://mcp.example.com/mcp?token=secretのようにURLへ入れるのは避けます。URLはAccess Log・Browser History・Proxy Log・Monitoring・Refererなどへ残る可能性があります。Bearer Tokenは、

正しい送り方
Authorization: Bearer <token>

としてHeaderへ送ります。OAuth対応MCPでもこの方式が前提です。

本番ではDebug Modeを無効にする

開発中はDebug Modeが便利ですが、本番ではDetailed ErrorやInternal Stateを露出する可能性があります。Remote MCPでは、DEBUG Logging・Verbose Stack Trace・Development Server・Auto Reloadなどをproduction設定のまま残さないようにします。ログはServer側へ保存し、Clientには必要最小限の情報だけ返します。

ログにはUserとToolを追跡できる情報を残す

Remote MCPは複数Userが利用するため、問題発生時に、誰が・いつ・どのToolを・どのくらい実行し・成功したか失敗したかを追えると便利です。たとえばrequest_iduser_idclient_idtool_nameduration_msstatusなどを構造化Loggingへ残します。

ただし、Access Token・Refresh Token・Password・API Key・Tool Argument全文を無条件に保存しないようにします。個人情報や秘密情報がLogへ残る可能性があります。

OpenTelemetryも利用できる

複数Serverや外部APIをまたぐRemote MCPでは、単純なText Logだけでは原因を追いにくくなります。2026-07-28ではMCPのTrace Contextについても整理されており、OpenTelemetryなどのDistributed Tracingと組み合わせやすくなっています。stderrとOpenTelemetryをどちらもどう使い分けるかはMCPのProtocol Loggingが非推奨になった理由で詳しく解説しています。本番運用するRemote MCPでは検討する価値があります。

最初は1台でも2026-07-28を前提にしておくと拡張しやすい

小規模なMCPなら最初はServer 1台で十分です。

最初の構成
mcp.example.com
↓
MCP Server × 1

しかし利用者が増えたら、

拡張後の構成
Load Balancer
↓
MCP Server × N

へ増やせます。MCP 2026-07-28ではProtocol CoreがStatelessになったため、以前のようなProtocol Sessionを共有する必要がなく、通常のRound-robin Load Balancerへ載せやすくなっています。新しくRemote MCPを作るなら、古いSession前提の記事より2026-07-28対応SDKの実装を参考にしたほうが将来的な拡張が容易です。

ローカルMCPをRemote化するときに変わるのはTransportだけではない

Tool Logicだけを見ると、

Tool定義
@mcp.tool()
def search(query: str):
    ...

はstdioでもStreamable HTTPでもほぼ同じです。しかし運用面は大きく変わります。stdioなら「Local Process」だったものが、Remoteでは、

Remoteで新たに必要になる要素
Public URL
TLS
Identity
Token
Network
Proxy
Monitoring
Rate Limit
Authorization

を持つWeb Serviceになります。つまり、mcp.run("streamable-http")へ変えるだけでProduction-readyになるわけではありません。

最初に作るならReverse Proxy + HTTPS + OAuthの構成が分かりやすい

一般的なRemote MCPなら、次の構成から考えると分かりやすいでしょう。

基本構成
MCP Client
↓
HTTPS
↓
Reverse Proxy / Load Balancer
↓
Bearer Token検証
↓
Streamable HTTP MCP Server
↓
Tool
↓
Database / External API

Public URLはhttps://mcp.example.com/mcpにします。Reverse ProxyでTLSを終端し、MCP Server本体のPortはInternetへ直接公開しません。MCP ServerではBearer Tokenを検証し、AudienceやScopeも確認します。さらにTool実行時にもUserやTenantのAuthorizationを確認します。この形なら通常のWeb APIで使われてきたSecurityとDeploymentの知識をそのまま活用できます。

MCPサーバーのリモート公開に関するよくある質問

Qstdioで作ったMCPサーバーのToolはそのままリモート公開できますか

ATool自体の定義(名前・説明・入力スキーマ・処理内容)はTransportと分離されているため、基本的にそのまま利用できます。変わるのはmcp.run()の引数などTransportの指定と、認証情報の受け取り方です。ただしローカルファイルパスをそのまま扱うToolは、Remote環境からアクセスできないため設計を見直す必要があります。

QHost Allowlistを設定しないとどうなりますか

A本番Domainへデプロイしても、ローカル向けのデフォルト設定(localhost・127.0.0.1・::1のみ許可)が残っているため、Requestを受け付ける前に403 Forbiddenで拒否されることがあります。TransportSecuritySettingsのallowed_hostsへ実際の公開Domainを追加してください。

Q固定のAPIキーだけでもRemote MCPを運用できますか

A限られたネットワーク内の少人数利用であれば技術的には可能です。ただしInternetへ公開する場合、有効期限やScope、失効機能、利用者識別がない固定Tokenは運用上のリスクが大きくなります。複数ユーザーや外部提供を想定するなら、OAuth 2.1ベースのAccess Tokenを使ったほうが安全に運用できます。

QMCP Server自身でログイン機能を実装する必要がありますか

A必要ありません。MCP ServerはOAuthのResource Serverとして実装するのが基本で、Tokenの発行はAuth0やEntra ID、社内Identity ProviderなどのAuthorization Serverに任せます。MCP Server側はTokenVerifierでAuthorization HeaderのTokenを検証するだけです。

Q独自に追加したHTTP RouteもMCPの認証で保護されますか

A自動的には保護されません。FastMCPの@mcp.custom_route()や低レベルAPIで追加したASGI RouteはMCPのOAuth Auth Middlewareの対象外です。/healthのように認証不要にしたいRoute以外は、必要に応じて個別に認証・認可を実装してください。

QReverse Proxyは必須ですか

A必須ではありませんが、TLS終端やAccess Log、Rate Limit、IP制限などを一か所に集約できるため、多くの構成で扱いやすくなります。MCP Server本体のPortをInternetへ直接公開せず、Reverse Proxyの背後へ配置する構成が一般的です。

リモートMCPでは「URLを公開する」より「Web APIとして守る」ことが重要

ローカルMCP ServerをRemote化するだけなら、Streamable HTTPへ変更してPortを公開すれば通信自体はできます。しかし本番で重要なのは、通信できることではなく、許可されたClientだけが安全にToolを実行できる状態を作ることです。

Remote MCPではHTTPSを利用し、MCP Applicationの生Portを直接InternetへExposeするのではなく、Reverse ProxyやLoad Balancerの背後へ配置する構成が扱いやすくなります。Python SDKを実際のDomainへDeployする場合は、localhost向けに有効になっているDNS Rebinding ProtectionのHost Allowlistも変更する必要があります。設定しなければ403 Forbiddenで拒否されることがあります。

認証にはOAuth 2.1 Bearer Tokenを利用でき、MCP ServerはTokenを発行するAuthorization Serverではなく、Tokenを検証するResource Serverとして実装できます。そしてTokenの署名だけでなく、Audience・Resource・Scope・User・Tenantまで確認し、ToolごとのAuthorizationを行います。

2026-07-28ではRemote MCPがStatelessなHTTP Workloadとして運用しやすくなり、通常のLoad Balancer、Gateway、Rate Limiter、Observability基盤へ載せやすくなっています。

MCPサーバーをRemote公開するときは、「ローカルServerへURLを付ける」のではなく、「外部から利用されるWeb APIへ昇格させる」と考えるのが重要です。最初は、Streamable HTTP・HTTPS・Host Allowlist・Bearer Token・Reverse Proxy・Server側Authorizationまでを基本構成とし、その後利用者数や用途に応じてRate Limit、Audit Log、OpenTelemetry、複数Workerなどを追加すると、安全に拡張しやすくなります。