はじめに
AI Agentの時代が本格的に到来していますが、開発者の皆さんはこんな悩みを抱えていませんか?
「Gemini、Claudeなどの優秀なAIモデルがあるのに、自社のデータベースやAPIと連携させるのが面倒…」 「カスタムツールを作っても、複数のAIプラットフォームで使い回せない…」 「セキュアで本格的な外部連携を実装するには、認証やらインフラやら考えることが多すぎる…」
そんな課題を一気に解決してくれるのがMCP (Model Context Protocol) です。一度構築すれば、複数のAIプラットフォームから同じツールを利用でき、しかもCloud Runを使えばスケーラブルで運用も楽々。今回は、実際にCloud Run上でMCP Serverを構築し、Google Chatにメッセージを送信する手順を詳しく解説します。
対象読者
- AI AgentやLLMアプリケーションの開発に興味がある方 外部APIやデータベースとAIを連携させたい方
- Google Cloud(特にCloud Run)の基本的な操作ができる方
- Docker コンテナとPythonの基礎知識がある方
- 「AIは使えるけど、本格的なツール連携はまだ…」という方
- Google Chatを業務で使っていて、AIとの連携に興味がある方
- 「AIに通知業務を任せられたら楽になりそう」と思っている方
MCPとは
MCP(Model Context Protocol)は、AIモデル(例: Gemini, Claude)が外部のデータソースやツールと連携するための標準化されたプロトコルです。これにより、LLMが最新の情報や外部サービスを効率的に利用できるようになり、開発の効率化やAIの活用の幅を広げることが期待されています。
なぜMCPが必要なのか?
従来のLLM(大規模言語モデル)は、学習データに基づいてテキストを生成するだけで、外部のデータやツールを直接操作することができませんでした。そのため、例えばチャットにメッセージを送信したり、カレンダーを操作したりするためには、個別のAPIを実装する必要があり、開発の複雑さが増していました。
MCPは、これらの問題を解決するために登場しました。AIが外部のツールやデータにアクセスするための共通のインターフェースを提供することで、開発者はMCPに対応したツールであれば、容易にAIに連携させることができます。
今回作るもの:Google Chat連携MCP Server
本記事では、Google Chatにメッセージを送信するMCP Serverを実際に構築します。

実装する機能
Google Chat Webhook連携
- チャットスペースへのメッセージ送信
MCP Server実装
- Google Chat送信ツールの定義
構築手順
Google Chatのスペースを作成
MCP Serverからの通知用スペースを作成します。
- 「新しいチャット」→「スペースを作成」を押下

- 任意のスペース名(今回はmcp-demo)を入力し、「作成」を押下

これでスペースが作成できました!ここからはWebhookの設定をしていきます。
- スペース名の右側の「v」を押下後に表示される「アプリと統合」を押下

- 「+ Webhookを追加」を押下

- 任意の名称(今回はmcp-demo)を入力し「保存」を押下

- Webhookの設定が完了しました!

※Webhook URLは動作確認の際に必要になるので、控えておいてください!
MCP Server実装
- ディレクトリ構成
mcp-server
├── Dockerfile
├── gws
│ ├── __init__.py
│ ├── clients
│ │ ├── __init__.py
│ │ └── chat.py
│ ├── models
│ │ ├── __init__.py
│ │ └── responses.py
│ └── tools
│ ├── __init__.py
│ └── chat_message.py
├── pyproject.toml
├── server.py
├── tests
│ ├── http_client.py
│ └── model.py
└── uv.lock
- ライブラリインストール
uv add fastmcp httpx pydantic requests
- gws/clinets/chat.py: チャットスペースのWebhookを使用しメッセージを送信するclass
import requests
class WebhookChatClient:
def __init__(self, webhook_url: str):
self.webhook_url = webhook_url
def send_message(self, text: str):
try:
payload = {"text": text}
response = requests.post(
self.webhook_url,
json=payload,
headers={"Content-Type": "application/json"},
)
if response.status_code == 200:
print("メッセージが正常に送信されました")
return True
else:
print(f"送信エラー: {response.status_code} - {response.text}")
return False
except Exception as e:
print(f"送信エラー: {e}")
return False
- gws/models/responses.py: MCP Serverが返却するメッセージのmodel
from typing import Optional, Any
from pydantic import BaseModel, Field
class MCPToolResponse(BaseModel):
"""MCP tool response format"""
success: bool = Field(description="Tool execution success")
message: Optional[str] = Field(default=None, description="Response message")
sent_payload: Optional[dict[str, Any]] = Field(
default=None, description="Request payload sent"
)
error: Optional[str] = Field(default=None, description="Error details")
- tools/chat_message.py: WebhookでGWSチャットにメッセージを送信するMCPツール登録関数
from typing import Annotated
from fastmcp import FastMCP
from pydantic import Field
from gws.clients.chat import WebhookChatClient
from gws.models.responses import MCPToolResponse
def register_gws_chat_tools(mcp: FastMCP) -> None:
@mcp.tool(tags={"public", "gws", "chat"})
async def send_gws_chat_message(
webhook_url: Annotated[str, Field(description="送信先WebhookURL")],
message: Annotated[str, Field(description="送信メッセージ")],
) -> MCPToolResponse:
try:
client = WebhookChatClient(webhook_url)
client.send_message(message)
return MCPToolResponse(
success=True,
message="The message was successfully sent.",
sent_payload={
"webhookUrl": webhook_url,
"message": message,
},
)
except Exception as e:
print(f"Exception occurred while sending message: {str(e)}")
return MCPToolResponse(
success=False,
error=f"送信エラー: {str(e)}",
sent_payload={
"webhookUrl": webhook_url,
"message": message,
},
)
- server.py: MCP Server起動スクリプト
import os
import json
from fastmcp import FastMCP
from gws.tools.chat_message import register_gws_chat_tools
def pydantic_object_serializer(data):
return json.dumps(data.model_dump(), ensure_ascii=False, indent=2)
def create_server() -> FastMCP:
mcp = FastMCP(
name="MCPServer",
include_tags={"public"},
on_duplicate_tools="error",
on_duplicate_resources="warn",
on_duplicate_prompts="replace",
tool_serializer=pydantic_object_serializer,
)
register_gws_chat_tools(mcp)
return mcp
def main() -> None:
print("Starting Message Sender MCP Server")
mcp = create_server()
mcp.run(
transport="streamable-http",
host=os.getenv("HOST", "0.0.0.0"),
port=int(os.getenv("PORT", "9001")),
)
if __name__ == "__main__":
main()
Cloud Runへデプロイ
- ここからは、Google CloudのCloud RunにMCP Serverをデプロイしていきます。先ずは、MCP ServerのDocker Imageを格納するArtifact Registoryのリポジトリを作成していきます。

