<style> .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { text-transform: none; } .reveal .slides { width: 80%; height: 80%; margin: auto; } .reveal .slides section { width: 100%; height: 100%; text-align: left; border-width: 1px; border-color: #ffffff; } .reveal .slides section img { width: 100%; margin-left: auto; margin-right: auto; object-fit: contain; } .reveal .slides pre code { white-space: pre-wrap; max-height: 70%; } .reveal .slides section.title-slide { h1 { font-size: 5rem; } display: grid; place-content: center; text-align: left; } .reveal .slides section.center { display: grid; place-content: center; text-align: center; } </style> <!-- .slide: class="title-slide" --> # Cloud RunでのリモートMCPサーバー構築と、<br>それらを支えるIaCのお話 <br><br><br> 2025年10月15日<br> 太田有人 (sakai-nako) - Jagu'e'r 関西分科会<br> Azure x AWS x Google Cloud コミュニティ本音トーク! notes: 00:30 --- ## 自己紹介 <div style="display: flex; align-items: flex-start; gap: 2rem;"> <div style="flex: 1.2; text-align: center;"> <h3>太田有人(sakai-nako)</h3> <img src="/images/profile_composite_01.jpg" alt="太田有人 (sakai-nako)" style="width: 100%; max-width: 600px; border-radius: 8px;"> <div style="margin-top: 1rem;"> <img src="/images/professional-cloud-architect.png" alt="Google Cloud Certified - Professional Cloud Architect" style="width: 110px; margin: 5px;"> <img src="/images/professional-cloud-developer.png" alt="Google Cloud Certified - Professional Cloud Developer" style="width: 110px; margin: 5px;"> <img src="/images/professional-cloud-devops-engineer.png" alt="Google Cloud Certified - Professional Cloud DevOps Engineer" style="width: 110px; margin: 5px;"> </div> </div> <div style="flex: 2; font-size: 2.4rem;"> <ul> <li>1987年生まれ ずっと京の都に住んでいる</li> <li>コンビニバイト10年の後、IT業界に飛び込む(現在8年目)</li> <li>SES企業:システムエンジニア(2017~2021)<br>↓<br>フェンリル:クラウドエンジニアin <a href="https://www.fenrir-inc.com/jp/business/gimle/">GIMLE</a> Team<br>(2022~2024/6)<br>↓<br>フェンリル:インフラエンジニアin <a href="https://www.nilto.com/ja">NILTO</a> Team<br>(2024/7~)</li> <li>「理想の彼女がいなければ、自分で作ればいいじゃない」と思い立ち、女装を始めて4年くらい</li> <li>最近はRustにハマリ気味</li> <li style="font-weight: bold;">Jagu'e'r 関西分科会 運営</li> <li><a href="https://hack-pleasantness.com/">個人サイト(リニューアル進行中……)</a></li> </ul> </div> </div> notes: 01:00 / 01:30 --- ## Jagu'e'r 関西分科会について - [Jagu'e'r](https://jaguer.jp/#about)は、Google Cloud や Google Workspace の公式ユーザーグループ - 特定のテーマごとにたくさんの分科会があり、それぞれの分科会で多様なイベント・勉強会などが開催 - 関西分科会では、地域分科会ならではの対面の交流を通じた情報収集 - 3ヶ月に1回集まってます <img src="/images/jagu'e'r-kansai-01.png" alt="" style="height: 60%;"> notes: 01:00 / 02:30 --- ## おしながき - MCP(Model Context Protocol)とは? - Cloud RunでのMCP構築 - IaC (Terraform/Terragrunt) で支えるインフラ - 実際の使い方 - まとめ notes: 00:30 / 03:00 --- ## MCP(Model Context Protocol)とは? - **LLM開発を支援するためのオープンなプロトコル** - Anthropic社が中心となって提唱され、現在はオープンソースとして開発が進んでいる - **AIエージェント(モデル)と外部ツール間の連携をスムーズに** - モデルが外部の知識や機能を「ツール」として呼び出すための標準的なやり取りを定義 - 例: コード補完、APIドキュメントの参照、社内データベースへの問い合わせなど - **モデルの「グラウンディング」を実現** - モデルの回答を、特定のプロジェクトやリアルタイムのデータといった、より具体的な情報に基づかせる(グラウンディングする)ことが可能に notes: 02:00 / 05:00 はい、ではまず、今回のテーマの根幹である「MCP」、Model Context Protocolについて簡単にご説明します。 MCPは、一言で言うと、LLM/AIモデルが外部のツールと対話するための「共通言語」のようなものです。Anthropic社が中心となって提唱されたオープンなプロトコルで、現在はオープンソースとして開発が進んでいます。 みなさんがChatGPTやGeminiを使うとき、AIは学習済みの膨大な知識の中から回答を生成しますよね。ですが、例えば「うちの会社の最新の売上データを教えて」とか、「このプロジェクトで使っている〇〇関数の使い方を教えて」と聞いても、AIは答えられません。なぜなら、AIはその「外部」の情報を知らないからです。 MCPは、この問題を解決します。モデルが「わからないこと」や「やるべきこと」に直面したとき、MCPというルールに則って、外部のAPIやデータベースといった「ツール」に問い合わせを行うことができます。ツールからの返答を受け取ることで、モデルはより正確で、文脈に沿った回答を生成できるようになります。これを「グラウンディング」と呼びます。 例えば、コードエディタでGemini Code Assistを使っていると、プロジェクト内のコードについて質問に答えてくれますよね。あれもMCPの仕組みを利用して、モデルがローカルのコード情報を参照しているんです。 今回は、このMCPの仕組みを利用して、私たちが開発しているヘッドレスCMS「NILTO」を操作するためのMCPサーバーを、Cloud Run上に構築したお話をさせていただきます。 --- ## Cloud RunでのMCP構築 - 自社開発のSaaS(ヘッドレスCMS NILTO)で、Cloud RunでMCPサーバーを構築 - [現在テクニカルプレビュー提供中](https://www.nilto.com/ja/lp/mcp) <img src="/images/mcp-architecture-02.png" alt="" style="height: 60%;"> notes: 01:30 / 06:30 さて、先ほどご説明したMCPの仕組みを使って、私たちが実際にどのようなものを構築したのか、そのアーキテクチャの概要からお話しします。 こちらが、今回構築したリモートMCPサーバーの簡単な構成図です。 ユーザーは、GeminiのCLIや、Google Cloudの各種アプリケーションといったAIエージェントと対話します。例えば、「NILTOに新しいブログ記事を入稿して」といった指示を出すわけですね。 その指示を受け取ったAIエージェントは、私たちのヘッドレスCMS「NILTO」を操作するための知識を持っていないので、設定されたMCPサーバーに問い合わせを行います。 MCPサーバーは、AIエージェントからのリクエストを解釈し、NILTOのバックエンドAPIが理解できる形に変換してリクエストを送信します。そして、APIからのレスポンスを今度はAIエージェントが理解できる形式で返却します。 これにより、AIエージェントは、あたかも最初からNILTOの操作方法を知っていたかのように振る舞うことができ、ユーザーとの対話を通じてコンテンツの作成や管理といった操作を実行できるようになります。 今日はヘッドレスCMSとは何か、というところの説明までは時間が足りないのですが、もしヘッドレスCMS使ってるよ!という方がいれば、テクニカルプレビューとしてこの機能を提供しているので、よかったら見てみてください。 --- ## Cloud RunでのMCP構築 ### 使用した技術スタック - **FastMCP**: MCPサーバー構築フレームワーク - **Uvicorn**: ASGIサーバー - **Python**: メイン言語 - **Docker**: コンテナ化 中身はPython製のサーバーアプリなので、コンテナ化してCloud Runで簡単に動かせる! notes: 01:00 / 07:30 このMCPサーバーを構築するにあたって使用した技術スタックは、こちらの通りです。 メインの言語はPythonで、MCPサーバーを構築するためのフレームワークとして「FastMCP」というライブラリを採用しました。これは、PythonのWebフレームワークであるFastAPIにインスパイアされたもので、非常に直感的にMCPのツールを定義できます。 サーバー自体はUvicornというASGIサーバー上で動作します。これらをまるっとDockerコンテナにパッケージングして、Cloud Run上で動かしています。 このように、中身はPython製のWebアプリケーションとほぼ同じ構成ですので、特別な知識を必要とせず、非常に簡単にコンテナ化してCloud Runのようなマネージドサービス上で動かすことができました。 --- ## Cloud RunでのMCP構築 ### AIエージェントが利用する"ツール"の実装 ※コード例であり、実際に動いているコードとは異なります。 ```python [all] @mcp.tool( name="nilto_create_content", title="NILTO CMSにコンテンツの入稿するMCPツール", description="NILTO CMSの特定のスペースに対して、指定したモデルでコンテンツを作成するMCPツール。パラメータとして指定されているspace_idとmodel_luid, publishedなどは...", ) async def nilto_create_content( ctx: Context, field_values: Dict[str, Any], space_id=None, # ... ) -> Any: # ...Backendにリクエストを送る処理を実装 ``` notes: 01:30 / 09:00 では、実際にAIエージェントが利用する「ツール」をどのように実装するのか、コードを見ていきましょう。これはFastMCPを使った実装例です。 `@mcp.tool` というデコレータを使って関数を定義するだけで、AIエージェントから利用可能なツールとして登録できます。`name`や`title`、そして`description`といった引数で、このツールが何をするためのものなのか、AIが理解できるように説明を記述します。このdescriptionの内容が、AIがツールを適切に選択するための非常に重要な情報となります。 関数`nilto_create_content`の引数には、AIエージェントから渡される様々なコンテキスト情報やパラメータが定義されています。ちょっとNILTO特有の言葉を使っているので、一旦ここはAIエージェントから色々情報をもらっているんだな〜とだけ思っておいてもらえれば。 そして、この関数の中で、受け取った情報を元にNILTOのバックエンドAPIを呼び出す処理を実装します。こうすることで、AIエージェントは自然言語での指示を、この`nilto_create_content`ツールを介してNILTOのAPI操作に変換できるようになるわけです。 --- ## Cloud RunでのMCP構築 ### 認証の仕組み - 現状、MCPサーバーそのものに対する認証はなし - NILTOで発行したMCPアクセストークンで認証・認可 - 正式版ではOAuth認証の導入を予定 notes: 01:00 / 10:00 認証の仕組みについても触れておきます。 現状のテクニカルプレビュー版では、MCPサーバー自体には認証機能を設けていません。その代わり、NILTOの管理画面から発行できる専用の「MCPアクセストークン」をAIエージェントに設定していただき、そのトークンを使ってNILTO側で認証・認可を行う、というシンプルな形を取っています。 これにより、まずは手軽に試していただけるようになっています。将来的には、よりセキュアで柔軟な連携を実現するために、正式版としてリリースする際にはOAuth認証の導入を予定しています。 --- ## IaC (Terraform/Terragrunt) で支えるインフラ ### インフラ構成図 <img src="/images/mcp-architecture-01.png" alt="" style="height: 60%;"> notes: 01:30 / 11:30 さて、ここからは少し視点を変えて、このMCPサーバーを支えるインフラ、特にIaC、Infrastructure as Codeについてのお話をします。 こちらが、先ほどのアーキテクチャ図に、Google Cloudの具体的なサービス名を加えたインフラ構成図です。 ユーザーからのリクエストは、まずCloud Load Balancingで受けます。ここで、Cloud ArmorによるWAFの保護や、SSL証明書の処理が行われます。そして、リクエストはバックエンドサービスであるCloud Run上のMCPサーバー本体へと転送されます。DNSレコードはもちろんCloud DNSで管理しています。 これらのリソース群は、すべてTerraformとTerragruntによってコードで管理されています。この構成によって、迅速かつ安全なインフラの展開と更新ができていると感じています。次のスライドで、具体的にどのようなリソースをコード化したかを見ていきましょう。 --- ## IaC (Terraform/Terragrunt) で支えるインフラ ### 構築したリソースたち - **CI/CD**: Cloud Buildトリガー - **コンピューティング**: Cloud Runサービス - **ネットワーク・セキュリティ**: - グローバルIP - Cloud Armor (WAF) - Cloud Load Balancing - DNSレコード - 証明書関連リソース notes: 01:00 / 12:30 こちらが、先ほどの構成図を実現するために、Terraformでコード化した主なリソースの一覧です。 CI/CDのトリガーとなるCloud Build、アプリケーション本体であるCloud Runサービスはもちろんのこと、外部にサービスを公開するために必要なグローバルIPやロードバランサ、WAFであるCloud Armor、そして証明書関連のリソースまで、すべてをコードで定義しています。 これらのリソースを「MCPサーバー用モジュール」として一つにまとめることで、新しい環境への展開や、既存環境の変更が、誰でも・安全に・再現性を持って行えるようになっています。 とは言っても、今はインフラ担当のエンジニアがわたし含めて2人だけなので、再現性よりはどちらかというとスピードを重視してはいます。 --- ## IaC (Terraform/Terragrunt) で支えるインフラ ### フォルダ構成 - Terraform/Terragruntでインフラをコード化 - 環境ごとにフォルダを分け、共通する値をTerragrunt用のhclファイルで管理 - Terraform側にはVariableの値として渡す - さらなる再利用性を目指して、Terragruntの[Explicit Stacks](https://terragrunt.gruntwork.io/docs/features/stacks/#explicit-stacks-blueprint-based-generation)への移行も検討中 ```sh └── environments ├── develop │ ├── mcp ------------------ 追加したMCPのモジュール │ │ ├── main.tf --------- 各種リソースの定義 │ │ └── terragrunt.hcl -- モジュール固有の値を定義 │ ├── ... │ └── develop.hcl ---------- 環境ごとに共通な値を管理 │ ├── production │ ├── mcp │ ├── ... │ └── production.hcl │ └── staging ├── mcp ├── ... └── staging.hcl ``` notes: 01:30 / 14:00 はい、実際のインフラのコードは、このようなディレクトリ構成で管理しています。 `environments`ディレクトリ以下に`develop`、`staging`、`production`といった環境ごとのディレクトリを分けています。 各環境ディレクトリの中には、今回追加した`mcp`のような機能ごとのモジュールディレクトリがあり、その中の`terragrunt.hcl`で、例えば「本番環境のMCPサーバーに割り当てるCPUの数」といった、その環境固有の値を定義しています。そして、`develop.hcl`のような環境全体で共通の値を持つファイルも用意し、コードの重複を減らす工夫をしています。 これにより、環境間の差異を明確に管理しつつ、 DRY、つまりDon't Repeat Yourselfの原則を守った効率的なインフラ管理を実現しています。今後は、TerragruntのStacks機能の活用も検討しており、さらなる運用の改善を目指しています。 --- ## 実際の使い方 - Gemini CLIやClaude Desktop、GitHub Copilot CLIなどから利用可能 - 設定ファイルにいくつかの情報を追記するだけ - コマンド - URL - MCPアクセストークン Gemini CLIの設定例 <img src="/images/nilto-mcp-gemini-cli-config-01.png" alt="" style="height: 60%;"> notes: 01:00 / 15:00 では、実際にこのMCPサーバーをどのように使うのか、簡単にご紹介します。 Gemini CLIやClaudeのデスクトップアプリなど、主要なAIエージェントから利用することができます。使い方は非常にシンプルで、各エージェントの設定ファイルに、私たちが提供するMCPサーバーのURLや、先ほどお話ししたMCPアクセストークンなどの情報を追記するだけです。 こちらに表示しているのはGemini CLIの設定例ですが、このように数行書き加えるだけで、いつものAIエージェントがNILTOを操作する能力を獲得します。 --- <!-- .slide: class="center" --> # 時間があれば、デモやります! notes: 02:00 / 17:00 はい、ありがとうございます。 もしこの後、お時間に余裕があれば、実際にこのMCPサーバーを使って、AIエージェントと対話しながらNILTOにコンテンツを入稿するデモンストレーションをお見せしたいと思います。 --- ## まとめ - **Cloud Run** を活用し、迅速にMCPサーバーを構築 - **IaC (Terraform/Terragrunt)** で、スピード感もありつつ、再現性と保守性の高いインフラを維持 - 今後の展望 - 認証強化(OAuth) - さらなる機能拡張とオブザーバビリティ強化 - Cloud Runのサービスも増えてきたので、GKEがそのうち視野に入ってくるかな…… - **Jagu'e'r** での知見共有と、コミュニティへの貢献 notes: 02:00 / 19:00 最後に、本日の発表のまとめです。 本日は、私たちのSaaS「NILTO」の機能をAIエージェントから利用可能にするための、リモートMCPサーバー構築の事例について、Google Cloudの活用を中心にお話ししました。 今回のプロジェクトでは、コンテナをデプロイするだけでスケーラブルな環境が手に入る **Cloud Run** を全面的に採用しました。これにより、私たちのような少人数のチームでもインフラの管理コストを最小限に抑え、新しい機能のPoCから実装までを非常に高速に進めることができました。 そして、そのインフラはTerraformとTerragruntを用いて完全にコード化されており、再現性と保守性の高い状態を保っています。 今後の展望としては、OAuthによる認証強化や、オブザーバビリティの向上などを進めていきます。そして何より、今回得られた知見は、ぜひ **Jagu'e'r** のようなコミュニティで皆さんと共有し、議論を深めていきたいと考えています。Google Cloudには、今日お話しした以外にもたくさんの魅力的なサービスがあります。ぜひ皆さんと一緒に学び、日本のクラウド活用を盛り上げていきたいです。 本日の話が、皆さんのプロダクト開発における現実的な次の一歩を考える上での、何かしらのヒントになれば幸いです。この後、懇親会などでもぜひ気軽にお声がけください。 --- <!-- .slide: class="center" --> # ご清聴ありがとうございました