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では、
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での実装手順を中心にします。
- リモートMCPではstdioではなくStreamable HTTPを使う
- 0.0.0.0でListenするだけではRemote公開は完成しない
- 本番URLは公開Domainの形にする
- HTTPS終端後のX-Forwarded-*を正しく扱う
- HTTPSからHTTPへのRedirectはMCP Clientに拒否される
- Remote MCPではHost Allowlistを設定する
- TransportSecuritySettingsで公開Domainを許可する
- OriginとHostは別物
- Internet公開するMCPは認証なしにしない
- MCPサーバー自身でログイン画面を作る必要はない
- Bearer TokenをAuthorizationヘッダーで送る
- TokenVerifierでTokenを検証する
- Tokenの署名だけではなくAudienceも確認する
- ScopeでToolへの権限を分ける
- 「認証」と「認可」は分けて考える
- 401のWWW-AuthenticateからAuthorization Serverを発見できる
- resource_server_urlは実際の公開URLと合わせる
- APIキー1本だけでもいいのか
- Authorization ServerをMCP Serverへ自作しない
- 2026-07-28ではAuthorizationも強化されている
- 2026-07-28ではRemote MCPを通常のHTTP APIに近く運用できる
- Mcp-MethodとMcp-NameをGatewayで利用できる
- Reverse ProxyでMCP Endpointだけを公開する
- nginxでHTTPS終端する場合のイメージ
- Browserから使うならCORSも必要
- 独自HTTP Routeは自動的に認証されるとは限らない
- Health Checkには秘密情報を返さない
- Error Responseに秘密情報を含めない
- APIキーをTool Argumentに渡さない
- ユーザーごとの外部サービスTokenはServer側で関連付ける
- Rate Limitも入れておく
- TimeoutとRequest Sizeも制限する
- Tool自体にもAuthorizationを入れる
- Read系ToolとWrite系Toolを分ける
- 破壊的操作にはユーザー確認も検討する
- まずVPNやPrivate Networkだけで公開する方法もある
- IP制限だけを認証の代わりにしない
- Public URLへAPIキーをQuery Stringで付けない
- 本番ではDebug Modeを無効にする
- ログにはUserとToolを追跡できる情報を残す
- OpenTelemetryも利用できる
- 最初は1台でも2026-07-28を前提にしておくと拡張しやすい
- ローカルMCPをRemote化するときに変わるのはTransportだけではない
- 最初に作るならReverse Proxy + HTTPS + OAuthの構成が分かりやすい
- MCPサーバーのリモート公開に関するよくある質問
- リモートMCPでは「URLを公開する」より「Web APIとして守る」ことが重要
リモートMCPではstdioではなくStreamable HTTPを使う
stdioはMCP HostがServer Processを直接起動する方式です。概念的には次の構成です。
Claude Code ↓ python server.py ↓ stdin / stdout ↓ MCP Server
この方式ではServerがClientと同じPC上に存在するため、URLやPortは必要ありません。Remote MCPではServerが別Machine上に存在します。
Claude Code ↓ HTTPS ↓↓ MCP Server https://mcp.example.com/mcp
そこでStreamable HTTPを利用します。Python SDKなら、ローカルでまず次のように動作確認できます。
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へ配置するとき、
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だけにします。概念的には次の構成です。
Claude Code ↓↓ Reverse Proxy ↓ https://mcp.example.com/mcp↓ MCP Server http://127.0.0.1:8000/mcp
TLS証明書はReverse ProxyやCloud Load Balancerで管理します。Let’s Encryptを使える環境ならCaddyやCertbotなどで自動更新する構成も作れます。
HTTPS終端後のX-Forwarded-*を正しく扱う
HTTPSをReverse Proxyで終端すると、内部MCP Serverへ届く通信はHTTPになることがあります。
Client ↓ HTTPS Reverse Proxy ↓ HTTP Uvicorn
このときApplication側が「自分はhttp://で公開されている」と誤認すると、Redirect URLまでhttp://で生成する場合があります。Python SDKのDeployガイドでは、TLSをProxyで終端する場合にUvicornへProxy Headerを信頼させるよう案内されています。たとえば、
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対策があります。ローカルではデフォルトで、localhost・127.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を明示できます。概念的には次のようになります。
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: mcp.example.com
Originは、そのRequestを発生させたWeb 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が、
データベース検索 ファイル操作 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です。構成は次のようになります。
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を組み込めます。概念的には次の構成です。
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:read・notes:write・adminのような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 UnauthorizedのWWW-Authenticate HeaderからこのMetadataへ誘導できます。この発見の仕組み自体はClaude Code + MCP セキュリティ設計完全ガイドで解説している認可フローの一部です。Python SDKでは概念的に次の情報が返ります。
{
"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へ載せやすくなっています。つまり、
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-Version・Mcp-Method・Mcp-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にする構成が扱いやすくなります。たとえば、
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終端する場合のイメージ
たとえば概念的には次のような構成にできます。
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を作る場合は、
{
"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が必要な場合もあります。この場合、
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-MethodやMcp-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_file・delete_file・run_sql・send_email・deployなどです。認証済み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_customer・create_customer・update_customer・delete_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_id・user_id・client_id・tool_name・duration_ms・statusなどを構造化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だけを見ると、
@mcp.tool()
def search(query: str):
...
はstdioでもStreamable HTTPでもほぼ同じです。しかし運用面は大きく変わります。stdioなら「Local Process」だったものが、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などを追加すると、安全に拡張しやすくなります。
