夏休みに親の地位を向上させた話
AI/ML がチョットデキル自動車製造ソリューションアーキテクトの呉です。Builder の皆様いかがおすごしでしょうか。
うだるような暑い夏でしたね。案の定ウチの子どもは宿題もやらずにずっと遊んでいました。しかし子どもに宿題をやれと叫んで体力を消耗するだけでなく、来る(きたる)反抗期に備えて余計な恨みを買わないというのも大事でしょう。私は子供と残された限りある時間一緒に遊ぶ選択肢を取りました。子供と遊ぶにしても普段からナメられている私などにとって、子供と遊ぶにしても親の凄さを見せつけてマウンティングしなければなりません。でなければ反抗期にバカにされてしまいます。しかし、子供がよくやるゲームにおいて、私が子供より上手くやるのも難しいところです。ただし我らは Builder 、IT を使わせたら子供に負けるはずなどありません(※よく負けます)。
ということで、IT を使って子供の遊びに勝ちにいきました。今回は Minecraft で子供に「こんなことできるの!?すごい!」と言われるために私がスクラッチで開発した MCP (Model Context Protocol) - MCP (Minecraft Play) を皆様に紹介いたします。この記事を読めばこんなことができるようになります。
デモ動画
LLM のレスポンスがやや遅いので倍速再生していますが、人間の入力は「高さ 1 以上の部分を Air で埋めて、高さ 0 以下のところを grass で埋めて更地にして。」「巨大でかっこいいカラフルなピラミッドを作って。素材には TNT(※) を多く散りばめて。」という 2 つのプロンプトだけで平地を作りその上にカラフルなピラミッドができあがりました。
※TNT を AI が発火する術(すべ = 火打ち石)を持っていないのでこの作業はとても安全です(超重要)。そして TNT を散りばめておくことで、AI がイマイチな構造物を作ったとしても、人間が安全を確認した上発破をかけられるのでとても便利です。
みなさまもこれを導入したら、子どもから羨望の眼差しを受けること間違いなしです!!
ご注意
本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。
builders.flash メールメンバー登録
builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。
MCP (Model Context Protocol) - MCP(Minecraft Play) ってなに?
これは私が勝手に命名したものですが一応説明を入れると Strands Agents という AI Agent のパッケージ(ライブラリ)と MCP(Model Context Protocol) を使って MCP(Minecraft Play) するというものです。
Minecraft とは?
「『Minecraft』(マインクラフト)は、2011年に発売されたサンドボックスビデオゲームであり、マルクス・ペルソン(Notch)によって開発された。日本国内では「マイクラ」と略される。」(Wikipedia より)
サンドボックスとあるようにいろんなものを作ったりすることができる Creative Mode や、生き残りをかけた Survival Mode があるゲームです。そして、一部のプラットフォームでは API で Minecraft を操作できます。つまり Builder に向いたゲームなのです。今回は Creative Mode を使って遊びます。
MCP とは?
MCP は、アプリケーションが LLM に利用可能なツールや使用方法などのコンテキストを提供する方法を標準化するオープンプロトコルです。MCP は AI アプリケーション向けの USB-C ポートのようなものと考えてください。USB-C がデバイスをさまざまな周辺機器やアクセサリーに接続するための標準化された方法を提供するように、MCP は AI モデルを異なるデータソースやツールに接続するための標準化された方法を提供します。(MCP 公式ドキュメントの Introduction を生成 AI で和訳)
上記紹介は私にはピンとこなかったのですが、LLM が外部世界とつながるために Tool Use (あるいは Function Calling と呼んだり)という仕組みを使って、各 API の仕様に合わせて使用すべきツールの選定及び引数を抽出し、それに合わせてツール側の実装を必要に応じて行っていました。対して MCP を使うと、MCP サーバー(LLM にツールやリソースを提供する側)の開発者は、その規格に則って開発するだけでサーバーの利用者に Interface について相談することなく使ってもらえる(e.g. AWS MCP Servers)他、その規格に則ったライブラリがあるため開発が容易になりました。MCP クライアント(用意されたツールなどを利用する)側は、すでに MCP サーバーに対応したクライアントが多数ある(e.g. Amazon Q Developer CLI, Claude for desktop)ので、サーバーをアタッチするだけで使える他、MCP クライアントを実装する際もライブラリが用意されているので Client の実装も容易です(e.g. Amazon Bedrock を用いた Client 実装)。
Strands Agents とは?
Strands Agents は、わずか数行のコードで AI エージェントを構築・実行するモデル駆動型アプローチを採用したオープンソース SDK です。Strands は、シンプルなエージェントのユースケースから複雑なものまで、そしてローカル開発から本番環境でのデプロイまで対応します。AWS の複数チームが既に本番環境で AI エージェントに Strands を使用しており、これには Amazon Q Developer、AWS Glue、VPC Reachability Analyzer などが含まれます。今回、皆様が独自の AI エージェントを構築できるよう Strands について共有したいと思います。(Strands Agents – オープンソース AI エージェント SDK の紹介より)
モデル駆動型アプローチがしっくりこない方もいらっしゃるかと思いますが、モデル駆動型アプローチとは、開発者が複雑なワークフローを定義する代わりに、LLMの高度な推論能力に依存してエージェントの次のステップやツールの使用を自律的に判断させる方法です。AWS が AI Agents を作るための SDK を公開したのですが、こちらが AI Agent に MCP Client 機能を簡単に追加できるようになっており、今回こちらを利用しました。
結局何をしたのか?
MCP (Model Context Protocol) を使って MCP(Minecraft Play) という言葉遊びを考えていたところに、Strands Agents というそれを簡単に実現する SDK が出てきたため MCP Server 及び MCP Client を実装して試してみた、というお話です。
まずは動かしてみよう
環境作成
このコードは Raspberry Pi で動くことを前提にしています。詳細の理由は後述しますが、Raspberry Pi で動かせる Minecraft には Python API が存在するためです。皆様は Builder ゆえに一家に Raspberry Pi が複数台ある逸般の誤家庭だと思うので(私も少ないながら Raspberry Pi 3 Model B 及び Raspberry Pi 4 Model B を一台ずつ持っております)、すでに Raspberry PI そして Minecraft: Pi Edition が動かせる前提からスタートします。今回は Raspberry PI 4 Model B で動作確認を取っています。持っていない方はぜひお近くの Amazon でご購入いただき、準備してください。
コマンドを実行
Raspberry PI のターミナルから以下のコマンドを実行します。私はカレントディレクトリを Desktop にして実行しました。
# Desktop をカレントディレクトリに
cd ~/Desktop
# AWS CLIのインストール
sudo apt install awscli -y
# uvのインストール
curl -LsSf https://astral.sh/uv/install.sh | sh
# AWS 認証情報の設定
# 今回は AmazonBedrockFullAccess ポリシーを持ったIAM ユーザーのアクセスキー及びシークレットキーを用意しました。
aws configure
(認証情報の入力)
# リポジトリのクローン
git clone https://github.com/aws-samples/mcp-mcp
cd mcp-mcp/basic
mcp.json ファイルを修正
そして mcp.json ファイルを修正します。 以下の /path/to/clone/directory/ の部分を実際に clone した場所に書き換えます(私の場合は /home/{OS のユーザー名}/Desktop/mcp-mcp/basic/server でした。
{
"mcpServers": {
"minecraft": {
"command": "uv",
"args": [
"--directory",
"/path/to/clone/directory/mcp-mcp/basic/server",
"run",
"minecraft.py"
]
}
}
}
実行
あらかじめ Minecraft: Pi Edition:reborn (Client)を Creative Mode で開いておきます(↓のフィールドの画面が出ていればOK)。

MCP 付きでエージェントを起動
その後、以下のコマンドを実行します。

Minecraft で動かしたい内容を入力
すると Prompt を入力できるようになるので、そこに Minecraft で動かしたい内容を自然言語で入力し Enter キーを押せば OK です。
「高さ 1 以上の部分を Air で埋めて、高さ 0 以下のところを grass で埋めて更地にして。」と入力した例

Minecraft の API が実行
すると、AI Agent がどうすればユーザーの要望に応えられるかを考え、Minecraft の API を MCP 経由で実行し始めます。
(結果は写っている景色によっても毎回変わることにご注意ください)
実行中の様子

実際にやったこと: capture ツールを使ってスクリーンショットを取得する
AI が作業し終わった後追加で司令を出すこともできます。



成功 !
このように AI Agent とのやりとりを通じて TNT を散りばめたカラフルなピラミッドが出来上がりました。

実装を覗く
さて、ここからどのようにこの仕組みが動いていたのか実装を見ていきます。アーキテクチャーは以下の図の通りです。実装を見るとこのアーキテクチャーの意味がわかってくるかと思います。

Strands Agents をつかった実装
まずはエントリーポイントであった ./basic/client/client.py から見ていきましょう。
ここでは Strands Agents を使って Agents を構築しているのが見て取れます。

client.py
コード
# 10行目
from strands import Agent
# 199 行目
agent = Agent(
system_prompt = """
You are a professional at creating structures in Minecraft. Please use the given tools to meet user requests.
However, user requests may be rough and lack information. In such cases, proceed by assuming what the user wants as a professional.
Before starting work, during work, and at the end, please use the capture tool and image_reader, setPlayerPos tools to understand the situation in the Minecraft field.
Since the player's perspective is fixed in a bird's-eye view, it is important to check if everything looks appropriate from above.
Frequent checks improve the accuracy of your work, so they need to be done often.
""",
tools = tools,
callback_handler=strands_callback_handler
)
# インタラクティブチャットループを開始
print("\n=== Minecraft Builder /w Strands Agents チャットを開始します ===")
print("終了するには 'exit' または 'quit' と入力してください\n")
# ツール使用状態をリセット
if hasattr(strands_callback_handler, 'last_tool'):
delattr(strands_callback_handler, 'last_tool')
while True:
# ユーザー入力を受け取る
message = input("\n> ")
英語の部分がシステムプロンプトでこの AI Agent の役割を定義しています。
一般に LLM は英語を使ったほうが精度が出やすいのと、ユーザーの目に触れるところでもないので英語を使用しました。和訳すると以下の通りです。
英語部分の和訳
あなたは Minecraft で構造物を作成するプロフェッショナルです。与えられたツールを使用してユーザーのリクエストに応えてください。
ただし、ユーザーのリクエストは大まかで情報が不足している場合があります。そのような場合は、プロフェッショナルとしてユーザーが望むものを推測して進めてください。
作業を開始する前、作業中、そして作業終了時には、capture tool と image_reader、setPlayerPos ツールを使用して、Minecraft フィールドの状況を把握してください。
プレイヤーの視点は鳥瞰図に固定されているため、上から見て全てが適切に見えるかを確認することが重要です。
頻繁なチェックは作業の精度を向上させるので、頻繁に行う必要があります。
Tool について
そして message の部分がユーザーの入力部分(先の例では「更地にして」「ピラミッドを作って」)です。これだけで AI Agent が作れてしまいます。Strands Agents を使うと AI Agent の構築がとても簡単ですね。ただし、AI Agent は外部世界とやりとりするための Tool が大切です。今回は Tool に題名の通り MCP Server を使っているほか、Strands Agents にはよく使われるだろう組み込みの Tool を使った他、自作の Tool も用意しました
ツールを取得するコード
コード
# 14 行目
from strands_tools import image_reader
# 173 行目
tools = [capture, image_reader]
# クライアントからツールを取得
for i, client in enumerate(clients):
try:
logger.debug(f"クライアント {i+1}/{len(clients)} からツールを取得中...")
client_tools = client.list_tools_sync()
tools.extend(client_tools)
logger.debug(f"{len(client_tools)}個のツールを追加しました")
except Exception as e:
logger.error(f"クライアント {i+1}/{len(clients)} からツールの取得に失敗しました: {e}")
使用したツールについて
capture は私が用意した Minecraft のスクリーンショットを取得するツール、image_reader は strands 組み込み(とはいえパッケージとしては分離)の Tool で画像を LLM で解説する機能を持っています。これにより LLM が Minecraft が今どの状態かを認識できるようになり、Minecraft の生成物の精度の向上が見込めます。
capture したあと image_reader を使うことで、今の Minecraft の状況の確認を LLM が自発的に行える
capture のコード実装
capture のコード実装は、./basic/client/tools/capture.py にあります。重要な点を 2 つほど紹介します。
# 7 行目
from strands import tool
# 15 行目
@tool
def capture():
"""
This function takes a screenshot of Minecraft and returns the image file path.
Args:
None
Returns:
Screenshot file path
"""
capture のコード実装の解説
このように strands にある tool を import したあと、tool 化したい関数にデコレータ(@tool) をつけるだけで Tool として扱うことができます。これは Strands Agents の機能でとても楽です。また、@tool デコレータをつけた場合は docstring を書くことが大切です。この docstring にどういうときにこのツールを使う必要があるのか、引数と返り値はなんなのかを記述することで LLM が tool の使い時と使い方を理解できるようになります。
# 143 行目
img.save(save_path)
logger.info(
f"スクリーンショットを保存しました: {save_path}"
)
返り値の設定
もう一つは返り値の設定です。画像ファイルのパスを返すようにしています。画像は保存するだけではなく、image_reader に渡して画像を LLM が解釈できないと意味がありません。image_reader は input にファイルパスを受け取るため、保存した画像のファイルパスを返すことが大切です。
Strands Agents で MCP をサーバーを取り込む
Strands Agents は MCP Client の構築が簡単です。簡単な理由の一つに用意した MCP Server を取り込むのが簡単であり、MCP は USB Type-C と同様抜き差しが簡単である、というのを体現しています。MCP サーバーを取り込んでいるところのコードを見てみましょう。./basic/client/client.py で行っています。

MCP サーバーを取り込む
コード
# 86 行目
mcp_client = MCPClient(
lambda key=key: stdio_client(
StdioServerParameters(
command=mcp_settings[key]["command"],
args=mcp_settings[key]["args"]
)
)
)
# 150 行目
with ExitStack() as stack:
logger.info("クライアントコンテキストを設定中...")
clients = []
# クライアントコンテキストの設定とエラーハンドリング
for i, mcp_client in enumerate(mcp_clients):
try:
client = stack.enter_context(mcp_client)
clients.append(client)
logger.debug(
f"クライアント {i+1}/{len(mcp_clients)} のコンテキスト設定完了"
)
except Exception as e:
logger.error(
f"クライアント {i+1}/{len(mcp_clients)} のコンテキスト設定に失敗しました: {e}"
)
# クライアントが1つも設定できなかった場合はエラー
if not clients:
logger.critical("有効なクライアントが設定できませんでした")
raise RuntimeError("有効なクライアントが設定できませんでした")
logger.info("ツールを準備中...")
tools = [capture, image_reader]
# クライアントからツールを取得
for i, client in enumerate(clients):
try:
logger.debug(
f"クライアント {i+1}/{len(clients)} からツールを取得中..."
)
client_tools = client.list_tools_sync()
tools.extend(client_tools)
logger.debug(f"{len(client_tools)}個のツールを追加しました")
86 行目で先に上げた mcp.json の内容を読み込んでいます。mcp.json にはそれぞれの MCP Server の起動コマンドである command と、それの引数である args があるのでそれを読み取っておきます。
そして、150 行目からスタートしているコードの肝である、client_tools = client.list_tools_sync() で tool をMCP サーバとして取り込みます。これで Client がサーバーを Tool として使える準備が整いました。最後に tools.extend(client_tools) で tools 変数を Agent にいれることで AI Agent が MCP Server を使うことができます。
MCP Server の実装
Minecraft の API と LLM を結びつけるための MCP Server の実装を見てみましょう。

スクリプトの解説
というわけで ./basic/server/minecraft.py を見に行きましょう。 1000 行を超える長いコードですが、まず見ていただきたい部分はここです。
# 823 行目
if __name__ == "__main__":
try:
# 環境チェック
if not check_environment():
logger.critical(
"Environment check failed. Some features may not work correctly."
)
print(
"Critical: Environment check failed. Some features may not work correctly."
)
logger.info("Starting Minecraft Tool Server")
print("Starting Minecraft Tool Server...")
# サーバー起動
mcp.run(transport="stdio")
これだけ(実質 mcp.run(transport='stdio') のみ)で mcp サーバーを動かすことが可能です。とはいえ、これだけだと MCP Server で動く Tool の実態がゼロですので、この MCP Server に tool を紐づけていく作業が必要です。
postToChat をどうやって Tool として取り込む
Minecraft API でわかりやすい API である postToChat をどうやって Tool として取り込んでいるのかを ./basic/server/minecraft.py で見てみましょう。
# 960 行目
@mcp.tool()
def postToChat(message: str) -> str:
"""
Post a message to the Minecraft chat.
Args:
message: Message to post to chat, required, str
Returns:
Result message
"""
# 999 行目
# コマンドの構築
command = ["uv", "run", script_name, "--message", message]
# コマンドの実行をログに記録
logger.info(f"Executing command: {' '.join(repr(arg) for arg in command)}")
# コマンド実行とエラーハンドリング
try:
# タイムアウト設定付きでコマンド実行
result = subprocess.run(
command,
cwd=script_dir,
capture_output=True,
text=True,
timeout=30, # 30秒のタイムアウト
)
またしてもデコレータ @mcp.tool があります。tool としたい関数の前にこのデコレータをつけるだけで MCP Server に取り込むことができます。めちゃくちゃ簡単ですね。また、docstring も同様に書いておく必要があります。postToChat は Python の print のように Minecraft の画面でテキストを表示する API です。なので表示したいテキストを message 引数として取り込む必要があります。
そしてそのメッセージを command = ["uv", "run", script_name, "--message", message] で別の Python コードを呼び出すようにしています。Python コードなのに、なぜ別プロセスで Python を呼び出す必要があるのでしょうか。これは技術的な理由があり、Minecraft の Python API である mcpi が Python のバージョン 3.8 以下でしか動かないのに対し、MCP Server のパッケージ が Python 3.10 以上でしか動かないため、それぞれ別プロセスで呼ぶ必要があります。今回は uv を使って MCP を Python 3.12, mcpi を Python 3.8 で動かしています。
postToChat.py
さて呼び出された Python 3.8 で動くコードを見ていましょう。./basic/server/tools/postToChat.py です。
# 4 行目
from mcpi.minecraft import Minecraft
def connect_to_minecraft(max_retries=3, retry_delay=2):
"""Minecraftサーバーへの接続を試みる(リトライ機能付き)"""
for attempt in range(max_retries):
try:
mc = Minecraft.create()
# 48 行目
# Minecraftサーバーに接続
mc = connect_to_minecraft()
# チャットにメッセージを投稿
mc.postToChat(args.message)
コードの解説
Minecraft に接続して、postToChat を呼び出しているだけです。このように mcpi の API を MCP Server 及び Strands Agents の仕組みを介するだけで、簡単に AI Agent を作りあげることができます。動画でメインに使っていた API は setBlocks API のため、./basic/server/minecraft.py の def setBlocks の部分(398行目)と ./basic/server/tools/setBolocks.py もぜひ見てみてください。minecraft.py のほうには docstring に各 Block の ID と名前の紐づけを 300 行近くにわたって説明しているだけなので、私が心を無にしながら作業をしていたことがわかるかと思います。。。

まとめ
Strands Agents 及び MCP(Model Context Protocol) を使うと MCP (Minecraft Play) を人間にはできない速さで行えることがわかったかと思います。Strands Agents 及び MCP (両方の意) にはまだまだ様々な可能性があります。ぜひ一家に複数台ある Raspberry Pi を有効活用してその可能性の深さを体感してみてください。
「ん?これじゃ AWS のサービスは Amazon Bedrock しか使ってなくてクラウドの良さがわからないじゃないか?そんなもの AWS の記事で出すんじゃねぇ?」「ここに advanced ディレクトリがあるのが見えぬのか !? (空ディレクトリ)」「いいだろう…!MCP 及び Strands Agents をクラウドで扱う良さを見せてやろう。…市川さんが !!!!!!」

筆者プロフィール
呉 和仁 (Go Kazuhito / @kazuneet)
アマゾン ウェブ サービス ジャパン合同会社
自動車・製造ソリューションアーキテクト
IoT の DWH 開発、データサイエンティスト兼業務コンサルタントを経て現職。
プログラマの三大美徳である怠惰だけを極めてしまい、モデル構築を怠けられる AWS の AI サービスをこよなく愛す。
