Skip to content
業務OS Lab
Go back

スマホからAIエージェントを遠隔操作できるようにした話 — 3日間の失敗と、Claude Code Channelsという正解

「外出先でPCが開けない」問題

自分は会社で働きながら、個人のAIエージェント(Claude Code)を使って、いろいろなことを自動化してきた。

具体的にはこういうもの:

全部で50以上のスキル(自動化された機能)がある。ニュース配信、レポート生成、リサーチ、コード修正、データ分析——あらゆる「PCでやっていた作業」を自動化してきた。これらは全て、VPS(仮想サーバー)上で動くClaude Code(AnthropicのAIエージェントCLI)が実行する。

問題は、それが全部PCでしか操作できないこと。

Claude Codeはターミナル(コマンドライン)で動く。PCを開いて、ターミナルを立ち上げて、コマンドを打つ。あるいはSSHでVPSに接続して操作する。

外出先ではPCを開けない。でも「あのデータ、今すぐ確認したい」「朝のルーティン実行し忘れた」「移動中にあの提案書のドラフトを作りたい」みたいな瞬間は毎日のようにある。

つまり欲しかったのは、スマホからAIエージェントに指示を出して、結果を受け取る仕組みだった。

そもそもClaude Codeとは何か

Claude Codeは、Anthropic社が提供するAIエージェントのCLI(コマンドラインインターフェース)。ChatGPTのようなチャットAIとは根本的に違う。

普通のチャットAI:

Claude Code:

だから単なるチャットBotをスマホに入れても意味がない。Claude Codeのツール実行能力ごとスマホから使えるようにしないと、価値がない。

最初のアプローチ:SSH(Termius)

最初は力技だった。

スマホにTermius(SSHクライアントアプリ)を入れて、VPS(仮想サーバー)にSSH接続する。VPSにはClaude Codeがインストールしてあるから、ターミナルでそのまま操作できる。

[スマホ] → Termius(SSH) → VPS → Claude Code

技術的にはこれで「スマホからClaude Codeを使える」状態になった。実際に試してみた。

結論:実用的じゃなかった。

「動く」と「使える」の間には大きな溝がある。技術的に可能なことと、毎日使いたいと思えることは全然違う。

第2のアプローチ:Discord Botを自作する

1週間ほどSSHで我慢した後、もっといい方法を考えた。

スマホで毎日使っていて、UIが快適で、プッシュ通知があるアプリ。Discordだ。

自分は既にDiscordサーバーを持っていて、自動化ツールの通知先にしていた。ここにもう一つチャンネルを作って、チャットで指示を出す → 結果がチャットで返ってくる仕組みを作ればいい。

やることは明確だった:

  1. VPS上でDiscord Botを常駐させる
  2. #claude-cmd という専用チャンネルでメッセージを監視する
  3. メッセージが来たら、VPS上で claude -p "メッセージ内容" を実行する
  4. 実行結果をDiscordに返信する

claude -p というのは、Claude Codeの「非対話モード」。通常のClaude Codeはターミナルで対話的に使うが、-pオプションを付けると、一発のコマンドとして実行して結果を返してくれる。パイプやスクリプトから呼び出すための機能だ。

[スマホ] → Discord → Bot(VPS) → claude -p "指示" → 結果 → Discord返信

Claudecordとの違い

「Claudecord」という既存のDiscord Botがある。Claude APIをDiscord上で使えるようにしたもので、要はDiscordの画面でClaude(チャットAI)と会話できる。

でもこれでは足りなかった。

ClaudecordはClaude API(チャット機能)だけを使う。「この文章を要約して」みたいなテキストのやりとりはできるが、ファイルを操作したり、コマンドを実行したり、外部サービスと連携したりはできない。

自分が欲しいのは、Claude Codeのフル機能をDiscordから使えること。ファイル読み書き、コマンド実行、Git操作、50以上の自作スキル——全部使えないと意味がない。Claudecordの上位互換を作る。

1日目:Botは起動した。でも動かない。

