MCPで試すRoombaハック

気づいたら今日は僕の30歳の誕生日です!

このブログも20代、特に社会人になってから本腰入れて更新を始めるようになって本当に多くの方に読んでいただき、少なからずこのブログを続けたおかげで自分のことを知ってもらったり、影響を与えることもできてほそぼそと続けてきて良かったと思います。

皆さんいつもありがとうございます!30代になっても引き続きこのブログを投稿し続けていきたいと思います。

さて、前置きはこのぐらいにして、前回はとあるハドフで見つけたシリアルケーブルを使ってルンバをハックしてみました。

supernove.hatenadiary.jp

記事の内容としてはこれと言って目新しさはなかったのですが、思いの外バズってしまいこれまで書いたブログの最大スター数を上回って、総スター数の殆どをこのブログが占めることになってしまいましたw

このブログを公開してからMCPが流行るようになり、その中で僕が得意なFastAPIを使ってMCPサーバーを公開するネタをZennで公開しました。

zenn.dev

流行りの技術についてネタにしたこともあって、これまたバズってしまいましたw

そんな中、ふとこんなポストがありました

ちょうど次のルンバハックはゲームパッドとかでハックする方法を試そうかとしていたところでこれはいいネタだと思いましたし、ルンバ、MCPとそれぞれのネタを書いてバズった身としてはこの2つをかけ合わせてネタにすることで更にバズるのではと思ってしまったわけです(超絶安直w)

というわけで今回はMCPRoombaをハックする方法を試していきます。

先に今回のネタのソースコードは以下のレポジトリで公開しています。

  • MCPサーバー

github.com

  • Roombaのコントローラー

github.com

前提条件

前回の記事で紹介したルンバハックで必要になるハードウェアの設定を事前に済ませていることが前提条件になります(ソフトウェアについては前回とはまたちょっと違った方法で構築します)

supernove.hatenadiary.jp

セットアップ

Raspberry Pi(ルンバの操作)

Rasbeprry Piでルンバを操作できるように環境構築をしていきます。

mosquittoのセットアップ

まずはMCPサーバーからMQTTで操作するためのブローカーとしてRaspberry Piでmosquittoを使います。

以下のコマンドでmosquittoをインストールします。

sudo apt-get install mosquitto mosquitto-clients

今回はMCPサーバーを動かしている別のPCからRaspberry Piにアクセスする必要があるため、mosquittoを外部アクセスできるように設定を行います。

/etc/mosquitto/mosquitto.conf を開き以下の2行を追加します。

listener 1883
allow_anonymous true

追加したら以下のコマンドでmosquittoを再起動します。

sudo systemctl restart mosquitto

ソースコードのクローン

今回の環境構築で使用するuvをRaspberry Piでセットアップしていきます。

前回も述べたように今はRaspberry Pi OSのシステムに直接pipでライブラリをインストールするのは非推奨になっていますが、uvを使うことでNode.jsのようにプロジェクト単位でパッケージ化することができてしかも仮想環境も簡単に用意できるので、uvを採用していきます。

uvは以下のコマンドでインストールします。

curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.bashrc

インストールしたらルンバを操作するためのコントローラをクローンしてきます。

git clone https://github.com/Miura55/roomba-mqtt-controller

クローンしたら以下のコマンドで依存パッケージをインストールします。

cd roomba-mqtt-controller
uv sync

Subscriberは以下のコマンドを実行して起動します。

uv run main.py

ソースコードはレポジトリの中にあるものがすべてですが、今回のポイントはMQTTのSubscriberのコードとRoombaの操作を行うコードを別で分けています。

理由はSubscriberはMQTTの通信を受け付けるだけにして、ハードウェアのアクセスやビジネスロジックを分離して可読性を上げたかったのと移動以外にHomeコマンドを実行したときにSubscriberの中で実行すると何故か動かなかったので、Callback関数の処理を以下の通りシェルでルンバの操作を行うだけのシンプルなものにしました。

