X MCPでAI/E2Eのトピックを毎朝Discordに流すBotを作った話

X MCPでAI/E2Eのトピックを毎朝Discordに流すBotを作った話
約10分で読めます

やりたかったこと

AI開発ツールやE2Eテスト自動化まわりは動きが早くて、ちょっと目を離すと何か出ています。 便利なんですが、これを毎日Xで追いかけるのがだんだん面倒になってきました。 毎朝Xを開いて検索して、ノイズの多い投稿をかき分けながら見るのは、けっこう疲れるんですよね。

そこで、X MCPを使って関連トピックを1日1回だけ拾ってきて、Discordに1枚のEmbedとして流すBotを作りました。

目的はかなり単純です。

  • AI開発ツールの新しい話題を拾う
  • E2Eテスト自動化の話題も拾う
  • 毎朝Discordを見るだけでざっくり把握できるようにする
  • なるべく低コストで運用する
  • 空振りやエラーのときに無駄な投稿をしない

完成形としては、毎朝8時に AI / E2E X Topic Watch という投稿がDiscordに流れてくる、という感じです。

作ったもの

Node.js + TypeScriptで、常駐サーバー上で動くDiscord Botを作りました。

Bot自体はずっと常駐していて、X検索からDiscordへの投稿までの一連の処理だけをcronで毎朝起動する形です。 そのダイジェスト処理の流れはこんな感じ。

  1. cronで毎朝、ダイジェスト処理を起動する
  2. X MCPへ検索クエリを投げる
  3. 返ってきたstory(X/Grokがまとめた要約)/postを正規化する
  4. 関連度の低いものや検索汚染っぽいものを落とす
  5. 古い話題を鮮度フィルタで落とす
  6. AI開発ツール / E2Eテスト自動化 / MCP系に分類する
  7. Discord Embedとして投稿する

手動実行用に /digest-now も用意しています。 成功時の一時メッセージは数秒で消すようにして、Discord上に余計な表示が残らないようにしました。

構成

主な構成はこんな感じ。

  • Runtime: Node.js
  • Language: TypeScript
  • Discord: discord.js
  • Scheduler: node-cron
  • X連携: X MCP + @xdevplatform/xurl
  • 常駐: Ubuntu + systemd

X MCPへの接続は、xurl のMCP bridgeを経由しています。

Terminal window
npx -y @xdevplatform/xurl mcp https://api.x.com/mcp

初回だけOAuth認証が必要なので、サーバー側では次を実行します。

Terminal window
npm run x:auth

UbuntuサーバーはCLI環境なので、表示されたURLを手元のWindowsブラウザで開いて認可しました。 この運用でも問題なく通りましたよ。

検索対象

最初はAI関連を全般的に拾おうとしていたのですが、これがまあノイズの多いこと。

そのへんの話は後の「検索汚染への対応」でまとめて書くとして、結論だけ言うと、目的に対してあまり意味のない投稿が混ざりまくったので、今はかなり開発寄りに絞っています。

AI開発ツール側では、主にこのあたりを見ています。

  • Claude Code
  • OpenAI Codex
  • Cursor
  • GitHub Copilot
  • Devin
  • Windsurf
  • Replit
  • Lovable
  • Bolt
  • v0
  • MCP

E2Eテスト自動化側は、このへんを対象にしています。

  • Playwright
  • Cypress
  • Selenium
  • WebdriverIO
  • BrowserStack
  • QA Wolf
  • Autify
  • mabl
  • MagicPod
  • Momentic

実際のクエリは config/topics.ts にまとめてあります。 対象を増やしたくなったら、ここをいじる形ですね。

とにかく低コストで

今回いちばん気にしたのはコストです。

X API側のクレジットを消費するので、デバッグのために何度も /digest-now を叩くと、普通にお金が減っていきます。 最初はエラー調査のたびに再実行していたのですが、これはかなりよくない運用でした…。

今の本番設定は、だいたいこんな感じにしています。

GROK_ENABLED=false
X_SEARCH_QUERY_LIMIT=6
X_SEARCH_MAX_RESULTS=5
X_ENABLE_TRENDS=false
X_ENABLE_NEWS=false
TOPIC_FRESHNESS_HOURS=24
DAILY_CRON=0 8 * * *
TZ=Asia/Tokyo
POST_FAILURES_TO_DISCORD=false

X_SEARCH_QUERY_LIMIT=6X_SEARCH_MAX_RESULTS=5 なので、1回あたり最大30件くらいを見に行く想定です。 1日1回だけなら、かなり控えめな設定かなと思います。