discord.js(Node.jsのDiscordライブラリ)でBotを書いた。170行程度のシンプルなコード。

VPSにデプロイして、systemd(Linuxのサービス管理)で常駐化。起動ログを確認。

[discord-cmd] Bot ready as opsclaude#0242

Botは起動した。Discordを開いて #claude-cmd チャンネルに「テスト」と送る。

反応なし。

ログを確認する。メッセージの受信すら記録されていない。Botは起動しているのにメッセージを検知していない。

デバッグログの追加

コードにデバッグログを追加して、メッセージ受信のフィルタリングの各段階でログを出すようにした。

VPSに再デプロイ。もう一度「テスト」を送る。

今度はログが出た:

msg received: author=1478001975931961374 channel=claude-cmd
accepted: "<@&1480936114746429443>  test"

メッセージは受信していた。 しかも正しくフィルタを通過して、claude -p の実行まで進んでいた。

Discordには processing... という表示も出ている。つまりBotはメッセージを受け取り、「処理中」の表示を返し、Claude Codeの実行を開始している。

でも、そこから先が返ってこない。5分後にタイムアウト。

直接実行すると動く

VPSにSSHで入って、直接 claude -p "say hi" を実行してみる。

$ time claude -p "say hi" --output-format json
{"type":"result","result":"Hi!","session_id":"...","duration_ms":2180}

real    0m30.190s

普通に動く。 API自体は2秒、プロセス起動を含めて30秒で結果が返ってくる。

つまり、Claude Code自体は正常に動いている。Node.jsのBotから呼び出したときだけ、結果が返ってこない。

この日はここで行き詰まった。

2日目:5つの方式を検討し、全部却下する

execFile の罠

最初のコードでは execFile というNode.jsの関数を使っていた。外部コマンドを実行してコールバックで結果を受け取る、最もシンプルな方法だ。

execFile("claude", ["-p", prompt], (error, stdout) => {
  // ここが永遠に呼ばれない
});

このコールバックが永遠に呼ばれない。claude プロセスは起動していて、ログにはPIDも見えている。しかしプロセスが終了せず、コールバックも呼ばれない。

速度問題にも気づく

並行して、「仮にバグが直っても30秒はかかる」という問題にも気づいていた。claude -p は呼び出すたびにプロセスを新規起動する。プロセス起動だけで28秒。スマホからサッと使いたいのに、毎回30秒待つのは厳しい。

ここで一度立ち止まって、取りうる全ての方式を整理した。

方式A: claude -p を毎回起動(現状) — 遅い(30秒)が全機能使える。バグ修正のみ。

方式B: Claude APIを直接叩く — 速い(2-3秒)がチャットのみ。ファイル操作もコマンド実行もできない。

方式C: 常駐プロセス方式(stream-json) — Claude CLIにはstream-json入出力モードがある。プロセスを1つ立ち上げっぱなしにすれば起動コストはゼロ。最有力候補。

方式D: Agent SDKで自作 — ツールを全部自前で実装。工数が膨大。

方式E: ハイブリッド(B + A) — 会話はAPI直接、スキルは claude -p。2系統のメンテが必要。

方式Cに賭ける → 撃沈

最も有望に見えた方式C(常駐プロセス)を調査した。VPSで実際にstream-jsonモードを試す。

出力なし。

stdinを開いたままにしてみる。環境変数を変えてみる。フォーマットを変えてみる。全部ダメ。

Claude Codeの内部仕様を調査した結果:stream-jsonは常駐デーモン設計ではない。 Claude CLIは1回の呼び出しで完結する設計。stdinから継続的にメッセージを受け取る機能は存在しなかった。

方式Cは不可能。B(API直接)は機能不足。D(SDK自作)は工数過大。E(ハイブリッド)はメンテが複雑。

残ったのは方式A。バグを直して、30秒の遅延を受け入れる。

3日目:発想の転換と解決

「30秒」は本当に致命的か?

ここで発想を変えた。自分がスマホからClaude Codeを使いたいシーンを具体的にリストアップした:

