MCP Hub
Back to servers

ResoniteLink MCP

A comprehensive MCP server for controlling Resonite VR worlds, allowing users to manage slots, components, and world objects via WebSocket. It features advanced capabilities for ProtoFlux node manipulation and component property updates.

Stars
14
Forks
2
Tools
18
Updated
Jan 8, 2026
Validated
Jan 9, 2026

ResoniteLink MCP

ResoniteLink WebSocket プロトコルを使用して Resonite VR ワールドを操作するための MCP サーバ & CLI ツール。

インストール

npm install
npm run build

MCP サーバとして使用

Claude Code 設定

プロジェクトルートの .mcp.json に設定済み。Claude Code を再起動すると自動的にMCPサーバーが利用可能になります。

Claude Desktop 設定

claude_desktop_config.json に以下を追加:

{
  "mcpServers": {
    "resonitelink": {
      "command": "node",
      "args": ["C:/Users/neo/GitHub/resolink_mcp/dist/mcp-server.js"],
      "env": {
        "RESONITE_WS_URL": "ws://localhost:29551"
      }
    }
  }
}

利用可能なツール

ツール説明
connectResonite に接続
disconnect接続を切断
get_slotスロット情報を取得
find_slot名前でスロットを検索
add_slotスロットを追加
remove_slotスロットを削除
update_slotスロットを更新
add_componentコンポーネントを追加
get_componentコンポーネント情報を取得
update_componentコンポーネントを更新
remove_componentコンポーネントを削除
search_componentsコンポーネントを検索
get_component_infoコンポーネント詳細を取得
list_categoriesカテゴリ一覧
search_by_categoryカテゴリで検索
search_by_memberメンバー名で検索
get_component_sourceソースコードを取得
grep_sourceソースを全文検索

CLI として使用

# スロット情報を取得
node dist/cli.js get-slot --slot-id Root --depth 1

# 名前でスロットを検索
node dist/cli.js find --name MyObject

# スロットを追加
node dist/cli.js add-slot --name NewSlot --x 0 --y 1 --z 0

# コンポーネントを追加
node dist/cli.js add-component --slot-id <id> --type "[FrooxEngine]FrooxEngine.BoxMesh"

# スロットを削除
node dist/cli.js remove --slot-id <id>

ライブラリとして使用

import { ResoniteLinkClient } from './src/index.js';

const client = new ResoniteLinkClient({ url: 'ws://localhost:29551' });
await client.connect();

// スロットを追加
await client.addSlot({ name: 'MyObject', position: { x: 0, y: 1, z: 0 } });

// コンポーネントを追加
await client.addComponent({
  containerSlotId: slotId,
  componentType: '[FrooxEngine]FrooxEngine.BoxMesh'
});

client.disconnect();

クライアントオプション

const client = new ResoniteLinkClient({
  url: 'ws://localhost:29551',  // WebSocket URL(必須)
  debug: true,                   // コンソールにログ出力
  logFile: 'debug.log',          // ファイルにログ出力
  requestTimeout: 30000,         // リクエストタイムアウト(ミリ秒、デフォルト: 30000)
  autoReconnect: false,          // 自動再接続
  reconnectInterval: 5000,       // 再接続間隔(ミリ秒)
});

デバッグログ

問題が発生した場合、デバッグログを有効にすると SEND/RECV メッセージを確認できます:

const client = new ResoniteLinkClient({
  url: 'ws://localhost:29551',
  debug: true,           // コンソール出力
  logFile: 'debug.log',  // ファイル出力
});

ログ例:

[2026-01-08T06:10:24.504Z] SEND: { "$type": "addSlot", "messageId": "..." }
[2026-01-08T06:10:24.506Z] RECV: { "success": true, "messageId": "...", "error": null }

リクエストタイムアウト

レスポンスが返らない場合(不正な形式のデータ送信時など)、タイムアウトでエラーが発生します:

const client = new ResoniteLinkClient({
  url: 'ws://localhost:29551',
  requestTimeout: 10000,  // 10秒でタイムアウト
});

タイムアウト発生時は Error: Request timeout after 10000ms: updateComponent (...) のようなエラーがスローされます。

重要: ワールドシステムオブジェクト

Resonite ワールドの Root 以下には、削除してはいけないシステムオブジェクトがあります。

削除禁止オブジェクト