GROK_ENABLED=false にしているのもコストが理由です。 Grokに要約させると文章は自然になるんですが、今の目的だと元データの品質のほうが大事なので、まずはルールベースの整形だけで運用しています。

X_ENABLE_NEWS=falseでもstoryが返る

ちょっとややこしかったのが、X_ENABLE_NEWS=false の扱いです。

この設定は「追加のnews取得をするかどうか」を切り替えるものなんですが、X MCP側に search_news という検索ツールがある場合、通常の検索クエリでも search_news のほうが選ばれることがあります。

つまり、X_ENABLE_NEWS=false でも、検索結果としてX/Grokのstory要約が返ってくることがあるわけです。

最初はここを勘違いしやすかったのですが、今のBotはこの挙動を前提にしています。 投稿そのもののURLがない場合は、元投稿リンクではなく「この話題をXで検索」という検索リンクを出すようにしました。

デバッグ用にraw payloadを保存する

今回かなり効いたのが、raw responseの保存です。

X MCPの返り値は、検索ツールやタイミングによって形がころころ変わります。 投稿本文が返ることもあれば、story要約が返ることもあるし、エラー時にはJSON文字列としてエラーが返ってくることもありました。

そこで、1回だけ有料のX MCP呼び出しをして、その結果をファイルに保存できるようにしました。

DEBUG_SAVE_RAW_PAYLOADS=true
DEBUG_RAW_PAYLOAD_FILE=data/debug/latest-raw-items.json

保存しておけば、次のようにするとX APIを呼ばずに同じデータで再生成できます。

REPLAY_RAW_PAYLOAD_FILE=data/debug/latest-raw-items.json

このリプレイモードを入れたおかげで、表示ロジックやフィルタ調整を無料で試し放題になりました。 これは最初から入れておけばよかったやつです。

表示の調整

最初の出力は、正直あんまり役に立ちませんでした。

特に微妙だったのがこのあたり。

  • 主要アカウントの反応が空欄のまま残る
  • 検索対象のキーワードをランキング表示しているだけになる
  • 詳細リンクに見えるけど、実際は元投稿ではなくX検索リンク

ほかにも「投稿本文のつもりがstory要約だった」「エラーJSONがそのまま出た」あたりが地味につらかったのですが、これは後の「苦労した点」でまとめて書きます。

ということで、表示方針をけっこう変えました。

今は「投稿まとめ」ではなく「トピックウォッチ」として扱っています。

Embed内の主なセクションはこんな構成です。

  • トレンド概要
  • AI開発ツール
  • E2Eテスト自動化
  • MCP / エージェント基盤

E2Eの有効トピックが取れなかった日は、何も出さずに黙るのではなく「直近の取得範囲では見つかりませんでした」と出すようにしました。 地味ですが、毎日見るものとしてはここがかなり大事だと思っています。

表示されていないのか、取得できなかったのか、単に話題がなかったのかが分からないと、なんか不安になるんですよね。

検索汚染への対応

AI関連の検索は、本当にノイズが多いです。

単に AIChatGPT で検索すると、開発者向けじゃないものが大量に混ざってきます。 いわゆるAI驚き屋っぽい投稿、情報商材っぽい投稿、ChatGPTの呼び方の話、画像生成で遊んでみた系の話題…このあたりが目的に対してほぼ無意味で、なかなかしんどい。 今回ほしいのはAI一般ニュースではなく、AI開発ツールとE2Eテスト自動化の動向なので、要らないものはかなり落とすようにしました。

例えば、このへんは除外寄りにしています。

  • 副業・収益化・情報商材っぽい話
  • AIイラストや画像生成の遊び
  • ChatGPTの呼び方の話
  • バズや炎上そのものが中心の話
  • E2E検索で拾ったのにE2Eの文脈がない話

逆に残す条件としては、ツール名だけじゃなくて、実装・開発・CI・テスト・API・GitHub・CLIあたりの文脈があるかを見ています。

このへんは完璧ではないです。 ただ、何でもかんでも拾うよりはだいぶマシになりました。

鮮度フィルタ

毎朝見るものなので、古い話題が何日も居座るのは微妙です。

そこで TOPIC_FRESHNESS_HOURS を入れて、今は24時間以内の話題だけを表示するようにしています。

TOPIC_FRESHNESS_HOURS=24

ただし、X MCPのstory要約は更新時刻ベースになることがあって、良い話題でも24時間をちょっと超えると落ちてしまうことがあります。 空振りが多いようなら、ここを48時間に戻すのが現実的かなと思います。