こうすることで、移動もHomeコマンドで自動でホームベースに戻す挙動も実現できるようになりました。(地味に一番詰まったポイント)

def on_message(client, userdata, msg):
    print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
    
    # コマンドラインでスクリプトを実行(Homeコマンドがきかないので)
    subprocess.run(f"uv run roomba_controller.py \'{msg.payload.decode()}\'", shell=True)

本当はメッセージに対してバリデーションを入れたほうがいいですが、そこはMCPサーバー側でスキーマ定義を行っているのでここはシンプルに来たものを受け入れるだけにしています。

MCPサーバー側

uvをセットアップ

MCPサーバー側も同様にuvをセットアップしていきます。

WindowsでもMacでも動作するので、お使いのOSに合わせてセットアップを行います。

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 
curl -LsSf https://astral.sh/uv/install.sh | sh

ソースコードをクローン

以下のコマンドでMCPサーバーのソースコードをクローンしていきます。

git clone https://github.com/Miura55/roomba_mcp_server.git

クローンしたら以下のコマンドで依存パッケージをインストールします。

cd roomba_mcp_server
uv sync

セットアップが終わったらMCPサーバーを立ち上げる必要がありますが、その前にRaspberry Pi側のMQTTブローカーに接続するためにあらかじめRaspberry Piのポートを確認、MCPサーバーの環境変数で設定しておく必要があります。

Raspberry Pi側では以下のコマンドでポート番号を確認しておきます。

ifconfig

設定したポートをMCPサーバーを起動するターミナルに対して以下の設定を行います。

export MQTT_ENDPOINT=【Raspberry Piのポート番号】

以下のコマンドでプログラムを実行します。

uv run main.py

実装のポイントとしては、移動のリクエストのときは以下のようにデフォルト値を定義しておくことです。

こうしておくことで、雑なプロンプトを投げて漏れている情報はデフォルトの値でリクエストを受けてくれるのでエラーが起きないようになるはずです。

それ以外はソースコードを見ていただければわかるはずです。

class MoveCommand(BaseModel):
    """Roombaの移動コマンドを表すモデル
    速度と回転速度を指定して移動するためのコマンドを定義します。
    速度はm/s、回転速度はrad/sで指定します。
    durationは移動時間を秒単位で指定します。

    Args:
        BaseModel (_type_): PydanticのBaseModelを継承したクラス
    """
    velocity: Optional[float] = 0.2  # 速度 (m/s)
    yaw_rate: Optional[float] = 0  # 回転速度 (rad/s)
    duration: Optional[int] = 0  # 移動時間 (秒) 

これで必要なプログラムが立ち上がりました。

動かしてみる

mcp-proxy をインストール

これ準備が整ったので、エージェントと接続してみます。

エージェントで接続するために以下のコマンドでmcp-proxyをインストールします。

uv tool install mcp-proxy

エージェントの設定

あとはMCPの接続設定をお使いのエージェントに設定します。

MCPに対応したエージェントに対して以下の設定を追加します。追加したらエージェントを再起動させます。

...
        "roomba-controller":{
            "command":"mcp-proxy",
            "args":[
                "http://localhost:8000/mcp"
            ]
        }
...

動作確認

手順が多かったですが、これでRoombaMCPで動かせるようになったはずです。

以下の動画のようにエージェントに「roombaを3秒前進させて」や「roombaを元に戻して」と話しかけるとMCPサーバーからMQTTでRaspberry Piに対して操作コマンドが送られれば今回のMCPは正常に動作しています。

youtu.be

まとめ

今回はMCPを使ってルンバを操作してみる実験をしました。

MCPでそれなりにスキーマを定義すれば雑にエージェントでプロンプト投げてもエラーなく動いてくれることがわかり、MCPの実装ポイントがまた一つ分かったような気がします。

やっぱりAIが理解してロボットがそれに合わせて動いている様子は学生時代を思い出させるものがあり、個人的には30代の最初にこれを試せてよかったと思いました。

これからもこういう遊び心を大切にしていきたいところ!