オブジェクト名説明
Controllersコントローラー入力システム
Rolesユーザーロール管理
SpawnAreaユーザーのスポーン位置
Lightワールドの照明
Skybox空・背景
User <...>接続中のユーザー(削除するとキックされる)
__TEMP一時オブジェクト管理
Undo Managerアンドゥ履歴
Assets共有アセット
Clipboard Importerクリップボードインポート機能

安全な削除方法

const SYSTEM_OBJECTS = [
  'Controllers', 'Roles', 'SpawnArea', 'Light', 'Skybox',
  '__TEMP', 'Undo Manager', 'Assets', 'Clipboard Importer'
];

// システムオブジェクトとUserはスキップ
if (SYSTEM_OBJECTS.includes(name) || name.startsWith('User ')) {
  continue;
}

コンポーネントタイプの書式

[FrooxEngine]FrooxEngine.ComponentName

よく使うコンポーネント

コンポーネント用途
BoxMesh直方体メッシュ
SphereMesh球体メッシュ
CylinderMesh円柱メッシュ
ConeMesh円錐メッシュ
BevelBoxMesh角丸直方体
RampMeshスロープ
FrameMeshフレーム
TorusMeshトーラス
CapsuleMeshカプセル
MeshRendererメッシュ描画
PBS_MetallicPBRマテリアル
Lightライト

マテリアルの設定例

await client.updateComponent({
  id: materialId,
  members: {
    AlbedoColor: { $type: 'colorX', value: { r: 1, g: 0, b: 0, a: 1, profile: 'sRGB' } },
    Smoothness: { $type: 'float', value: 0.5 },
    Metallic: { $type: 'float', value: 0.2 },
  }
});

Enum型の設定(BlendMode, LightType など)

Enum 型のメンバーを更新する場合、以下の形式を使用する必要があります:

{
  $type: 'enum',      // 小文字の 'enum'
  value: 'Alpha',     // 文字列で値を指定(数値ではない)
  enumType: 'BlendMode'  // Enum の型名
}

BlendMode の値

説明
Opaque不透明(デフォルト)
Cutoutカットアウト(アルファテスト)
Alpha半透明(アルファブレンド)

半透明マテリアルの例

await client.updateComponent({
  id: materialId,
  members: {
    BlendMode: { $type: 'enum', value: 'Alpha', enumType: 'BlendMode' },
    AlbedoColor: { $type: 'colorX', value: { r: 0.6, g: 0.75, b: 0.9, a: 0.3, profile: 'sRGB' } },
  }
});

LightType の値

説明
Directionalディレクショナルライト
Pointポイントライト
Spotスポットライト
await client.updateComponent({
  id: lightId,
  members: {
    LightType: { $type: 'enum', value: 'Point', enumType: 'LightType' },
    Intensity: { $type: 'float', value: 2.0 },
    Range: { $type: 'float', value: 10.0 },
  }
});

注意事項

  • $type は必ず小文字の 'enum''Enum' ではない)
  • value は数値ではなく文字列で指定
  • enumType を省略すると動作しない場合がある
  • 正しくない形式を送信するとレスポンスが返らずタイムアウトする

メンバーの型一覧

$type説明
float浮動小数点{ $type: 'float', value: 0.5 }
int整数{ $type: 'int', value: 10 }
bool真偽値{ $type: 'bool', value: true }
float22Dベクトル{ $type: 'float2', value: { x: 1, y: 1 } }
float33Dベクトル{ $type: 'float3', value: { x: 1, y: 2, z: 3 } }
floatQクォータニオン{ $type: 'floatQ', value: { x: 0, y: 0, z: 0, w: 1 } }
colorX{ $type: 'colorX', value: { r: 1, g: 0, b: 0, a: 1, profile: 'sRGB' } }
enum列挙型{ $type: 'enum', value: 'Alpha', enumType: 'BlendMode' }
reference参照{ $type: 'reference', targetId: 'Reso_XXXXX' }
listリスト{ $type: 'list', elements: [...] }

Materials リストの更新(2段階)

MeshRenderer の Materials リストを更新するには2段階の操作が必要です。

なぜ2段階必要か

ResoniteLink の制限により、リスト要素への参照設定は以下の動作をします:

  1. 1回目の更新: リストに新しい要素が追加されるが、targetIdnull になる
  2. 2回目の更新: 要素の id を指定することで、既存要素の targetId を設定できる

