FastMCPでMCPサーバーを作れたら、次に必要なのは「ローカルでは動く」を「外部から安全に使える」へ変える作業です。
本番運用では、Pythonコードだけでなく、コンテナ化、HTTPS、ヘルスチェック、ログ、再起動、認証までを1つの仕組みとして考える必要があります。
この記事では、FastMCP 3系のサーバーをDockerで動かし、nginxでHTTPS化し、監視できる状態まで持っていく最小構成を解説します。クラウド固有の設定に寄せすぎず、VPS・AWS・Google Cloud・Azure・Renderなどへ応用できる形にします。
この記事のゴール
- FastMCPをDockerコンテナとして起動できる
/mcpをHTTPSで公開する構成が分かる/healthでコンテナの死活監視ができる- 本番運用で避けるべき設定が分かる

FastMCPの本番構成
最小の本番構成は、次の5要素に分けると整理しやすくなります。
| 要素 | 役割 |
|---|---|
| FastMCP | ツール、リソース、プロンプトをMCPとして提供する |
| Uvicorn | FastMCPのASGIアプリをHTTPサーバーとして動かす |
| Docker | 実行環境と依存関係を固定する |
| nginx / クラウドLB | TLS終端、HTTPS、転送、アクセス制御を担当する |
| 監視基盤 | ヘルスチェック、ログ、アラートを扱う |
FastMCPのHTTPトランスポートでは、標準のMCPエンドポイントは/mcpです。本番ではFastMCPをインターネットへ直接さらすより、手前にnginxやクラウドのロードバランサーを置き、HTTPSとアクセス制御を任せる構成が扱いやすいです。
最小プロジェクトを作る
今回は、次の4ファイルを用意します。
fastmcp-production/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignore
1. FastMCPサーバーを作る
app.pyに、MCPツールとヘルスチェック用のHTTPルートを定義します。
from fastmcp import FastMCP
from starlette.requests import Request
from starlette.responses import JSONResponse
mcp = FastMCP("Production MCP Server")
@mcp.tool
def ping(message: str = "pong") -> dict[str, str]:
"""疎通確認用のMCPツール。"""
return {"message": message}
@mcp.custom_route("/health", methods=["GET"])
async def health_check(request: Request) -> JSONResponse:
return JSONResponse({"status": "healthy"})
app = mcp.http_app()
mcp.http_app()は、Uvicornなどから起動できるASGIアプリを返します。MCPの接続先は/mcp、監視用URLは/healthになります。
/healthにはAPIキーや内部ホスト名などの情報を含めません。外部監視サービスやロードバランサーから呼ばれる可能性があるため、正常かどうかだけを短く返します。
2. 依存パッケージを固定する
requirements.txtを作ります。
fastmcp>=3,<4
uvicorn[standard]>=0.30,<1
再現性をさらに高めたい場合は、検証済みのバージョンへ固定し、Dependabotなどで定期更新します。FastMCPのメジャーバージョンを無制限に上げないことが重要です。
3. Dockerfileを作る
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
RUN useradd --no-log-init --create-home --uid 10001 appuser
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py ./
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2)"]
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
ポイントは次のとおりです。
python:3.12-slimで不要なパッケージを減らす- 依存ファイルを先にコピーしてDockerのビルドキャッシュを活用する
USER appuserでroot権限のまま実行しない--reloadを付けない。本番での自動リロードは不要HEALTHCHECKには追加インストール不要のPython標準ライブラリを使う- Uvicornは全インターフェースで待ち受け、公開範囲はDockerやファイアウォール側で制御する
Docker公式ドキュメントでも、不要なパッケージを減らすこと、絶対パスのWORKDIRを使うこと、権限が不要なサービスを非rootユーザーで動かすことが推奨されています。
4. 不要ファイルをイメージへ入れない
.dockerignoreを作ります。
.git
.gitignore
.env
.venv
__pycache__/
*.py[cod]
*.log
tests/
特に.envを含めないことが重要です。秘密情報はイメージへ焼き込まず、実行時の環境変数やクラウドのSecret Managerから渡します。
Dockerイメージをビルドして起動する
プロジェクトのディレクトリで、次を実行します。
docker build --check .
docker build -t fastmcp-server:latest .
docker run --rm \
--name fastmcp-server \
-p 127.0.0.1:8000:8000 \
--env-file .env \
fastmcp-server:latest
-p 127.0.0.1:8000:8000とすると、ホストの外部インターフェースへ直接公開せず、同じマシン上のnginxからだけ接続しやすくなります。構成によってはDockerネットワーク内だけでつなぎ、ホストへポート公開しない方法も選べます。
別のターミナルからヘルスチェックを確認します。
curl --fail http://127.0.0.1:8000/health
次のレスポンスが返れば、Webサーバーは起動しています。
{"status":"healthy"}
コンテナの状態も確認できます。
docker inspect \
--format='{{json .State.Health}}' \
fastmcp-server
healthyになるまでには、start-periodと最初のチェック分の時間がかかることがあります。
MCPクライアントから疎通確認する
/healthが通っても、MCPの初期化やツール呼び出しが成功するとは限りません。本番投入前には、MCPクライアントから/mcpへ接続して確認します。
import asyncio
from fastmcp import Client
async def main() -> None:
async with Client("http://127.0.0.1:8000/mcp") as client:
result = await client.call_tool(
"ping",
{"message": "docker-ok"},
)
print(result.data)
asyncio.run(main())
期待する出力は次のとおりです。
{'message': 'docker-ok'}
ここまで通れば、FastMCP、Uvicorn、Docker、MCP通信の一連の経路を確認できています。
nginxでHTTPS化する
MCPサーバーを外部公開する場合は、平文HTTPではなくHTTPSを使います。nginxの基本的な転送設定は次のようになります。
server {
listen 443 ssl http2;
server_name mcp.example.com;
ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 300s;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
FastMCPのHTTP通信はストリーミングを使うため、通常の短いREST APIとは違う注意点があります。
proxy_buffering offでレスポンスを溜め込まないproxy_read_timeoutをツールの最大処理時間より長くするproxy_http_version 1.1を指定する- 転送元のプロトコルとIPアドレスをヘッダーで渡す
TLS証明書の取得・更新はCertbotやクラウドのマネージド証明書へ任せます。証明書をコンテナイメージへコピーしないでください。
nginxを含めてDocker Composeで管理する場合は、FastMCPコンテナを外部公開せず、nginxだけに443番ポートを公開すると境界が明確になります。
ヘルスチェックは3段階で考える




監視を1本のURLへ詰め込むと、障害の切り分けが難しくなります。役割を3段階に分けると運用しやすくなります。
| 確認 | 目的 | 失敗時の扱い |
|---|---|---|
Liveness /health |
Pythonプロセスが応答するか | コンテナ再起動の判断に使う |
Readiness /ready |
DBや外部APIを利用できるか | 新規リクエストの振り分けを止める |
| MCP疎通テスト | 初期化と主要ツールが動くか | アラートを上げ、原因を調査する |
Livenessで外部APIやデータベースを毎回呼ぶと、一時的な外部障害で正常なコンテナまで再起動されます。/healthは軽量に保ち、依存先の確認は/readyや定期的なMCP疎通テストへ分けます。
ログとアラートで最低限見るもの
本番では、標準出力・標準エラーへログを出し、コンテナ基盤側で収集します。最低限、次を追える状態にします。
- リクエストIDまたはトレースID
- 呼び出されたツール名
- 成功・失敗
- 所要時間
- HTTPステータス
- タイムアウトと再試行回数
- コンテナの再起動回数
- CPU、メモリ、ディスク使用量
一方で、Authorizationヘッダー、アクセストークン、ユーザーの入力全文、ツールの機密な戻り値は、そのままログへ残しません。
アラートは「1回失敗した」だけで鳴らすより、5分間のエラー率、連続ヘルスチェック失敗、p95レイテンシなどで設定するとノイズを減らせます。
複数ワーカーと水平スケールの注意点
FastMCPのStreamable HTTPセッションは、標準ではステートフルです。そのまま複数ワーカーや複数コンテナへ増やすと、同じクライアントの後続リクエストが別のプロセスへ入り、セッションを見つけられない場合があります。
最初は1ワーカーで動かし、CPUや同時接続数を計測するのが安全です。水平スケールが必要になったら、利用機能を確認したうえでstateless_http=Trueを検討します。
mcp = FastMCP(
"Production MCP Server",
stateless_http=True,
)
ただし、elicitationやsamplingなど、サーバーからクライアントへ働きかける機能はステートレス化の影響を受けます。「ワーカー数を増やせば速くなる」と決めつけず、セッション、イベントストア、ロードバランサーの構成を含めて設計してください。
CORSはブラウザクライアントがある場合だけ設定する
デスクトップMCPクライアントやサーバー間通信だけなら、一般的なブラウザCORS設定は不要です。ブラウザから接続する場合のみ、許可するオリジンを明示します。
本番でallow_origins=["*"]にするのは避け、必要なWebアプリのURLだけを許可します。また、MCPで必要となるmcp-protocol-version、mcp-session-id、Authorization、Content-Typeなどのヘッダーを許可・公開する必要があります。
公開前のセキュリティ確認
MCPサーバーをHTTPS化しても、認証がなければURLを知る第三者がツールを呼べる可能性があります。外部公開する場合は、BearerトークンやOAuthで保護してください。
認証実装は、FastMCPの認証を実装する|Bearer・OAuthで保護で詳しく解説しています。
また、REST APIをMCP化したサーバーを公開する場合は、FastMCPでREST APIをMCP化|OpenAPIから自動生成も参考になります。
FastMCP自体の作り方から確認したい場合は、PythonでMCPサーバーを作る|FastMCP入門から読むと流れをつかめます。
本番公開前チェックリスト
- □
/healthが200を返す - □ MCPクライアントから
/mcpへ接続できる - □ コンテナを非rootユーザーで実行している
- □
.envや秘密鍵がイメージに含まれていない - □
--reloadを本番で使っていない - □ HTTPSが有効で、HTTPをHTTPSへ転送している
- □ nginxのバッファリングを無効にした
- □ タイムアウトがツールの最大処理時間より長い
- □ 認証・認可を有効にした
- □ CORSの許可元を必要なURLだけに限定した
- □ トークンや入力全文をログへ残していない
- □ ヘルスチェック失敗とエラー率のアラートがある
- □ コンテナ再起動後にも正常復旧することを確認した
- □ 依存パッケージとベースイメージの更新手順がある
まとめ
FastMCPを本番公開するときは、単にmcp.run()をサーバー上で動かすだけでは不十分です。
まずFastMCPをASGIアプリとしてUvicornで起動し、Dockerで実行環境を固定します。次にnginxやクラウドのロードバランサーでHTTPS化し、ストリーミングに必要なプロキシ設定を加えます。最後に、軽量なヘルスチェック、MCP実疎通、ログ、アラートを分けて監視します。
最初は1ワーカーの小さな構成から始め、実測データを見てからスケールさせるのが堅実です。公開範囲が広がる前に認証まで入れておけば、MCPサーバーを安全に育てやすくなります。

コメント