Cloud RunにMCPサーバーをデプロイしてみた

記事タイトルとURLをコピーする

G-gen の佐々木です。当記事では Cloud Run に MCP サーバーをデプロイし、Gemini CLI からの接続を試してみます。

はじめに

MCP サーバーについて

MCP サーバーの概要

MCP(Model Context Protocol)は、Anthropic 社が提唱した、Gemini のような大規模言語モデル(LLM)と外部ツールを連携するためのオープンプロトコルです。

MCP サーバー は、MCP で実装された外部ツールを LLM に提供するためのサーバーです。ファイルシステムやメール、データベース、天気情報 API など、特定の領域に特化したツールを MCP サーバーに実装することで、LLM の機能を拡張することができます。

MCP サーバーに対しては、Gemini CLI や Claude Desktop、Cursor、Cline といった LLM アプリケーションから接続することができます。

ツール(tools)

MCP サーバーにおけるツールは、MCP サーバーが LLM に対して提供する関数です。MCP サーバーは LLM からの要求に従ってツール(関数)を実行し、その処理結果を LLM に返します。

LLM はユーザーが自然言語で入力したプロンプトに応じて適切な MCP サーバー・ツールを選択し、返ってきた結果を元に処理を継続したり、応答を生成したりします。

Cloud Run について

Cloud Run の概要

Cloud Run は、Google Cloud が提供するサーバーレス コンテナ コンピューティング サービスであり、ユーザーによるインフラ管理が不要なサーバーレス環境でコンテナを実行することができます。

Cloud Run では、必要なときだけ CPU、メモリといったコンピューティングリソースを確保する(コンテナを起動する)ことができ、コンテナが起動していないときはコンピューティングリソースの使用料金が発生しないという特徴があります。

Cloud Run にはいくつかの種類がありますが、当記事ではウェブアプリケーションの実行に特化した Cloud Run サービス を使用します。

サービスの詳細な解説については、以下の記事をご一読ください。

blog.g-gen.co.jp

Cloud Run における MCP サーバーのホスティング

Cloud Run で MCP サーバーをホスティングすることで、複数のユーザーが共通の MCP サーバーを使用することが可能になります。また、IAM 認証により MCP サーバーに接続できるユーザーを制限することができます。

IAM で許可されたユーザーだけが Cloud Run 上の MCP サーバーを使用できる

2025年12月時点では、MCP サーバーとの通信方式(Transports)として標準入出力(stdio)と Streamable HTTP が利用できます。

Cloud Run に MCP サーバーをホストする場合は、Streamable HTTP のみ使用することができます。通信方式の詳細については以下のドキュメントを参照してください。

検証環境とツール

当記事では、Google が提供するオープンソース AI エージェントである Gemini CLI から、Cloud Run にデプロイした MCP サーバーのツールを呼び出せるように構成していきます。

MCP サーバーの実装は Python で行い、パッケージ管理ツールとして uv を使用します。それぞれ、以下のバージョンを使用しています。

$ python --version
Python 3.13.3
  
$ uv self version
uv 0.9.16

Gemini CLI は以下のバージョンを使用しています。

$ gemini -v
0.19.4

Gemini CLI の詳細やインストール方法等については、以下の記事を参照してください。

blog.g-gen.co.jp

当記事では、以下の Google Cloud ドキュメントを参考に MCP サーバーを作成していきます。

サンプル MCP サーバーの作成

Python プロジェクトの準備(uv)

まずは、uv を使用して Python プロジェクトを作成します。

当記事では以下のように mcp-on-cloudrun ディレクトリにプロジェクトを作成し、各ファイルを配置します。

└── mcp-on-cloudrun
    ├── pyproject.toml
    ├── server.py
    ├── Dockerfile
    └── uv.lock

まずは mcp-on-cloudrun ディレクトリを作成します。

# ディレクトリを作成する
$ mkdir mcp-on-cloudrun && cd mcp-on-cloudrun

mcp-on-cloudrun ディレクトリ内で uv を使用してプロジェクトを初期化します。

# uv で Python プロジェクトを作成する
$ uv init \
    --name "mcp-on-cloudrun" \
    --description "Example of deploying an MCP server on Cloud Run" \
    --bare \
    --python 3.13