ほとんどのユースケースで、30秒は問題にならなかった。

しかもスマホ経由なら「投げて放置 → 通知で結果確認」が自然なフロー。PCの前で30秒じっと待つのとは体験が違う。

認識を改めた:速度ではなく「手軽さ」が本質だった。

根本原因の修正

claude -pstdinの終了を待ってからプロセスを完了する仕様 だった。一方、Node.jsの execFile はデフォルトでstdinをパイプとして開いたままにする。

つまり:

  1. Botが execFile("claude", ...) を実行
  2. Claude Codeが起動し、処理を完了する
  3. しかしstdinがまだ開いているので、「まだ入力があるかもしれない」と待ち続ける
  4. プロセスが終了しないので、コールバックも永遠に呼ばれない

修正は2つ:

3日間悩んだ問題の原因が、stdinを閉じる1行だった。

テスト成功

修正をVPSにデプロイして、Discordに「テスト」と送る。8秒後——結果が返ってきた。

「Issue確認して」を送る。16秒後、15件のIssue一覧が返ってきた。テーブルがDiscordのマークダウンで崩れたので、コードブロックに自動変換する処理も追加した。

翌日、スレッド内の返信が動かないバグも見つけて修正(Discordのスレッドは channel.name がスレッド名になるため、親チャンネル名のチェックが必要だった)。

自作Botが完成した。

完成した翌日に知ったこと

自作Botが動いて喜んでいた翌日、あることを知った。

Claude Code v2.1.80で、「Claude Code Channels」という公式機能がリリースされていた。

Claude Code Channelsは、DiscordやTelegramなどのメッセージングアプリからClaude Codeのセッションに直接メッセージを送れる仕組みだ。VPS上でClaude Codeを常駐させておけば、スマホのDiscordからDMを送るだけで、ファイル操作もBash実行もスキル呼び出しも全部できる。まさに自分が3日間かけて自作しようとしていたもの——それが公式機能として、コマンド3つで実現できる。

# プラグインのインストール
claude plugins install discord@claude-plugins-official

# Botトークンの設定
# ~/.claude/channels/discord/.env にトークンを書く

# 起動
claude --channels plugin:discord@claude-plugins-official

しかもClaude Code Channelsには、自作Botにはなかった利点がある:

自作Bot → Claude Code Channels で何が変わったか:

特に「常駐セッション」は大きい。自分が方式Cとして調査して「不可能」と結論したもの—— Anthropicが「Claude Code Channels」として公式に実装していた。 当然、内部の仕組みを知っている開発元だからこそできること。

Claude Code Channelsに移行

迷わず移行を決めた。

自作Botを停止し、Claude Code Channelsをセットアップした。手順はシンプルだった:

  1. プラグインインストールclaude plugins install discord@claude-plugins-official
  2. Botトークン設定~/.claude/channels/discord/.env にトークンを書く
  3. 起動claude --channels plugin:discord@claude-plugins-official
  4. ペアリング — DiscordでBotにDMを送ると、認証コードが返ってくる。VPS側でそれを承認すると、自分のDiscordアカウントが許可リストに登録される
  5. 権限設定settings.json にBashやDiscord返信の権限を事前登録して、承認プロンプトなしで動くように設定

VPS上ではtmuxで常駐セッションとして走らせている。一度起動すれば、スマホからDMを送るたびにそのセッションが反応して結果を返す。

DiscordでBotにDM:「イシュー確認して」

数秒後、Issue一覧が返ってきた。承認なし、起動コストなし。

何ができるようになったか

スマホのDiscordでopsclaudeにDMを送る。それだけで:

VPS上のClaude Codeがフル機能で起動する。

具体的な使用例:

朝の通勤電車で: 「実行して」→ ニュース配信、未読メッセージのダイジェスト、カレンダー取得が一括実行される

外出先で: 「今週のKPI確認して」→ スプレッドシートからデータを読み取り、前日比・前週比の分析結果が返ってくる