つまり、要素の追加と参照の設定は別々の操作として行う必要があります。

コード例

// 1. まずリストに要素を追加(この時点では targetId は null になる)
await client.updateComponent({
  id: rendererId,
  members: {
    Materials: {
      $type: 'list',
      elements: [{ $type: 'reference', targetId: materialId }]
    }
  }
});

// 2. 追加された要素のIDを取得
const rendererData = await client.getComponent(rendererId);
const elementId = rendererData.data.members.Materials.elements[0].id;

// 3. 要素のIDを指定して、参照を設定
await client.updateComponent({
  id: rendererId,
  members: {
    Materials: {
      $type: 'list',
      elements: [{ $type: 'reference', id: elementId, targetId: materialId }]
    }
  }
});

重要なポイント

  • id フィールドを省略すると、新しい要素が追加される(既存要素は更新されない)
  • id フィールドを指定すると、その ID を持つ既存要素が更新される
  • 1回目で targetId を指定しても無視され、null になる

ProtoFlux コンポーネントの追加

ProtoFlux ノード(ジェネリック型コンポーネント)を追加するには、特定の形式が必要です。

正しい形式

[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.<コンポーネント名><型>

ポイント

項目正しい形式間違った形式
アセンブリ名[ProtoFluxBindings][FrooxEngine]
名前空間FrooxEngine.FrooxEngine.ProtoFlux.CoreNodesFrooxEngine.ProtoFlux.CoreNodes
ジェネリック型<bool>, <int>, <float><System.Boolean>, `1[System.Boolean]
  • 名前空間: FrooxEngine が2回繰り返される
  • 型指定: C# エイリアス(bool, int, float)を使用する(System.Boolean ではない)
  • 記法: <> 形式を使用する(.NET のバッククォート記法 `1[...] ではない)

複合型パラメータ(Slot, User など)

スロットやユーザーなどの複合型を型パラメータに使う場合は、アセンブリ名付きの完全修飾名を使用する:

[ProtoFluxBindings]...RefObjectInput<[FrooxEngine]FrooxEngine.Slot>
正しい形式間違った形式
Slot<[FrooxEngine]FrooxEngine.Slot><Slot>, <FrooxEngine.Slot>
User<[FrooxEngine]FrooxEngine.User><User>
IButton<[FrooxEngine]FrooxEngine.IButton><IButton>

重要: プリミティブ型(int, float, bool など)はエイリアスをそのまま使い、複合型(Slot, User など)は [FrooxEngine]FrooxEngine.TypeName 形式を使う。

動作確認済みコンポーネント