当記事では、fastmcp フレームワークを使用して MCP サーバーを実装します。以下のコマンドで、プロジェクトの依存関係に fastmcp を追加します。

# fastmcp を依存関係に追加する
$ uv add fastmcp==2.13.3 --no-sync

ここまでの手順で、mcp-on-cloudrun ディレクトリに以下の内容で pyproject.toml が作成されています。

[project]
name = "mcp-on-cloudrun"
version = "0.1.0"
description = "Example of deploying an MCP server on Cloud Run"
requires-python = ">=3.13"
dependencies = [
    "fastmcp==2.13.3",
]

MCP サーバーを実装するためのファイル(server.py)と、Dockerfile を作成します。ファイルの内容については後の手順で解説します。

# server.py と Dockerfile を作成する
$ touch server.py Dockerfile

サンプルコード

server.py

server.py に MCP サーバーを実装していきます。当記事では、シンプルな加算ツールと減算ツールのみ定義します。

fastmcp フレームワークでは、@mcp.tool デコレーターに関数を渡すことで、関数をツールとして登録することができます。

ツール 説明
add 数値 a と数値 b を足して返す
subtract 数値 a から数値 b を引いて返す

fastmcp は関数の型ヒント((a: int, b: int) の部分)とドキュメント文字列("""Use this to add two numbers together. ~ """ の部分)からツールの定義を自動生成して LLM に提供します。これにより、LLM が入力されたプロンプトに応じて適切なツールを選択できるようになっています。

# server.py
import asyncio
import logging
import os
  
from fastmcp import FastMCP 
  
  
logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)
  
  
mcp = FastMCP("MCP Server on Cloud Run")
  
  
@mcp.tool()
def add(a: int, b: int) -> int:
    """Use this to add two numbers together.
  
    Args:
        a: The first number.
        b: The second number.
  
    Returns:
        The sum of the two numbers.
    """
    logger.info(f">>> 🛠️ Tool: 'add' called with numbers '{a}' and '{b}'")
    return a + b
  
  
@mcp.tool()
def subtract(a: int, b: int) -> int:
    """Use this to subtract two numbers.
  
    Args:
        a: The first number.
        b: The second number.
  
    Returns:
        The difference of the two numbers.
    """
    logger.info(f">>> 🛠️ Tool: 'subtract' called with numbers '{a}' and '{b}'")
    return a - b
  
  
if __name__ == "__main__":
    logger.info(f"🚀 MCP server started on port {os.getenv('PORT', 8080)}")
    # Could also use 'sse' transport, host="0.0.0.0" required for Cloud Run.
    asyncio.run(
        mcp.run_async(
            transport="http",  # Streamable HTTP transport を使用する
            host="0.0.0.0",
            port=os.getenv("PORT", 8080),
        )
    )

Dockerfile

Dockerfile は uv を使用してビルドできるように、以下のように記述します。

# Use the official Python image
FROM python:3.13-slim
  
# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
  
# Install the project into /app
COPY . /app
WORKDIR /app
  
# Allow statements and log messages to immediately appear in the logs
ENV PYTHONUNBUFFERED=1
  
# Install dependencies
RUN uv sync
  
EXPOSE $PORT
  
# Run the FastMCP server
CMD ["uv", "run", "server.py"]

Cloud Run へのデプロイ

Artifact Registry リポジトリの準備

当記事では asia-northeast1 リージョンに各種 Google Cloud リソースを作成する前提で進めていきます。

Cloud Run にデプロイするコンテナイメージは Artifact Registry リポジトリに格納しておく必要があるため、リポジトリがない場合は以下のコマンドで作成します。

# Artifact Registry リポジトリを作成する
$ gcloud artifacts repositories create <リポジトリ名> \
    --repository-format=docker \
    --location=asia-northeast1

コンテナイメージの作成

MCP サーバーのコンテナイメージを作成していきます。

イメージ名を含む Artifact Registry リポジトリのパスを IMAGE_NAME シェル変数として定義します。当記事ではイメージ名を mcp-on-cloudrun としています。

# イメージ名(mcp-on-cloudrun)を含むリポジトリのパスを定義する
$ IMAGE_NAME="asia-northeast1-docker.pkg.dev/<プロジェクトID>/<リポジトリ名>/mcp-on-cloudrun:latest"

