はじめに
Claude CodeでレビューしてほしいテキストをCodexの画面にコピーし、返ってきた指摘をまたClaude Codeに貼り直す。複数のCLIエージェントを併用していると、この「コピペの伝令役」を人間がやる時間がじわじわ積み上がる。MCPサーバーを立てれば解決しそうだが、ツールを跨いだメッセージングのためだけに常駐プロセスと設定ファイルを増やすのは正直気が重い。
agmsg は、この橋渡しを bash と sqlite3 だけで済ませるOSSツールだ。デーモンもMCPサーバーも使わず、1つのSQLiteファイルをClaude CodeやCodexが直接読み書きすることでエージェント間の会話を成立させるという。本当にサーバーレスで動くのか、実際にインストールしてエージェント同士にメッセージを送らせて確かめた。
この記事で学べること
-
agmsgのインストールから2エージェント間の送受信までの実際の手順 - SQLiteファイルの中身(スキーマ・ジャーナルモード)を直接確認した結果
- MCPを使わない設計が何を犠牲にしているか(欠けている機能)
対象読者
- Claude CodeとCodex・Gemini CLIなど複数のCLIエージェントを併用している方
- MCPサーバーを増やさずにエージェント間連携を試したい方
前提環境
- OS: Ubuntu 24.04 LTS相当のクラウドコンテナ(Claude Code on the web の実行環境)
- bash: 5.2.21
- sqlite3: 3.45.1(未導入だったため
apt-get install -y sqlite3で追加) - agmsg:
app-v0.2.0(2026-07-16リリース。git cloneでmainを取得)
TL;DR
-
agmsgはbash+sqlite3のみで動くCLIエージェント間メッセージングツール。MCPサーバー・デーモン・ネットワーク通信を一切使わない。 - 実際にteamへ2エージェントを登録し、送信→受信→履歴表示までコマンドラインだけで動作することを確認した。
- SQLiteは
WAL(Write-Ahead Logging)モードで動いており、複数リーダー・単一ライターの競合回避はデータベース側の機能に委ねられている。 - 送信順の保証はSQLiteの行IDが担うが、「同じタスクを2エージェントが同時に拾う」ような排他制御(claim/lock)はv1時点では実装されていない。
背景・課題
エージェント間連携の実装というと、まずMCPサーバーを書く発想になりやすい。しかしMCPはツール呼び出しのプロトコルであって、「エージェントAからエージェントBへの非同期メッセージ」を素朴に実現する用途には、サーバーのライフサイクル管理や認証設定が過剰装備になりがちだ。
agmsg の作者はこの用途を、サーバーを持たない構成に倒した。README では「MCPではない。MCPサーバーも追加ランタイムもいらない、ただの bash+sqlite3」と明言されている1。設計判断としては単純だが、単純さの分だけ「本当に複数エージェントの同時書き込みに耐えるのか」という疑問が残る。この疑問を実機で潰していく。
やったこと
ステップ1: 環境構築とインストール
このクラウド環境には sqlite3 コマンドが入っていなかったため、まず導入した。
apt-get install -y sqlite3
git clone --depth 1 https://github.com/fujibee/agmsg.git
cd agmsg
./install.sh --cmd agmsg
インストール先は ~/.agents/skills/agmsg/ に固定されており、Claude Code用のスラッシュコマンド /agmsg が ~/.claude/commands/ に追加された。
ステップ2: 2エージェントをチームに登録する
agmsg はチーム単位でエージェントを束ねる。CLIから直接スクリプトを叩き、Claude Code役の reporter とCodex役の reviewer を同じチームに参加させた。
scripts/join.sh qiita-demo reporter claude-code <project-path>
# → Created team: qiita-demo / Joined team qiita-demo as reporter
scripts/join.sh qiita-demo reviewer codex <project-path>
# → Joined team qiita-demo as reviewer
scripts/team.sh qiita-demo
# → reporter (claude-code) / reviewer (codex) — 2 member(s)
ステップ3: メッセージを送受信する
scripts/send.sh qiita-demo reporter reviewer "PRの差分をレビューして。対象は scripts/publish-qiita.js"
# → Sent to reviewer in team qiita-demo
scripts/inbox.sh qiita-demo reviewer
# → 1 new message(s):
# [2026-07-17T04:41:21Z] reporter: PRの差分をレビューして。対象は scripts/publish-qiita.js
scripts/send.sh qiita-demo reviewer reporter "確認したにゃ。1スロット1本のクランプはOK。429ハンドリングも良さそう。"
scripts/history.sh qiita-demo
# → reviewer → reporter / reporter → reviewer の2件が時系列で表示される
送信・受信・履歴表示のいずれもHTTPリクエストを1本も発生させず、ローカルのファイルI/Oだけで完結した。実運用ではここに、Claude Codeの Monitor ツールを使ったリアルタイム受信(monitor モード)か、ターン終了時にStopフックが受信箱を確認する turn モードが被さり、人間が inbox.sh を手で叩かなくても新着メッセージが本文に流れ込む。
仕組み: SQLite1枚が「共有の床」になる
実際に作られたデータベースの中身をそのまま覗いてみた。
sqlite3 ~/.agents/skills/agmsg/db/messages.db ".schema"
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
team TEXT NOT NULL,
from_agent TEXT NOT NULL,
to_agent TEXT NOT NULL,
body TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
read_at TEXT
);
CREATE INDEX idx_unread ON messages(team, to_agent, read_at) WHERE read_at IS NULL;
CREATE INDEX idx_history ON messages(team, created_at DESC);
テーブルは1枚だけで、未読抽出用と履歴表示用の2つのインデックスが張られている。ジャーナルモードを確認すると、期待どおり wal だった。
sqlite3 ~/.agents/skills/agmsg/db/messages.db "PRAGMA journal_mode;"
# → wal
WALモードでは、書き込みは専用のログファイルに追記され、読み取り側は既存のデータベース本体を参照し続けられる。これにより複数の読み取りプロセスと1つの書き込みプロセスが同時に動いても、読み取り側がロック待ちでブロックされにくい2。agmsg が「デーモンなしで複数エージェントの同時アクセスに耐える」と主張できる根拠は、この一点に集約されている。送信順の一貫性も、SQLiteが自動採番する id(AUTOINCREMENT)と created_at が担っており、agmsg 自身が独自の順序管理ロジックを持っているわけではない。
MCPとの違い
比較すると、両者は競合というより住み分けの関係にある。
| MCPサーバー | agmsg | |
|---|---|---|
| 常駐プロセス | 必要 | 不要 |
| 通信路 | ネットワーク/stdio | ローカルファイル(SQLite) |
| 主な用途 | エージェントからツール・外部システムを呼び出す | エージェント同士が対等にメッセージを送り合う |
| 認証・権限設定 | サーバー側で管理 | OS のファイル権限に委譲 |
README でもこの点は明確に切り分けられており、「agmsgは既存のMCP構成と並行して動かせる、独立したスタック」と説明されている1。ツール呼び出しの標準化はMCPに任せ、エージェント間の非同期な一言二言だけをagmsgに任せる、という分担が実態に近い。
ハマりポイント
ポイント1: Codexには Monitor ツールがない
Claude Codeはセッション中に受信箱をストリームで監視できる Monitor ツールを持つが、Codexにはこれに相当する仕組みがない。そのためCodexが一度アイドル状態に入ると、後から send.sh で送られたメッセージにCodex自身は気づけない。README は「spawn 時に --boot-prompt でタスクを渡すのが、Codexピアに一撃でゴールを渡す唯一の方法」と明記しており、Codexを会話の受け手として使うなら、起動直後にタスクを渡すか、turn モード(ターンの合間にStopフックが確認する方式)を前提にする必要がある1。
ポイント2: 同時に同じタスクを2エージェントが拾う排他制御はない
FAQには「2つのClaude Codeインスタンスが同じ役割を購読していたら、両方が同じ受信メッセージを見てしまう。誰が処理するかを決めるclaim/leaseの仕組みはv1にはない」とはっきり書かれている1。actas コマンドが同一ロールの二重セッションを防ぐロックは持っているが、これは「役割の重複登録」を防ぐものであり、「届いたタスクをどちらが処理するか」という業務ロジック側の排他制御はプロンプト側の設計に委ねられている。複数エージェントに同じ役割で並列作業をさせる構成を組む場合、この境界線を把握したうえでプロトコル(誰が拾うかのルール)を自分のプロンプトに書き込む必要がある。
著者視点の発見ポイント
READMEを読んだ時点では「本当に競合しないのか」が一番の疑問だったが、.schema と PRAGMA journal_mode を直接叩いて確認すると、agmsg は独自の同期プロトコルを実装していたわけではなく、SQLiteのWALモードが提供する読み書き分離にほぼそのまま乗っていることが分かった。つまり信頼性の土台はSQLiteという枯れた実装に委譲されており、agmsg 自身のコードは「誰が誰に何を送ったか」を記録するだけの薄い層になっている。設計を小さく保つほど検証すべき独自ロジックが減るという、地味だが実務的に効くトレードオフだ。
GitHubのスター数は2026年6月19日時点の440から、この記事の執筆時点(7月17日)で1.2kまで伸びており3、約1ヶ月で3倍近い成長を見せている。前日にはメッセージング機能を持つ本体(v1.1.8)とは別に、Tauri製のGUIアプリ側も app-v0.2.0 としてリリースされており、CLIだけでなく画面付きの運用へも開発が広がっている段階にある。
まとめ
-
agmsgはbash+sqlite3だけでCLIエージェント間のメッセージングを実現するOSSツールで、実際にteam登録から送受信・履歴表示まで動作することを確認した。 - 信頼性の根拠はSQLiteのWALモードによる読み書き分離であり、
agmsg独自の排他制御ロジックは薄い。 - Codexのように
Monitor相当を持たないエージェントを組み込む場合や、同一役割を複数エージェントに割り当てる場合は、README のFAQが明記する制約(起動時のboot-prompt必須・claim/lockなし)を前提にプロンプト設計する必要がある。
複数のCLIエージェントを人力コピペで橋渡ししている作業がある方は、まず npx agmsg で試すハードルの低さも含めて評価してみる価値がある。
関連記事
- Genkit Agents API、resume()に配列を渡すと落ちる罠
- MCPサーバーとの通信を「盗聴」できる mcpsnoop を動かしてみた
- Claude Codeの10分の1のトークンで動くエージェント「Pi」を試した
参考リンク
- fujibee/agmsg(GitHubリポジトリ) — README・CHANGELOG・FAQを参照
- SQLite: Write-Ahead Logging — WALモードの仕組み