- 下記情報を入力&設定し、作成ボタンを押下
名前: 任意の名称
形式: Docker
リージョン: 任意のリージョン

- MCP ServerのDocker Imageを保管するリポジトリが完成しました!

MCP ServerのDocker Imageをbuild
- Dockefile
# Build stage
FROM python:3.13-slim-bookworm as builder
WORKDIR /build
COPY pyproject.toml uv.lock .python-version ./
# Install build dependencies
RUN apt-get update && apt-get upgrade -y && \
apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
pkg-config \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install uv and generate requirements.txt
RUN pip install --no-cache-dir uv && \
uv pip compile pyproject.toml -o requirements.txt
# Install dependencies to be copied to the final image
RUN pip install --no-cache-dir --no-warn-script-location --user -r requirements.txt
# Final stage
FROM python:3.13-slim-bookworm
# Set environment variables
ENV PYTHONUNBUFFERED=True
ENV APP_HOME=/app
WORKDIR $APP_HOME
# Copy Python packages and scripts from builder
COPY --from=builder /root/.local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /root/.local/bin /usr/local/bin
# ソースコードコピー
COPY gws/ ./gws/
COPY server.py ./
EXPOSE 8080
CMD ["python", "server.py"]
- gcloudを使用してimage build
gcloud builds submit --region=asia-northeast1 --tag asia-northeast1-docker.pkg.dev/[ProjectID]/mcp-demo/mcp-server:0.0.1
- コマンドが正常終了したことを確認します。

- 先ほど作成したArtifact Registoryのリポジトリにもしっかり登録されています!

Cloud Runにデプロイ
- gcloudを使用し、Artifact Registoryに登録したDocker ImageをCloud Runにデプロイします。
gcloud run deploy mcp-server --image asia-northeast1-docker.pkg.dev/[Project ID]/mcp-demo/mcp-server:0.0.1 --region asia-northeast1 --platform managed --allow-unauthenticated
- Cloud RunにMCP Serverをデプロイできました。
※今回は検証のため、「未認証を許可」でデプロイしましたが、本番では認証が必要な設定でデプロイしましょう!

動作確認
- tests/http_clinet.py: MCP Serverにメッセージを送信するツール
import asyncio
from fastmcp import Client
async def test_http_mcp_tool():
"""Streamable HTTP MCPサーバー経由でツールをテスト"""
# パラメータ
webhook_url = "[チャットスペースに追加したWebhook URL]"
message = "hello mcp"
print("HTTP MCPクライアントでGWSメッセージ送信テストを開始...")
print(f"Message: {message}")
print("-" * 50)
client = Client("[デプロイしたCloud RunのURL]/mcp/")
# client = Client("http://127.0.0.1:9001/mcp")
try:
async with client:
# MCPサーバで提供されているツール一覧の取得
tools = await client.list_tools()
print(f"Available tools: {tools}")
# ツール呼び出し
results = await client.call_tool(
"send_gws_chat_message",
{"webhook_url": webhook_url, "message": message},
)
for result in results:
print("---")
print(result)
except Exception as e:
print(f"fastmcp.Client エラー: {e}")
print(f"エラータイプ: {type(e)}")
if __name__ == "__main__":
asyncio.run(test_http_mcp_tool())
- メッセージ送信ツール実行
python tests/http_client.py
- 結果確認
## ツール実行結果
type='text'
text='{
"success": true,
"message": "The message was successfully sent.",
"sent_payload": {
"webhookUrl": "[実際のWebhook URL]",
"message": "hello mcp"
},
"error": null
}'
annotations=None
- Cloud Run(MCP Sever)ログ

- Google Chatスペース

Google Chatのスペースにメッセージが投稿されました!
Cloud RunにデプロイしたMCP Serverも動作していることが確認できましたね!
まとめ
Cloud Run上でのMCP Server構築は、AI時代の外部連携課題を解決する強力なソリューションです。
今回の構築で得られるメリット
- 複数AIプラットフォーム対応の統一ツール
- スケーラブルなインフラ
- 運用コストの最小化
- 将来的な拡張性の確保
今後検討が必要な課題
- ClientとCloud Run間の認証
- Cloud RunとGoogle Chat認証
MCPはまだ発展途上の技術ですが、その可能性は計り知れません。今のうちに基礎を固めておくことで、AIファーストな開発体制の構築において大きなアドバンテージを得られるでしょう。
皆さんもぜひ、この機会にMCPの世界に飛び込んでみてください!