Cloud Build を使用してコンテナイメージをビルドします。コードと Dockerfile が配置されているディレクトリで以下のコマンドを実行します。

# コンテナイメージをビルドする
$ gcloud builds submit \
    --region asia-northeast1 \
    --tag $IMAGE_NAME

Cloud Run サービスの作成

作成したコンテナイメージを使用して、Cloud Run サービスを作成します。当記事ではサービス名を mcp-on-cloudrun とします。

# Cloud Run サービスを作成する
$ gcloud run deploy mcp-on-cloudrun \
    --image $IMAGE_NAME \
    --region asia-northeast1 \
    --no-allow-unauthenticated

--no-allow-unauthenticated オプションを指定することで、IAM で許可されているユーザーのみが Cloud Run 上の MCP サーバーにアクセスできるように制限します。

動作確認

認証について

Cloud Run 上のサービスにアクセスするための IAM ロール

当記事では、--no-allow-unauthenticated オプションを指定して、Cloud Run 上の MCP サーバーを非公開のサービスとして作成しました。

非公開の Cloud Run サービスにアクセスするためには、以下のいずれかの IAM ロールをユーザーに付与する必要があります。

  • Cloud Run 起動元(roles/run.invoker)
  • Cloud Run サービス起動元(roles/run.servicesInvoker)

上記以外にも、run.routes.invoke 権限を持つその他のロール(オーナーなど)であれば、サービスを呼び出すことができます。

非公開のサービスへのアクセス方法

Gemini CLI から非公開の Cloud Run サービスに直接アクセスするには、適切な権限を持つユーザーで以下のいずれかの方法を使用します。

  • gcloud run services proxy コマンドを使用して、ローカルホストで起動したプロキシ経由でアクセスする
  • ID トークンをリクエストヘッダーに含めて、Cloud Run のエンドポイントにアクセスする

どちらの手順もシンプルですが、プロキシと Cloud Run サービスは一対一となるので、接続する MCP サーバーが複数ある場合(Cloud Run サービスが複数ある場合)は ID トークンを使用する方法が便利です。

proxy コマンドを使用した認証

MCP クライアントの設定

まず、proxy コマンドを使用して Cloud Run の認証を行い、Gemini CLI から MCP サーバーに接続してみます。

Gemini CLI の構成ファイルとして、.gemini/settings.json を作成します。

