Skip to main content
Glama
rassi0429

ResoniteLink MCP

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"
      }
    }
  }
}

利用可能なツール

ツール

説明

connect

Resonite に接続

disconnect

接続を切断

get_slot

スロット情報を取得

find_slot

名前でスロットを検索

add_slot

スロットを追加

remove_slot

スロットを削除

update_slot

スロットを更新

add_component

コンポーネントを追加

get_component

コンポーネント情報を取得

update_component

コンポーネントを更新

remove_component

コンポーネントを削除

import_texture_file

テクスチャをファイルからインポート

search_components

コンポーネントを検索

get_component_info

コンポーネント詳細を取得

list_categories

カテゴリ一覧

search_by_category

カテゴリで検索

search_by_member

メンバー名で検索

get_component_source

ソースコードを取得

grep_source

ソースを全文検索

CLI として使用

WebSocket URL の指定方法

CLI は WebSocket URL を以下の優先順位で決定します:

  1. コマンドライン引数 --url (最優先)

  2. 環境変数 RESONITELINK_URL

  3. デフォルト値: ws://localhost:29551

環境変数で設定する例:

# Windows (PowerShell)
$env:RESONITELINK_URL = "ws://localhost:29469"

# Windows (コマンドプロンプト)
set RESONITELINK_URL=ws://localhost:29469

# Linux/Mac
export RESONITELINK_URL=ws://localhost:29469

環境変数を設定すれば、毎回 --url オプションを指定する必要がなくなります。

基本的なコマンド

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

# 特定のスロット情報を取得
node dist/cli.js get-slot --id <slotId> --depth 1

# コンポーネント情報も含めて取得
node dist/cli.js get-slot --id <slotId> --components

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

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

# 親スロットを指定してスロットを追加
node dist/cli.js add-slot --parent <parentId> --name MySlot --position 0,1,0 --scale 1,1,1

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

# スロットを更新
node dist/cli.js update-slot --id <slotId> --name NewName --position 0,2,0

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

# コンポーネントを取得
node dist/cli.js get-component --id <componentId>

# コンポーネントを削除
node dist/cli.js remove-component --id <componentId>

# スロット階層をツリー表示
node dist/cli.js tree --depth 3

# WebSocket URLを指定(異なるポートの場合)
node dist/cli.js root --url ws://localhost:9422 --depth 2

ライブラリとして使用

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 (...) のようなエラーがスローされます。

テクスチャインポート

ResoniteLink のアセットインポートAPIを使用して、テクスチャをインポートできます。

ファイルからインポート

// Resoniteホストのローカルファイルからテクスチャをインポート
const result = await client.importTexture2DFile({
  filePath: 'C:/path/to/texture.png'
});

if (result.success) {
  console.log('Asset URL:', result.assetURL);
  // assetURL を StaticTexture2D などに設定可能
}

生データからインポート

// RGBAピクセルデータからテクスチャをインポート
const width = 256;
const height = 256;
const rawData = Buffer.alloc(width * height * 4); // RGBA, 4 bytes per pixel

// ピクセルデータを生成(例: グラデーション)
for (let y = 0; y < height; y++) {
  for (let x = 0; x < width; x++) {
    const offset = (y * width + x) * 4;
    rawData[offset] = x;         // R
    rawData[offset + 1] = y;     // G
    rawData[offset + 2] = 128;   // B
    rawData[offset + 3] = 255;   // A
  }
}

const result = await client.importTexture2DRawData({
  width,
  height,
  colorProfile: 'sRGB',  // 'sRGB' または 'Linear'
  rawData
});

if (result.success) {
  console.log('Asset URL:', result.assetURL);
}

注意事項

  • filePath は Resonite が動作しているホストのローカルパス

  • assetURL はセッション内でのみ有効(一時的なURL)

  • サポートされる形式: PNG, JPG など一般的な画像形式

  • 生データは RGBA フォーマット(1ピクセル4バイト)

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

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_Metallic

PBRマテリアル

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 }

float2

2Dベクトル

{ $type: 'float2', value: { x: 1, y: 1 } }

float3

3Dベクトル

{ $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: [...] }

empty

出力メンバー

{ $type: 'empty', id: 'Reso_XXXXX' }

empty 型(ProtoFlux 出力メンバー)

ResoniteLink の更新により、ProtoFlux ノードの出力メンバー$type: "empty" として返されるようになりました。

背景

ProtoFlux ノードには入力(SyncRef)と出力(NodeValueOutput)があります:

  • 入力: 値を受け取る(reference型で返される)

  • 出力: 値を出力する(以前は返されなかった → 今は empty 型で返される

例: GlobalTransform ノード

{
  "componentType": "...GlobalTransform",
  "members": {
    "Instance": { "$type": "reference", "targetId": null, ... },
    "GlobalPosition": { "$type": "empty", "id": "Reso_82E" },
    "GlobalRotation": { "$type": "empty", "id": "Reso_82F" },
    "GlobalScale": { "$type": "empty", "id": "Reso_830" }
  }
}

出力メンバーの参照方法

出力メンバーの id を使って、他のノードの入力に接続できます:

// GlobalTransform の GlobalPosition 出力を取得
const globalTransformComp = slotData.data?.components?.find(c =>
  c.componentType?.includes('GlobalTransform')
);
const globalPositionId = globalTransformComp.members.GlobalPosition.id; // "Reso_82E"

// ValueSub の A 入力に GlobalPosition を接続
await client.updateComponent({
  id: subComp.id,
  members: {
    A: { $type: 'reference', targetId: globalPositionId },  // 出力IDを直接参照!
  }
});

以前との違い

項目

以前

現在

出力メンバー

JSONに含まれない

$type: "empty" で返される

複数出力ノードの参照

コンポーネントID全体を参照 → エラー

出力IDを個別に参照 → 成功

これにより、GlobalTransform のような複数出力を持つノードも正しく接続できるようになりました。

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.CoreNodes

FrooxEngine.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

-
security - not tested
F
license - not found
-
quality - not tested

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rassi0429/resolink-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server