Model Context Protocol(MCP)を使ったVRChatとAIの連携実装

![pic6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157463/05a1570b-f158-4b00-b747-56e1bdf41102.gif)

## TL;DR
* **目的:** 自然言語でVRChatアバターを操作する「Prompt My Avatar」の技術実装  
* **主要技術:** Python+Flet UI、Model Context Protocol、ReAct Agent、OSCプロトコル  
* **開発ポイント:** MCP ClientとServerの両方を開発、アバター情報の動的取得・活用  
* **LLM活用:** Gemini 2.0 FlashとGemini 2.5 Proを比較検討、用途に応じた使い分け  
* **今後の課題:** セキュリティ強化、UIの改善、パフォーマンス最適化

**関連記事:**[メタバース×AI \- 自然言語でVRChatアバターを操作する「Prompt My Avatar」](https://qiita.com/Kuretan_vr/items/021ae9578e85cc3e3def)



## Prompt My Avatarとは

「Prompt My Avatar」は、自然言語でVRChatのアバターを操作できるツールです。「笑って」「手を振って」といった日常的な言葉だけでなく、「退屈な会議のリアクションをして」といった複雑な指示も理解し、アバターを適切に動かすことができます。AIとVRChatを橋渡しすることで、初心者でも直感的にアバターを操作できる環境を提供します。

## **1\. 技術選定と苦労した点**

### 利用技術スタック

Prompt My Avatarは、以下の技術で実現されています：

* **Python \+ Flet UI:** AI分野で主流なPythonと、モダンUIを簡単に作成できるFlet  
* **Model Context Protocol (MCP):** AIモデル(LLM)と外部ツール(VRChat操作機能)を連携させるプロトコル  
* **ReAct Agent:** 複雑な指示をAIが理解し具体的な操作に変換する仕組み  
* **OpenSoundControl(OSC)プロトコル:** VRChatが提供する外部連携用通信プロトコル

### **MCPとクライアント開発**

MCPはAIモデルが外部ツールと対話するためのプロトコルで、以下の構成からなります：

* **MCP Client:** LLMと直接やりとりするアプリ（Claude DesktopやCursorなど）  
* **MCP Server:** 外部サーバーとやりとりしたり、ローカルでプログラムを実行する中継サーバー

多くのMCP関連記事ではServerの開発のみを扱いますが、本プロジェクトではClientとServer両方を開発しました。これは既存のClientでは以下の課題があったためです：

1. LLMからの応答が遅い  
2. ツールのオンオフがしにくい  
3. アバター名を自動取得できない（VRChatの特性上これは必須）  
4. 一般ユーザーにProプランは高コスト[^1]

https://x.com/Kuretan_vr/status/1907439309814366687


### **開発言語選択とGUIフレームワーク**

当初はTypeScriptでの開発を検討しましたが、exe化の障壁に直面し、私が慣れているPythonに移行しました。UIフレームワークはモダンなデザインと簡単な実装を両立できるFletを選びました：

| フレームワーク | 主な特徴 | 選定/見送り理由 |
| ----- | ----- | ----- |
| **Flet** | Flutterベース、モダンUI、クロスプラットフォーム | **採用。** モダンUIをPythonで容易に構築可能 |
| PyQt | 高機能、成熟、クロスプラットフォーム | 学習コスト高、ライセンス考慮で見送り |
| Kivy | OpenGLベース、モバイル寄り | UI記述が独特で表現力不十分 |
| Tkinter | Python標準、シンプル | UIのモダンさで劣るため見送り |

## **2\. データフロー**
**主な特徴**
* VRChatはOSCでしか入出力できない
* LLMは文脈がないと理解できないため、VRChat上の変化を伝える実装が必要


### ユーザー入力起点のフロー
```mermaid
sequenceDiagram
    actor User as ユーザー
    participant UI as Flet UI
    participant CM as ChatManager
    participant AI as AIProcessor
    participant MCP as MCPManager
    participant Relay as 中継サーバー
    participant VR as VRChat
    
    User->>UI: 1. テキスト指示入力
    UI->>CM: 2. イベント処理
    CM->>AI: 3. 入力テキスト・履歴渡し
    Note over AI: 4. プロンプト生成・LLM API呼び出し
    AI->>MCP: 5. MCPツール呼び出し
    MCP->>Relay: 6. WebSocket通信（JSON）
    Relay->>VR: 7. OSCメッセージ送信
    Note over VR: 8. アバター動作
    Relay-->>MCP: 結果応答
    MCP-->>AI: 結果応答
    AI-->>CM: 操作結果・LLM応答
    CM-->>UI: UI更新指示
    UI-->>User: 9. 操作結果表示
```
1. **ユーザー入力:** UIにテキスト指示を入力  
2. **イベント処理:** UI層がイベントを受け、コア層のChatManagerに処理を依頼  
3. **AI処理依頼:** ChatManagerがAIProcessorに入力テキストと履歴を渡す  
4. **LLM呼び出し & ツール実行:**  
   * AIProcessorがプロンプト生成、LLM API呼び出し  
   * LLMが必要と判断した場合、MCPツールを呼び出し  
5. **WebSocket通信:** 中継サーバーにJSON形式で指示を送信  
6. **OSC変換・送信:** 中継サーバーがJSONをOSCメッセージに変換し、VRChatに送信  
7. **VRChat動作:** 指示通りにアバターが動作

### VRChat起点のフロー

```mermaid
sequenceDiagram
    participant VR as VRChat
    participant Relay as 中継サーバー
    participant MCP as MCPManager
    participant AI as AIProcessor
    participant UI as Flet UI
    actor User as ユーザー
    
    Note over VR: 1. アバター変更などのイベント発生
    VR->>Relay: OSCで情報発信
    Note over Relay: 2. OSCをWebSocketに変換
    MCP->>AI: 3. システムプロンプト更新指示
    Relay->>MCP: WebSocketでClientに送信
    MCP->>UI: 4. UI更新指示
    Note over AI: LLMのシステムプロンプト更新
    UI->>User: 変更結果表示
```




1. **イベント発生:** アバター変更などのイベントがOSCで発信される  
2. **イベント変換:** 中継サーバーがOSCをWebSocketに変換しClientに送信  
3. **UI・プロンプト更新:** 新しいアバター情報をUIに表示し、LLMのプロンプトを更新

### なぜ中継サーバーを設置したか

一番の理由はMCPサーバーからOSCを送信することができなかったからです。
当初はMCPとOSCの相性が悪いのかと考えていましたが、検証してみたところClaude for Desktopが原因でした。[^2]

https://x.com/Kuretan_data/status/1906333145341038959

このことに気がついたのはある程度開発が進んでからで、その頃には中継サーバーを建てていたのでそのままにしました。

### なぜデータフロー2があるのか

後述するようにVRChatは使用しているアバターごとに使用できるパラメータが変化します。
AIが適切に指示を出すにはAIにアバターやパラメータの変更をリアルタイムで反映させる必要があるからです。

## 3\. LLMによる自然言語処理の実装

### アバター情報の活用

VRChatの重要な特徴として、アバターごとに操作パラメータが異なります。例えば「帽子を脱ぐ」という動作一つとっても多くの種類があり得ます。：

* `is_hat_on` (bool): True=帽子をつける、False=脱ぐ  
* `is_hat_off` (bool): 逆の意味になる可能性も  
* `hat` (int): 0=なし、1=赤い帽子、2=青い帽子...など

LLMは現在のアバターのパラメータを知らないため、システムプロンプトにこの情報を含める必要があります。VRChatはアバター情報をローカルファイルに保存しているため、これを活用しています。[^3]

システムプロンプトには「VRCEmote の 1 は手を振る動作」と言うように具体的に指示することで、指示と操作の紐付け精度が向上します。この対応付けは手間がかかりますが非常に重要です。

### **ReAct Agentによる複雑指示の処理**

「つまらない会議のリアクション」のような複雑な指示には、LLMが思考→行動→観察→再思考のサイクルを繰り返す「ReAct」パターンが必要です：

1. **思考:** 「つまらな会議」とは？→退屈な表情、ため息、「つまんない」とつぶやくなどの動作を組み合わせるべき  
2. **行動:** 現在のアバターパラメータを確認（`get_avatar_parameters`ツールを実行）  
3. **観察:** 利用可能なパラメータを確認（例：死んだふりというパラメータが使えるみたい）  
4. **思考:** これらを組み合わせて実行しよう  
5. **行動:** 各パラメータを設定するコマンドを実行  
6. **観察:** 実行結果確認  
7. **完了:** ユーザーに結果を報告


## **4\. AIモデル選定と性能比較**

AIモデルの選定はアプリ全体の性能とコストに直結します。MCP自体はAnthropicの出した規格ということもあり、
Claude Sonnetがtool呼び出しの面では優秀でした。
ただ値段と応答速度の問題もあるため、Geminiを選択しました。現在では**Gemini 2.0 Flash**と**Gemini 2.5 Pro**を選択できるようにしています。[^4]

**性能比較:**

* **応答速度:** Flashが明らかに高速。単純指示ではほぼリアルタイムで処理  
* **精度:** 複雑・曖昧な指示の解釈はProが優位。複数ステップや文脈理解が必要な場面で差が出る  
* **コスト:** Flashの方が低コスト

**使い分け:**

* **Flash:** 単純明快な指示（「ジャンプ」「笑って」）、速度重視の場合  
* **Pro:** 複雑・抽象的な指示、複数ステップ思考が必要な場合

他のAIモデルも対応できるような実装にはしてありますが、具体的な対応はこれから検討します。

## **5\. 学びと今後の展望**

### **開発を通じて学んだこと**

* **実践の重要性:** 実際に目的を持って手を動かすことで、AIエージェントの仕組み、限界、可能性を深く理解できました  特にtoolをちゃんと呼んでくれるモデルかという観点はこれまで意識していませんでした
* **新技術採用のリスク管理:** イノベーションには仕様変更やバグなどのリスクが伴います。疎結合設計や柔軟な対応計画が重要です

### **MCPの可能性と課題**

* **既存アプリへの統合:** MCP Serverを開発することで、様々なアプリケーションをAI対応にできる可能性があります  
* **セキュリティ配慮:** MCPは強力である一方、適切なセキュリティ設計が不可欠です。今回は全てローカルで完結する設計としてありますが、ネットワークにつなぐ場合は認証の問題もあり設計が複雑になります

### **今後の目標**

メタバースとAIの融合は始まったばかりです。今後は以下の方向性で発展を目指します：

* ユーザー行動に応じた動的コンテンツ生成  
* 自然な対話が可能なAI搭載ボットの開発  
* より多くのVRChat機能へのアクセス提供

## **参考リソース**

* 

https://x.com/Kuretan_vr/status/1912881948663673203
* [VRChat OSC Documentation](https://docs.vrchat.com/docs/osc-overview) \- VRChatの外部通信プロトコルの公式ドキュメント  
* [Model Context Protocol](https://modelcontextprotocol.io/introduction) \- MCPの概要と仕様  
* [LangChain](https://python.langchain.com/docs/introduction/) \- LLMアプリケーション開発フレームワーク  
* [LangGraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/) \- ReActエージェント実装に使用  
* [Flet UI](https://flet.dev/) \- Pythonでモダンなクロスプラットフォームアプリを開発するためのフレームワーク

## **おわりに**

Prompt My Avatarは現在αテスター募集中です。興味を持っていただいた方はDiscordサーバーへの参加とXのフォローをお願いします。皆様のフィードバックをお待ちしています！

Discord:https://discord.gg/7XPvTaMGQb
X:https://x.com/Kuretan_vr


[^1]:Proプランで無くてもMCP自体は使えるはずですが、利用制限を考えるとProプランの選択が必要となってきます。また普段LLMをあまり使わないVRChatユーザーにとってProプランはあまりに高額です。


[^2]:Claude for Desktopは起動時に2回MCP Serverを立ち上げたり、デスクトップアプリを閉じてもMCP Serverを正しくkillしないバグがあります。そのため同じMCP Serverが複数存在することになり、これが非常に困りました。OSCは1:1の通信にしか対応していないため、OSC通信するMCP Serverが複数あるとエラーになります。

[^3]:あくまでパラメータの型とパラメータ名しかないので、hatというパラメータ名が帽子を脱ぐのかつけるのかは不明なままです。

[^4]:この記事を書いてる最中にもGemini 2.5 Flashが出ました。情報更新が早すぎて数ヶ月後には記事が陳腐化してそうで恐ろしいです。