Anker Prime Charging Station (6-in-1, 140W) 【独自技術Anker GaNPrime採用/コンセント差込口 2口 / USB-C 2ポート / USB-A 2ポート / PSE技術基準適合】iPhone Galaxy Android スマートフォン MacBook ノートPC 各種 その他機器対応(ブラック)
¥14,990 (2025年4月28日 13:11 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)
「ドローンを飛ばして!」とAI(Claude)に話しかけるだけで、
仮想空間上でリアルなドローンを自在に操縦できたら──。
そんな夢を、箱庭ドローンシミュレータ × MCPサーバー × Claude連携で実現しました!
本記事では、
箱庭ドローンシミュレータをMCPサーバー化し、Claude(AI)から自然言語コマンドで操縦できる仕組みを紹介します。
まずは、動く様子をご覧ください!
📺 デモ動画はこちら →
全体アーキテクチャ上図の通りです。
この連携で重要なキーとなる技術要素は、たった2つだけ。
- Claude.ai は、MCPプロトコルで MCPサーバーと通信できる
- 箱庭ドローンシミュレータは、Python API経由でドローンを操作できる
つまり、
「自然言語 → MCP経由コマンド → Python API → ドローン操作」
というシンプルな連携構成が実現できた、というわけです!
箱庭MCPサーバー
箱庭はPythonと相性が良いので、今回作成したMCPサーバーはPythonで作っています。
こちらの記事を参考にして作成しました(感謝)。
今回やりたいことは、ツールの外部起動になりますので、list_toolsとcall_toolのみを追加することで対応しました。
コード断片:
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
return [
types.Tool(
name="describe_hakoniwa_drone",
description="Provides an overview of the Hakoniwa Drone Simulator",
inputSchema={
"type": "object",
"properties": {},
"required": []
},
),
types.Tool(
name="hakoniwa_drone_simulator_start",
description="Starts the Hakoniwa Drone Simulator",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
types.Tool(
name="hakoniwa_drone_simulator_stop",
description="Stops the Hakoniwa Drone Simulator",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
types.Tool(
name="provide_hakoniwa_drone_manual",
description="Provides the Hakoniwa Drone Simulator operation manual (PDF)",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
types.Tool(
name="drone_command_takeoff",
description="Executes a takeoff command on the drone simulator",
inputSchema={
"type": "object",
"properties": {
"height": {
"type": "number",
"description": "The target height for takeoff (default 1.0m)"
}
},
"required": []
}
),
types.Tool(
name="drone_command_move_to_position",
description="Move drone to specified (x, y, z) position (ROS coordinate system, meters)",
inputSchema={
"type": "object",
"properties": {
"x": {"type": "number", "description": "Forward direction, meters"},
"y": {"type": "number", "description": "Left direction, meters"},
"z": {"type": "number", "description": "Upward direction, meters"},
"speed": {"type": "number", "description": "Movement speed, meters/second"}
},
"required": ["x", "y", "z", "speed"]
}
),
types.Tool(
name="drone_command_land",
description="Land the drone",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
)
]
@server.call_tool()
async def handle_call_tool(name: str, arguments: dict | None) -> list[types.TextContent]:
if name == "describe_hakoniwa_drone":
return [
types.TextContent(
type="text",
text=(
"Hakoniwa Drone Simulatorは、仮想環境上でドローンの自律飛行や荷物輸送タスクを模擬できる高機能シミュレータです。"
"ROS座標系およびUnity座標系の両方に対応し、リアルタイムなカメラ画像取得、Lidarデータ取得、API制御による移動・離着陸・物体把持が可能です。"
"訓練用途、研究用途、またはAI開発向けのプロトタイピングに適しています。"
)
),
]
elif name == "hakoniwa_drone_simulator_start":
return [
types.TextContent(
type="text",
text=hakoniwa_drone_simulator_start()
)
]
elif name == "hakoniwa_drone_simulator_stop":
return [
types.TextContent(
type="text",
text=hakoniwa_drone_simulator_stop()
)
]
elif name == "provide_hakoniwa_drone_manual":
return [
types.TextContent(
type="text",
text=(
"箱庭ドローンシミュレータの公式マニュアルはこちらです!\n"
"👉 [Hakoniwa Drone Manual PDF](https://www.jasa.or.jp/dl/tech/simulator_operation_edition.pdf)"
)
)
]
elif name == "drone_command_takeoff":
height = 1.0
if arguments and "height" in arguments:
height = float(arguments["height"])
return [
types.TextContent(
type="text",
text=drone_command("takeoff", [str(height)])
)
]
elif name == "drone_command_move_to_position":
if arguments is None:
raise ValueError("Arguments required for moveToPosition")
x = str(arguments["x"])
y = str(arguments["y"])
z = str(arguments["z"])
speed = str(arguments["speed"])
return [
types.TextContent(
type="text",
text=drone_command("moveToPosition", [x, y, z, speed])
)
]
elif name == "drone_command_land":
return [
types.TextContent(
type="text",
text=drone_command("land")
)
]
else:
raise ValueError(f"Unknown tool: {name}")
今回、初めてMCPサーバーを作ってみて、いくつかハマりポイントがありましたので、共有しておきます!
標準出力にメッセージを出してはいけない
Claude側(MCPクライアント)は、標準出力を純粋なJSON通信に使っています。
そのため、print() などでログを出すと、JSONパースエラーになり通信が途絶えます。
➔ ログ出ししたい場合は、ファイルに書き出すか、サーバー側だけで完結させる必要あり。
ちなみに、今回は、箱庭のPython APIを外部プロセスとして外出して、subprocess.Popen()で実行かける対応をしました。
環境変数(PYTHONPATH / DYLD_LIBRARY_PATH)に注意
サーバープロセスを起動する際、Unityや箱庭ライブラリにパスを通しておかないと、モジュールインポートや動的ライブラリ読込みに失敗します。
➔ subprocess.Popen()のときに、環境変数を適切に引き継ぐ必要あり。
MCPサーバーからUnityプロセスの起動で落ちる…
Unityプロセスをバックグラウンドで起動しても、バックグラウンド実行コマンドが終了すると、一緒に落ちてしまうことがわかりました。
そのため、Unityを起動するためのデーモンプロセスを用意して、MCPサーバーと通信させて今回のデモを実現させています。
ドローンシミュレータ(箱庭)をMCPサーバー化し、
Claudeと自然言語で連携できる未来を手に入れました!
まだまだ機能追加や改善の余地はたくさんありますが、
まずは 「論よりRUN」 をモットーに、小さな一歩を形にすることができました!
次は…
🛫 自律飛行・WayPoint巡回モードを追加したい
🎒 荷物輸送ミッション(グラブ&リリース)に挑戦したい
🎥 飛行中のカメラ画像を取得して、AI解析に使いたい
🤖 Claudeともっと高度な「ドローン対話」シナリオを作りたい