打ち合わせ前に: 「競合の最新動向を調べて」→ Webから情報を収集し、差別化ポイントと示唆のサマリーが返ってくる

帰宅後のソファで: 「Issue確認して」→ 今のタスク一覧が返ってくる。「この提案書のドラフト作って」→ その場で生成される

PCでの作業の体感8割がスマホで完結する。

3日間で学んだこと

1. 「動く」と「使える」は別物

SSHは「動く」。でもスマホで毎日使いたいかと聞かれたらNo。UIが用途に合っていなかった。

技術的に実現可能なことと、ユーザー(今回は自分)が実際に使い続けるものの間には、大きなギャップがある。「動く」で止まらず、「使える」まで持っていくことが大事。

2. 原因の小ささと被害の大きさは比例しない

3日間悩んだバグの原因が proc.stdin.end() の1行だった。

execFile がデフォルトでstdinを開いたままにすること、claude -p がstdinの終了を待つこと。この2つの仕様の組み合わせが、プロセスの永久ハングを引き起こしていた。

どちらの仕様も単体では問題ない。組み合わせたときに初めてバグになる。こういう「仕様の隙間」に落ちるバグは、ドキュメントにも書いていないし、ググっても出てこない。

3. 全部の要件を満たそうとすると何もできない

「速度もフル機能もUIも全部完璧に」と考えると、永遠に完成しない。

5つの方式を検討して全部不十分だった。30秒の起動コストを受け入れた瞬間に、最もシンプルな方式A(バグを直すだけ)が正解だとわかった。

完璧を目指すより、「80点でいいから今日使えるもの」を作る方が価値がある。

4. ユースケースの解像度を上げると、必要な性能が変わる

最初の要件は「スマホからClaude Codeを使いたい」だった。これだと「速くないとダメ」「全機能使えないとダメ」と、全てが要件に見える。

「外出先でPCを開けないから、スマホで診断やルーティンを発火して、結果をプッシュ通知で受け取りたい」まで解像度を上げると、30秒の遅延は問題にならないことがわかった。

要件が曖昧なまま技術検討を始めると、必要以上に難しい問題を解こうとしてしまう。

5. 完成した後にもバグは出る

完成して喜んだ翌日、スレッド内の返信が動かないことに気づいた。Discordのスレッドでは channel.name がスレッド名になり、チャンネル名のチェックに引っかかって無視されていた。

リリースしてユーザー(自分)が使って初めて見つかるバグは必ずある。 完璧にしてからリリースするのではなく、早くリリースして早く直す方が速い。

6. 自分で作ったからこそ、公式の価値がわかる

3日間かけて自作したBotが、公式プラグインの3コマンドで実現できた。

「最初から公式を待てばよかった」と思うかもしれない。でも自分はそう思わない。

自作Botを作る過程で、以下を深く理解した:

公式プラグインがなぜ「常駐セッション」を採用しているのか、なぜ「DM」を使うのか、なぜ「MCP統合」なのか——全部、自分で失敗したから理解できる。

遠回りは、無駄じゃなかった。

7. 調査して却下したプロセスにも価値がある

方式C(常駐プロセス)の調査に半日かけた。結果は「不可能」だった。方式B、方式D、方式Eも検討して全部却下した。

一見、無駄に見える。でもこの調査があったから、「方式Aが最善である」と自信を持って判断できた。選択肢を全て検討した上での判断と、最初から1つしか知らない状態での判断は、たとえ結論が同じでも質が違う。


※ この記事のDiscord Bot(200行)もNote記事自体も、全てAI(Claude Code)が生成した。自分が書いたのは日本語の指示だけ。

筆者: コンサル・事業会社を経て中小企業の経営に参画。コードは1行も書けないが、Claude Codeで66個の業務スキルを構築し、経営業務の8割を自動化。日々の実験はXで発信中。


Share this post on:

Previous Post
生成AIの一番の価値は「仕事するだけでデータが溜まる」ことだった
Next Post
AI時代に最も価値がある能力は「プロンプト力」じゃない — 解像度と構造把握の話