fix: Docker sandbox MCP server fails on Windows source builds — yarn workspace junctions use absolute paths
## 概要
Windows + Docker sandbox ON(ソース版 / `yarn server`)で、MCP サーバー(`mcp-server.ts`)がコンテナ内で起動に失敗し、`handlePermission not found` エラーが発生する。
`DISABLE_SANDBOX=1` では正常動作。
## 再現環境
- OS: Windows 23H2
- MulmoClaude: v0.9.1(ソース版 / `git pull` → `yarn install` → `yarn build:packages:dev`)
- Node.js: v24 / Claude Code: 2.1.187
- Docker Desktop 導入済み(WSL2 backend)
## 再現手順
1. Windows でソース版を `yarn server`(sandbox ON)で起動
2. サーバーログに `[sandbox] Sandbox ready` + `[mcp] Available tools="notify, handlePermission, ..."` が表示される(ホスト側レジストリのログであり、コンテナ内の状態は反映しない)
3. チャットで操作を実行
4. `MCP tool mcp__mulmoclaude__handlePermission (passed via --permission-prompt-tool) not found` エラーが発生
5. 利用可能ツールとして claude.ai 連携 MCP(Gmail/Calendar 等)のみが表示される
## 根本原因(推定)
yarn が Windows でワークスペースパッケージを node_modules に配置する際、macOS/Linux では相対シンボリックリンクだが、Windows では絶対パスのジャンクションを使う(Node の `fs.symlink(..., "junction")` はターゲットを絶対パスに正規化する仕様)。
macOS/Linux(正常・実機確認済み):
```
node_modules/@mulmoclaude/core → ../../packages/core
```
Docker 内で `/app/node_modules` + `/app/packages` としてマウントされるため、相対リンクが `/app/packages/core` に解決される。
Windows(問題あり・推定):
```
node_modules\@mulmoclaude\core → C:\Users\<user>\mulmoclaude-src\packages\core
```
Docker コンテナ(Linux)内にこの絶対パスは存在しないため、モジュール解決が失敗する。
補足: sandbox イメージ(`Dockerfile.sandbox`)は node:22-slim + global の claude/tsx のみで node_modules を焼き込んでいないため、コンテナ内のモジュール解決は 100% ホストの bind mount に依存する。
## 影響を受けるインポート
MCP サーバー(`server/agent/mcp-server.ts`)は Claude CLI のサブプロセスとしてコンテナ内で `tsx /app/server/agent/mcp-server.ts` として起動するが、以下のワークスペースパッケージをトップレベルで import している(子プロセスの静的 import グラフ上のワークスペースパッケージはこの 5 つで全部):
- `server/agent/mcp-tools/index.ts` → `@mulmoclaude/x-plugin`
- `server/agent/mcp-tools/manageCollection.ts` → `@mulmoclaude/core/collection/server`, `core/collection`, `core/skill-bridge`, `core/workspace-setup`
これらの import が失敗 → MCP サーバーが initialize ハンドシェイク前にクラッシュ → Claude CLI のツールレジストリに mulmoclaude のツールが一切登録されない → `--permission-prompt-tool` が参照する `handlePermission` が見つからずエラー。
なお静的 import を直すだけでは不十分で、コンテナ内 MCP child の preset/runtime plugin 読み込み(`mcp-server.ts` の `runtimeReady`、失敗時は try/catch で静的ツールのみに縮退)も junction 経由のため、修正は全ワークスペースパッケージを対象にする必要がある。
## 該当コード
- MCP サーバー config: `server/agent/config.ts` L276-315(`buildMulmoclaudeServer`、useDocker 分岐で `tsx` + `/app/server/agent/mcp-server.ts` + `NODE_PATH=/app/node_modules`)
- Docker ボリュームマウント: `server/agent/config.ts` L588-688(`buildDockerSpawnArgs`、マウント指定は L664-682)
- `node_modules` → `/app/node_modules:ro`(ジャンクションがそのまま入る)
- `packages/` → `/app/packages:ro`(ソースは存在するがリンクが壊れている)
## 未確認事項
- [ ] 普通の会話(ツール不使用)が可能かどうか — `--permission-prompt-tool` の指定先が見つからない場合に CLI がセッション開始を拒否するのか、警告だけで続行するのかが不明
- [ ] Windows 実機での `node_modules/@mulmoclaude/core` のリンク種別の確認 — ジャンクション(絶対パス)である推定だが未検証
- [ ] ジャンクションがコンテナ内でどう見えるか — Docker Desktop の file sharing レイヤーが junction をサーバー側で追従する場合、dangling symlink にならず本仮説は成立しない。`docker run --rm -v <src>/node_modules:/app/node_modules:ro mulmoclaude-sandbox ls -la /app/node_modules/@mulmoclaude/` で確認可能
## 修正方針案
**A. 追加バインドマウント方式**
`buildDockerSpawnArgs` で `platform === "win32"` の場合、ワークスペースパッケージごとに個別の `-v` マウントを追加し、壊れたジャンクションをオーバーレイする。
macOS/Linux は影響なし。
ただし dangling symlink の上へのバインドマウントは、Docker がマウント先パスを rootfs 内で解決する際に失敗するか別の場所に着地する可能性があり、要検証。
**A'. 別ルート + NODE_PATH 方式(A の安全な変形)**
ワークスペースパッケージの実体を junction を経由しない別ルート(例 `/app/workspace_modules/@mulmoclaude/<name>`)にマウントし、`NODE_PATH=/app/node_modules:/app/workspace_modules` を渡す。
Docker では tsx が CJS 出力で動く(`mcp-server.ts` 内コメント参照)ため NODE_PATH が有効で、CJS 解決は dangling symlink を「存在しない」扱いにして次の探索パスへ進むので、壊れたジャンクションはそのまま放置できる。
**B. エントリポイント修正方式**
`sandbox-entrypoint.sh` で壊れたシンボリックリンクを検出し `/app/packages/` へのリンクに置換する。
ただし node_modules は `:ro` マウントのため、read-write への変更か tmpfs オーバーレイが必要。
**C. コピー方式**
コンテナ起動前にワークスペースパッケージの実体をジャンクションなしで一時ディレクトリに構築してマウントする。
ビルド時間・ディスク消費増。
## 関連
- #1770 — npx packaged install で MCP broker パスが壊れる問題(修正済み、本 issue は別原因)
- #1920 — npx 版の gui-chat-protocol peer dep 不一致(`packages/mulmoclaude/package.json` の dependency drift が原因。root package.json を使うソース版は非該当)。症状は類似するが、本報告では起動ログが `[plugins/preset] loaded requested=4 succeeded=4` であり(#1920 該当時は `succeeded=1`)、プラグインは全て正常ロードされているため別問題と切り分けられる
关闭于 2026-07-04 3 条评论