結論(先に書く)
- Windows + WSL 環境で Claude Code から MCP サーバーを
npxで起動すると、UNC パス(\\wsl.localhost\...)がカレントディレクトリになって npm が即死する - 解決は MCP サーバー設定に
cwdを明示するだけ。commandをいじらない - npm のグローバル install も、UNC をルートにした PowerShell も、ぜんぶ必要なかった
たぶん同じ罠で半日溶かしている人がいるはずなので、その手順をそのまま残しておく。
何が起きていたか
僕はいま Claude Code で自律エージェントを回している。エージェント側から外部サービスを叩くために MCP サーバーを足すのは日常作業なんだけど、ある日 claude-mem 系の MCP を npx 起動で追加しようとして、見たことのないエラーで止まった。
ログを抜くとだいたいこれだった。
npm error code ENOENT
npm error syscall spawn
npm error path \\wsl.localhost\Ubuntu\home\syake\workspace\company
npm error errno -4058
npm error enoent spawn \\wsl.localhost\... ENOENT
要するに npm が UNC パスをカレントとして実行されていて、子プロセスを生成できない。Windows の cmd.exe / node.exe は歴史的に UNC を cwd に取れない(CMD does not support UNC paths as current directories)。pushd で一時的にドライブレターを割り当てる、みたいな回避が必要なやつ。
Claude Code 側がエージェント実行時に作業ディレクトリを WSL 側のパスにしているので、npm を Windows ホストから呼ぶと地雷を踏む構図。
最初に試して失敗したこと
行き当たりばったりで色々やった。順番に書く。全部ダメだった理由つき。
失敗1:npm install -g でグローバルに置いた
npm install -g some-mcp-server
「npx がパス解決に失敗してるなら、グローバルに置いて直接呼べばいいじゃん」と思ったやつ。でも結局 Claude Code 側が MCP サーバーを起動するプロセスの cwd を UNC のまま渡してくる ので、some-mcp-server バイナリ自体は起動できても、その中で npm や node が再帰的に呼ばれた瞬間に死ぬ。表面の command を変えても根本は解決しない。
失敗2:MCP 設定の command を wsl bash -c "..." で包んだ
{
"mcpServers": {
"some-mcp": {
"command": "wsl",
"args": ["bash", "-lc", "npx some-mcp-server"]
}
}
}
WSL を経由させれば cwd 問題は消える、という発想。動くことは動いた。だけど stdio の改行コード差分で MCP のハンドシェイクが壊れた。\r\n と \n が混ざって JSON-RPC のフレームが切れる。これは別問題として深い穴があるので避けた。
失敗3:pushd でドライブを割り当てる起動スクリプト
PowerShell の pushd \\wsl.localhost\Ubuntu\... は一時的にドライブを割り当てて UNC を解消してくれる。スクリプトでラップして MCP 起動コマンドにした。これも動く。動くけどラッパースクリプトを保守する未来が見えて捨てた。外部依存を増やす解決はだいたい間違っている。
正解:MCP 設定に cwd を1行追加するだけ
Claude Code の MCP サーバー定義は command と args だけじゃなく cwd(作業ディレクトリ) を取れる。これを Windows 側の通常パス(C: ドライブ上のどこか)に向けてやれば、npm が落ちる原因が消える。
~/.claude.json(または ~/.config/claude/claude.json)の該当箇所をこう書き換えた。
{
"mcpServers": {
"some-mcp": {
"command": "npx",
- "args": ["-y", "some-mcp-server"]
+ "args": ["-y", "some-mcp-server"],
+ "cwd": "C:\\Users\\syake\\.claude\\mcp_workdir"
}
}
}
mcp_workdir は空のフォルダを Windows 側に1個用意するだけ。mkdir して終わり。
New-Item -ItemType Directory -Force `
-Path "C:\Users\syake\.claude\mcp_workdir" | Out-Null
これで Claude Code が MCP サーバーを起動するときの cwd が Windows ローカルになる。npx も node もちゃんと動く。npm のグローバル install は不要に戻せた。WSL 経由のラッパーも要らない。
なぜこれで直るか(一応の理屈)
Node.js / npm が子プロセスを spawn するとき、Windows 上では cwd が ローカルファイルシステム上の有効なパスであることが暗黙の前提になっている。UNC はネットワークパス扱いで、レガシーな CMD レイヤーが弾く。
Claude Code は親プロセスの cwd を引き継ぐデフォルト挙動だけど、MCP サーバー設定で cwd を明示すると その値で子プロセスを起動してくれる。MCP サーバー本体は stdio で会話するから cwd がどこだろうが機能には影響しない。だから「Windows 側の安全な空フォルダ」を指してやれば、command をいじらず根本だけ直る。
ここに気づくまでが長かった。ドキュメントの cwd フィールドの説明は素っ気なくて、UNC の文脈で書かれていないので、まさかこれが効くと最初は思わなかった。
動作確認のコマンド
設定を直したら、Claude Code を再起動して MCP の接続を見る。
claude mcp list
該当サーバーが connected になっていれば終わり。failed のままなら、以下のどれかを疑う:
-
cwdのパスが実在しない(タイポ・存在しないドライブ) -
cwdを WSL パス(/home/...)にしてしまった → Windows パスで書く -
npx自体が PATH にない → Node.js 本体の install から見直し
学び
この手の「環境がレイヤーをまたぐところでだけ壊れる」バグは、表面の症状から本質に辿り着くまでに毎回時間が溶ける。ログには ENOENT としか出ないし、ググっても古い CMD の話が出てくるだけで MCP のコンテキストに当たらない。
今回の教訓は1個だけ。外側のレイヤー(npm の install 戦略、ラッパースクリプト)をいじる前に、設定ファイルが受け取れるパラメータを全部読む。cwd は最初から仕様にあったし、一行で済んだ。コードは哲学の実装、というけれど、設定ファイルもまた哲学の実装で、書いた人の意図を読み逃すと半日が消える。
次回予告
次は MCP サーバーを 自作する側の話を書く予定。Python で stdio MCP サーバーを最小構成で書いて、Claude Code から呼ぶまで。fastmcp を使うとどれくらい楽になるか、逆にどこで嵌るか。実装しながらメモする。
— Sai
この記事が役に立ったら:僕が自律エージェントを動かすときに実際に使っているプロンプトを2つのパックにまとめました — 自律エージェント用プロンプト100選 と Claude Code パワーユーザー向けプロンプト。どれも「まず動かす」発想で、ターミナルに貼って即使えます。
Top comments (0)