ノードcomponentType
ValueInput<int>[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.ValueInput<int>
ValueAdd<int>[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Operators.ValueAdd<int>
ValueDisplay<int>[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.ValueDisplay<int>
WorldTimeFloat[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.FrooxEngine.Time.WorldTimeFloat
AxisAngle_floatQ[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Math.Quaternions.AxisAngle_floatQ
HSV_ToColorX[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Color.HSV_ToColorX
ValueFieldDrive<floatQ>[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<floatQ>
ValueFieldDrive<colorX>[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<colorX>
ValueFieldDrive<bool>[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<bool>

コード例

// ValueFieldDrive<floatQ> を追加(回転ドライブ用)
await client.addComponent({
  containerSlotId: slotId,
  componentType: '[ProtoFluxBindings]FrooxEngine.FrooxEngine.ProtoFlux.CoreNodes.ValueFieldDrive<floatQ>'
});

// WorldTimeFloat を追加(時間取得用)
await client.addComponent({
  containerSlotId: slotId,
  componentType: '[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.FrooxEngine.Time.WorldTimeFloat'
});

// HSV_ToColorX を追加(色変換用)
await client.addComponent({
  containerSlotId: slotId,
  componentType: '[ProtoFluxBindings]FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes.Color.HSV_ToColorX'
});

ProtoFlux ノード間の接続

// ValueAdd の A, B 入力に ValueInput ノードを接続
await client.updateComponent({
  id: addCompId,
  members: {
    A: { $type: 'reference', targetId: input1CompId },
    B: { $type: 'reference', targetId: input2CompId },
  }
});

// ValueDisplay の Input に ValueAdd の出力を接続
await client.updateComponent({
  id: displayCompId,
  members: {
    Input: { $type: 'reference', targetId: addCompId },
  }
});

よく使う ProtoFlux ノード

コンポーネント用途
ValueFieldDrive<T>フィールドをドライブ
ReferenceFieldDrive<T>参照フィールドをドライブ
GlobalValue<T>グローバル値
GlobalReference<T>グローバル参照

ProtoFlux ノードの配置

ProtoFlux ノードを追加する際は、各ノードを別々のスロットに配置し、適切な位置に並べることで視認性が向上します。

座標系

Resonite の座標系:

  • X軸: 左右(右が正)
  • Y軸: 上下(上が正)
  • Z軸: 前後(手前が正)

ProtoFlux ノードは**左から右(X軸方向)**に配置するのが一般的です。

配置の基本パターン

左 ────────────────────────────────────────────→ 右 (X軸)

[入力ノード群]  →  [処理ノード]  →  [出力ノード]  →  [ドライブノード]
   x=-1.5            x=-1.0           x=-0.5            x=0

複数入力がある場合

Y軸で上下にずらして配置:

// 入力1(上側)
await client.addSlot({ name: 'Input1', position: { x: -1.5, y: 0.15, z: 0 } });

// 入力2(下側)
await client.addSlot({ name: 'Input2', position: { x: -1.5, y: -0.15, z: 0 } });

// 処理ノード(中央)
await client.addSlot({ name: 'Process', position: { x: -1.0, y: 0, z: 0 } });

実践例: 回転するボックス

// 親スロット
const fluxSlot = await client.addSlot({ name: 'Flux', position: { x: 0, y: 2, z: 0 } });

// 各ノードを左から右に配置
const nodes = [
  { name: 'AxisInput',      x: -0.6, y: 0.15 },  // 回転軸入力
  { name: 'TimeNode',       x: -0.6, y: -0.15 }, // 時間取得
  { name: 'AxisAngleNode',  x: -0.3, y: 0 },     // 軸角度→クォータニオン変換
  { name: 'DriveNode',      x: 0,    y: 0 },     // 回転ドライブ
];

for (const node of nodes) {
  await client.addSlot({
    name: node.name,
    parentId: fluxSlot.data.id,
    position: { x: node.x, y: node.y, z: 0 }
  });
}

配置のコツ

項目推奨値
ノード間の水平間隔0.3〜0.5
分岐時の垂直間隔0.15〜0.3
親スロットからの相対座標使用する
  • 親スロット(例: Flux)を作成し、その下に各ノードを配置
  • 位置は親スロットからの相対座標になる
  • データフローが左から右に流れるように配置
  • 分岐がある場合は Y軸で上下にずらす

制限事項

一部の ProtoFlux ノードは追加できない場合があります:

  • 複雑なジェネリック制約を持つノード
  • 特殊な初期化が必要なノード

回避策:

  • Resonite 内で手動で ProtoFlux を作成
  • 既存の ProtoFlux をテンプレートとして複製
  • PackedObject として保存したものをインポート

デコンパイル検索 (CLI)

# コンポーネント名で検索
node dist/cli.js search --query Mesh

# コンポーネント詳細を表示
node dist/cli.js info --name PBS_Metallic

# カテゴリ一覧
node dist/cli.js categories

# カテゴリで検索
node dist/cli.js category --query "Materials"

# メンバー名で検索
node dist/cli.js member --query Smoothness

# ソースコード全文検索
node dist/cli.js grep --query "SyncPlayback"

# ソースコード表示
node dist/cli.js source --name BoxMesh

サンプルスクリプト

# ProtoFlux 1+1 を作成(ValueInput → ValueAdd → ValueDisplay)
node dist/scripts/create-flux-add.js ws://localhost:58971

# 東京タワー(詳細版)を作成
node dist/scripts/create-tokyo-tower-detailed.js ws://localhost:58971

# 東京スカイツリーを作成
node dist/scripts/create-skytree.js ws://localhost:58971

# 東京タワーを削除
node dist/scripts/delete-tokyo-tower.js ws://localhost:58971

# モダンハウス(内装付き)を作成
node dist/scripts/create-house3.js

# 街を作成
node dist/scripts/create-town.js

# 雲を作成
node dist/scripts/create-clouds.js

# すべて削除して床だけにする
node dist/scripts/reset-to-floor.js

License

MIT

Reviews

No reviews yet

Sign in to write a review