このあたりは、速報性と空振り率のトレードオフですね。

systemdで常駐

サーバーではsystemdで常駐させています。

更新時はだいたいこの流れ。

Terminal window
cd /opt/x-mcp
git pull
npm install
npm run build
sudo systemctl restart x-mcp-digest-bot
sudo journalctl -u x-mcp-digest-bot -n 100 --no-pager

ログには、どのツールを使ったか、何件正規化できたか、何件フィルタで残ったかを出しています。

例えばこんな情報が出ます。

[digest] rawItems=6 mode=live
[digest] qualityFilter kept=7 dropped=16
[digest] normalizedPosts=23 relevantPosts=7 freshPosts=4 freshnessHours=24

rawItems が6なのに normalizedPosts が23に増えているのは、story要約1件の中に複数の投稿がぶら下がっていて、それを展開して数えているためです。

このログがあると、「X APIが失敗しているのか」「正規化で落ちているのか」「品質フィルタで落ちているのか」「鮮度フィルタで落ちているのか」が一目で分かるので便利です。

苦労した点

X MCPの返り値が思ったより一定じゃない

最初は「投稿検索だから投稿本文が返ってくるだろう」と思っていました。

ところが実際には、X/Grokのstory要約が返ってくることがあって、そこには元投稿URLや投稿者情報がない場合があります。

そのせいで、最初に作った「重要投稿」という表示はかなりズレていました。 今は「トピック」として扱って、リンクも元投稿ではなくX検索リンクなんだと分かる文言にしています。

エラーも投稿っぽく見えてしまった

クレジット不足のとき、{"detail":"credits depleted" ...} みたいなエラーJSONが、そのまま「重要投稿」として表示されてしまいました。

さすがにこれはよろしくないので、X APIのエラーpayloadを検出して、通常のトピックとしては扱わないようにしました。

クレジット不足の場合は、Botが壊れたわけではなく「X API側のクレジットが足りていない」と分かるメッセージを出すようにしています。

再実行がそのままコストになる

調整中に一番気をつけないといけないのがここでした。

表示が微妙だからといって何度も /digest-now を実行すると、そのたびにX APIを呼びます。要するに、直すたびにお金が飛ぶ。 調査にはraw payload保存とリプレイが必須でした。

やってよかった点

一番よかったのは、毎朝見る場所がDiscordにまとまったことです。

Xを開くと、ついそのまま別の投稿を延々と見続けてしまいがちなんですが、Discordに1枚だけ流れてくる形なら、必要な話題だけを軽く確認して終われます。

あと、E2Eテスト自動化の話題をAI開発ツールと同じ場所に流せるのも地味に便利です。 この2つは直接同じカテゴリというわけではないんですが、開発フロー改善という意味では一緒に見たいことが多いんですよね。

それと、低コスト前提で作ったので、運用し続ける心理的な負担が小さいです。 個人運用だと、ここがかなり大事だと思っています。

今後

今の状態で、ひとまず目的は達成できています。

今後いじるなら、このあたりを少しずつ良くしていきたいかなと。

  • 検索クエリの見直し
  • E2E側の話題が少なすぎる日の扱い
  • 除外ルールの調整
  • story要約ではなく投稿本文を拾うモードとの比較
  • コスト上限に応じた安全設定
  • Discord Embedの見やすさ改善

特にE2Eテスト自動化は、X上の話題量がAI開発ツールほど多くないので、24時間縛りだと空振りしやすいかもしれません。 その場合は、E2Eだけ48時間にするとか、カテゴリ別の鮮度設定を入れてもよさそうです。そのうちねw

おわりに

X MCPを使って、AI開発ツールとE2Eテスト自動化の話題を毎朝Discordに流すBotを作りました。

作ってみて分かったのは、検索そのものよりも、ノイズを落とすことと、取れたものを正直に表示することのほうがずっと大事だ、ということでした。 元投稿URLが取れないなら元投稿リンクっぽく見せない、E2Eの話題が取れないなら黙って消さない、エラーJSONを投稿として扱わない、デバッグではAPIを何度も叩かない。このへんを地道に直していって、ようやく毎日見てもいいかなという形になりました。

精度はまだまだ完璧とは言えませんが、個人で毎朝ざっくり技術トピックを追う用途としては、十分使えそうです。

次に読む

シリーズ一覧

すべて見る

このシリーズ情報は未設定です。シリーズ一覧ページから他の連載を探せます。