proxy コマンドを使用して Cloud Run 上の MCP サーバーに接続する場合、ローカルホストで起動するプロキシ(http://localhost:8080)が Cloud Run へのリクエストをフォワーディングするため、構成ファイルを以下のように記述します。

{
    "mcpServers": {
        "cloud-run": {
            "httpUrl": "http://localhost:8080/mcp"
        }
    }
}

proxy コマンドの実行

以下のコマンドを実行し、Cloud Run に接続するためのプロキシをローカルホスト(http://localhost:8080)で起動します。

# ローカルホストに Cloud Run 接続用のプロキシを起動する
$ gcloud run services proxy mcp-on-cloudrun --region asia-northeast1

Gemini CLI の構成ファイルで MCP サーバーの URL を http://localhost:8080/mcp に設定しているため、このプロキシを経由して Cloud Run 上の MCP サーバーに接続することができます。

gemini CLI で MCP サーバーに接続

Gemini CLI を起動して、MCP サーバーに接続してみます。

# Gemini CLI を実行する
$ gemini

Gemini CLI 起動時に以下のエラーが表示された場合、MCP サーバーへの接続に失敗しています。Gemini CLI の構成ファイルに記述ミスがあるか、ソースコードや Dockerfile の問題でサービスが正しく実行されていない可能性があります。各種ファイルや Cloud Run のログを確認してみてください。

✕ Error during discovery for server 'cloud-run': Connection failed for 'cloud-run': fetch failed

Gemini CLI を起動したら、以下のコマンドをプロンプトに入力して実行します。

# プロンプトに入力する
/mcp list

MCP サーバーに接続できている場合、使用できるツールのリストが表示されます。

> /mcp list

Configured MCP servers:

🟢 cloud-run - Ready (2 tools)
  Tools:
  - add
  - subtract

プロンプトに足し算の問題を入力し、送信してみます。Gemini CLI は、ソースコードに記述した型ヒントやドキュメント文字列から、適切な MCP サーバーを自動的に選択して呼び出します。

# プロンプトに入力する
1 + 2 = ?

MCP サーバーを初めて使用する場合、ツールの使用を許可するか確認する、以下のようなメッセージが表示されます。

> 1 + 2 = ?

╭──────────────────────────────────────────────────────────────────────────╮
│ ?  add (cloud-run MCP Server) {"a":1,"b":2}                            ← │
│                                                                          │
│ MCP Server: cloud-run                                                    │
│ Tool: add                                                                │
│                                                                          │
│ Allow execution of MCP tool "add" from server "cloud-run"?               │
│                                                                          │
│ ● 1. Yes, allow once                                                     │
│   2. Yes, always allow tool "add" from server "cloud-run"                │
│   3. Yes, always allow all tools from server "cloud-run"                 │
│   4. No, suggest changes (esc)                                           │
│                                                                          │
╰──────────────────────────────────────────────────────────────────────────╯
⠏ Waiting for user confirmation...

今回は MCP サーバーに実装した2つのツールのうち、add ツールが使用されます。ツール実行を一回限りで許可したい場合は 1 を、add ツールの使用を常に許可したい場合は 2 を、この MCP サーバーのすべてのツールの使用を常に許可したい場合は 3 を選択します。

4 以外を選択すると、MCP サーバーに実装した add ツールによる計算結果が返り、それを解釈した Gemini による回答が表示されます。

> 1 + 2 = ?

╭──────────────────────────────────────────────────────────────────────────╮
│ ✓  add (cloud-run MCP Server) {"a":1,"b":2}                              │
│                                                                          │
│ 3                                                                        │
╰──────────────────────────────────────────────────────────────────────────╯
✦ The answer is 3.

ID トークンによる認証

MCP クライアントの設定

次に、ID トークンによる認証を行って Cloud Run 上の MCP サーバーに接続してみます。

Gemini CLI の構成ファイル(.gemini/settings.json)は以下のように記述します。

{
    "mcpServers": {
        "cloud-run": {
            "httpUrl": "https://<Cloud Run の URL>/mcp",
            "headers": {
                "Authorization": "Bearer $ID_TOKEN"
            }
        }
    }
}

<Cloud Run の URL> は、以下のコマンドで取得したものに置き換えてください。

# Cloud Run の URL を取得する
$ gcloud run services describe mcp-on-cloudrun \
    --region asia-northeast1 \
    --format='value(status.url)'

gemini CLI で MCP サーバーに接続

まず、ID トークンを取得して環境変数に設定します。これにより、Gemini CLI 起動時に .gemini/settings.json に記述した $ID_TOKEN が、コマンドで取得したトークンに置き換えられます。

# ID トークンを取得して環境変数に設定する
$ export ID_TOKEN=$(gcloud auth print-identity-token)

次に、Gemini CLI を起動します。

# Gemini CLI を実行する
$ gemini

Gemini CLI のプロンプトから /mcp list を実行し、MCP サーバーのツールが表示されれば、Cloud Run 上の MCP サーバーに接続できています。

> /mcp list

Configured MCP servers:

🟢 cloud-run - Ready (2 tools)
  Tools:
  - add
  - subtract

引き算のツールを試してみます。今回は計算式ではなく、算数の問題のような文章をプロンプトに入力して送信します。

木にりんごが5つなっています。強い風が吹き、りんごが3つ落ちてしまいました。木に残っているりんごは何個ですか?

MCP サーバーに実装した subtract ツールによる計算結果が返り、それを解釈した Gemini による回答が表示されます。

> 木にりんごが5つなっています。強い風が吹き、りんごが3つ落ちてしまいました。
  木に残っているりんごは何個ですか?

╭──────────────────────────────────────────────────────────────────────────╮
│ ✓  subtract (cloud-run MCP Server) {"a":5,"b":3}                         │
│                                                                          │
│ 2                                                                        │
╰──────────────────────────────────────────────────────────────────────────╯
✦ 木に残っているりんごは2個です。

佐々木 駿太 (記事一覧)

G-gen最北端、北海道在住のクラウドソリューション部エンジニア

2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer に選出(2024 / 2025 Fellow / 2026)。好きなGoogle CloudプロダクトはCloud Run。

趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。