- ローカルで動いているClaude Codeを、既存のWebアプリの中にそのまま埋め込むSkillを作った
- ブラウザ側にxterm.jsのターミナル、PC側にLocal Agentを置き、WebSocketでClaude Code CLIをそのまま中継する構成
- GitHubで公開している:
claude-code-web-embed-skill
実際に動いている様子はこちら。
以下、なぜこれを作ったか、どう動くか、使ってみて何を考えたかを書く。
ブラウザとターミナルを何度も往復するのに疲れた
Claude Codeでフロントエンドを触っていると、こういうループが延々と続く。
ブラウザで画面を見る。ターミナルに戻ってClaude Codeに直す場所を伝える。終わったらまたブラウザに戻って確認する。
コードを書く部分はもうほとんどClaude Codeに任せられるようになった。なのに、「画面を見る場所」と「指示を出す場所」が分かれたままなのが、地味にずっと引っかかっていた。
だったら、その2つを同じ画面に置いてしまえばいいんじゃないか。というのが出発点。
作ったもの
構成はシンプルで、3つのパーツからできている。
Existing Web UI
└─ iframe / framework wrapper
└─ Terminal Frontend(xterm.js)
│ WebSocket
▼
Local Agent(PTY + Security)
│
▼
Claude Code CLI
ブラウザ側にはxterm.jsのターミナルを表示するだけ。PC上ではLocal Agentが動いていて、PTY上ですでにインストール済みのclaudeコマンドを起動する。あとはブラウザとLocal Agentの間を、入出力だけのWebSocketで繋ぐ。
やっていることは地味で、Claude Code自体には一切手を加えていない。独自のチャットUIとして再実装したわけでもない。普段ターミナルで使っているClaude Codeを、そのままWeb UIの中から操作できるようにしただけ、というのが一番正確な説明だと思う。
「Claude CodeのWeb版」を作ったわけではない、という話
似たようなことをやろうとすると、大体は「独自のチャット欄を作ってAPIに繋ぐ」方向に進みがちだと思う。実際、v0やBolt、Lovableのようなツールはそちら側のアプローチで、ゼロから専用のAIコーディング体験を作っている。
ただ自分がやりたかったのはそれではなかった。独自チャットを作ると、Claude Codeがすでに持っている
- ファイルの読み書き
- Bashコマンドの実行
- Permission Dialog
- Slash Command
- Git操作
- MCP
- セッション管理
を全部作り直すことになる。しかもClaude Code本体がアップデートされるたびに、追いかけ続けないといけない。それは正直やりたくなかった。
なので今回は「新しいツールを作る」のではなく、今使っているClaude Codeを、別のWeb画面の中に持ち込むという方向にした。PTY上でそのまま動かしているので、Thinking、Tool Use、Permission Dialog、Bash、Git、MCPも、普段どおりの挙動のまま使える。
既存のWebアプリが、そのままVibe Coding環境になる
これの本題はここだと思っている。「Claude Codeをブラウザで使えるようにした」というより、すでにあるWeb画面にClaude Codeを足せる、という方が近い。
プレビューを見ながら「モバイル表示で崩れている箇所を直して」のように頼むと、Claude Codeがローカルのリポジトリを読み、コードを変え、必要ならコマンドも叩く。結果は同じ画面のプレビューにすぐ反映される。見る→指示する→確認する→また指示する、というループが、タブを切り替えずに1画面で回るようになる(動画の通り)。
Figma Makeにあるような「選択モード」もついでに作った。有効にすると、動いている画面上の要素をそのままクリックして対象を指定できる。クリックした瞬間に送信されるわけではなく、選んだ要素は送信前のコンテキストとして保持される。これがあると、ユーザーが説明すべきなのは**「どこを変えるか」ではなく「どう変えたいか」だけ**になる。Vibe Codingでは、プロンプトを丁寧に書くことよりも「今どこの話をしているか」をAIと正確に共有できているかの方が、結果に効いてくると感じている。
パネルの位置もBottom / Right / Left / Floatingから実行中に切り替えられるようにしていて、選んだ位置はlocalStorageに保存される。このあたりの挙動も動画で確認できる。
リロードしても会話が消えない
ターミナルをただ埋め込むだけだと、ページをリロードするたびにClaude Codeのプロセスまで巻き添えで再起動してしまう。それだと開発ツールとしては正直使い物にならない。
そこで、ブラウザ側にセッションIDを持たせ、WebSocketが一時的に切れてもLocal Agent側でPTYをしばらく生かしておくようにした。リロードすると同じプロセスに再接続し、スクロールバックも復元される。短い切断なら、会話を最初からやり直す必要はない。
これによって、ページ内の一時的なウィジェットではなく、ある程度腰を据えて使える開発ツールとして成立するようになった。
なぜSkillという形にしたか
xterm.js、WebSocket、PTYを組み合わせるだけなら、この仕組み自体はそんなに難しくない。
ただ、既存プロジェクトに毎回導入しようとすると、確認しないといけないことが結構多い。
- 使っているフレームワークは何か
- パッケージマネージャーは何か
- 既存レイアウトはどうなっているか
- ターミナルをどこに置くか
- iframeかフレームワーク用ラッパーか
- Local Agentは何語で書くか
- ポートとOriginはどうするか
- 作業ディレクトリはどこに限定するか
- セキュリティ設定はどうするか
これを毎回ゼロから考えるのは面倒だし、判断がブレる。なので導入作業そのものをSkillにしてしまうことにした。
対象プロジェクトでSkillを呼ぶと、次の3ステップが走る。
1. Analyze プロジェクトを解析して導入方法を決める
2. Scaffold Local Agentとフロントエンド資産を配置する
3. Integrate 既存UIに組み込み、接続を確認する
リポジトリにはSKILL.mdだけでなく、解析手順・通信プロトコル・セキュリティ要件・Local Agent実装・フロントエンド・導入後のドキュメントまで含めている。
.
├── SKILL.md
├── steps/
│ ├── step1-analyze.md
│ ├── step2-scaffold.md
│ └── step3-integrate.md
├── references/
│ ├── protocol.md
│ ├── project-analysis.md
│ └── security.md
└── assets/
├── local-agent/
│ ├── node/
│ └── python/
├── frontend/
└── docs-templates/
毎回Claude Codeにゼロから実装させるのではなく、用意したテンプレートを配置して、プロジェクト固有の設定だけを変える形にしている。
Local AgentはNode.jsとPythonの2択
フロントエンドとLocal Agentの間には、input output resize status exit error ping/pong だけを扱う小さなJSONプロトコルを定義した。フロントエンドはこのプロトコルさえ知っていればいいので、Local Agentの実装言語には依存しない。
今のところNode.js版(ws + node-pty)とPython版(標準のpty + websockets)を用意している。Windowsでは標準のptyが使えないので、その場合はNode.js版一択になる。プロトコルさえ守れば、Go版やRust版を足すのも難しくないはず。
デフォルトはiframe
組み込み方式はiframeをデフォルトにした。ターミナルフロントエンドをLocal Agent側から独立した画面として配信し、既存アプリはそれをiframeとして表示する形。
こうしておくと、React、Next.js、Vue、Nuxt、Svelte、Astro、Vite、Vanilla JSなど、フロントエンド技術が何であっても同じ仕組みが使い回せる。既存アプリ側に入れる変更を最小限にできるのも大きい。必要ならReact/Vue向けのラッパーも用意している。
「ローカルで動く」の意味、正確に書いておく
このSkillには、通信やリポジトリの中身を中継する独自クラウドはない。Web UIとLocal Agentの通信はPC内で完結する。
ただしClaude Code自体は、普段どおり設定されたモデルプロバイダーにプロンプトやコードコンテキストを送信する。つまり完全にオフラインでAIが動くわけではない。
ローカルにあるもの
・Local Agent / PTY / Claude Codeプロセス
・リポジトリ / 開発サーバー / Web UIとのブリッジ
外部通信するもの
・Claude Codeと、設定されたモデルプロバイダーの間の通信
ここは誤解されやすいところなので、READMEにも明記している。
セキュリティで想定していること
Local AgentはClaude Codeとローカルリポジトリを操作できる。つまり、乗っ取られたときの被害が普通のWeb APIより大きい。想定している脅威は主に2つで、同一ネットワーク上の第三者からの接続と、悪意あるページやブラウザ拡張によるOrigin偽装。この2つに対して、次を必須にしている。
-
127.0.0.1だけで待ち受ける - 接続可能なOriginを制限する
- WebSocket接続にセッショントークンを要求する
- Claude Codeを起動する作業ディレクトリを限定する
- 同時セッション数を制限する
- Agent終了時に子プロセスを停止する
- 任意のShellコマンドを実行できる公開APIは作らない
WebSocket接続時にOrigin・トークン・セッション数を確認してからPTYを起動する形にしていて、「localhostだから安全」という前提には意図的にしていない。
使い方
git clone https://github.com/santa-monica-johnson/claude-code-web-embed-skill
cd claude-code-web-embed-skill
ln -s "$(pwd)" ~/.claude/skills/claude-code-web-embed
(シンボリックリンクの代わりにcp -Rでコピーしてもいい)
対象プロジェクトをClaude Codeで開いて、こう頼む。
Embed a Claude Code terminal into this project's web UI
または直接呼び出す。
/claude-code-web-embed
あとはSkillがプロジェクトを解析し、埋め込み方法・Local Agentの実装・配置場所・ポートやOriginを決めて、必要なファイルを配置してくれる。
今のところの限界
正直に書いておくと、まだ弱い部分もある。
- iframe前提の制約:親フレームとの通信やCSP設定に癖のあるプロジェクトでは、追加の調整が要る
- Local Agentが落ちたときの復旧:現状は再起動して再接続する形で、自動復旧の仕組みはまだない
- 複数人での同時利用は想定していない:あくまで1人の開発者がローカルで使う前提の設計
- 大きいリポジトリでのパフォーマンス:スクロールバックの復元やPTYの応答性は、まだ大規模プロジェクトで十分に検証できていない
このあたりは使いながら直していくつもり。
何に使えるか
完成したIDEを作ったつもりはない。あくまで、任意のWebアプリをClaude Codeと一緒に使えるローカル開発環境に変えるための土台。
- 開発中の画面プレビュー
- コンポーネントカタログやStorybookのようなUI確認環境
- 社内向けの開発ツール
- テスト結果やログを見ながら直すダッシュボード
- 要素を直接選んで編集するビジュアル編集画面
「Claude Code+プレビュー」なら画面を見ながら直せる環境に、「Claude Code+要素選択」なら対象を直接指定して直せる環境になる。周りに何を置くかで、用途に特化したVibe Coding環境が作れる。
作ってみて考えたこと
AIコーディングツールは、専用IDEの中でしか使えないものではないと思う。今はCursorやVS Code、ターミナルなど「AIが動く場所」を選んで、そこに開発作業を持ち込む形が主流だけど、逆に自分が普段作業している画面の方へ、AIを持ち込むという発想もあっていいはずだ。
画面を見るツールなら、その画面を見ながらコードを直す。要素を選べるツールなら、選んだ対象について指示を出す。新しいエージェントを毎回作る必要はなくて、Claude Codeはそのまま使い、作業に合ったインターフェースを周りに用意すればいい。
今回作ったSkillは、その「周り」と「Claude Code」を繋ぐ部分を、毎回考え直さなくていいように共通化したもの。完成品というより、自分なりのVibe Coding環境を作るための土台として公開した。
