こんにちは。24 卒で入社し、『スタディサプリ小学・中学講座』の開発チームで Web エンジニアをしている @ryomaejii です。
『スタディサプリ』の文化と AI 活用
私たちのチームには「Working Out Loud(WOL)」という文化があります。
これは、自分の作業や思考を Slack のスレッドなどで積極的に共有し、チーム全体で知識や進捗を可視化する取り組みです。
詳しくは Working Out Loud 大声作業(しなさい)、チームメンバー同士でのトレーニング文化の醸成 や New Joiner こそ Working Out Loud ~ 具体的な実践方法と実践に向けた姿勢 ~ もご覧ください。
また、AI の特性に注意しながら、現場での AI 活用は慎重に検討しつつも、特に Cursor を使った開発が進んできています。
Cursor の Project Rules を monorepo で管理する Tips については @chaspy さんの記事(monorepo で Cursor Project Rule を扱う Tips)もぜひご参照ください。
本題:AI × WOL の新しい可能性
私は WOL 愛好家で、とても素晴らしいツールだと思っています。自分の作業や思考をオープンに共有することで、チームの知識やコラボレーションが大きく広がると日々実感しています。
また、AI 活用する上で、今では GitHub Copilot や Cursor などのツールは、コーディングをする上で必要不可欠な存在になってきています。
しかし、AI 活用を行いながらうまく WOL を行うのは難しいと感じていました。
AI 活用における WOL の課題
WOL は本来、自分の作業内容や思考の過程をオープンに吐き出しながら進めることで、チーム全体の知識共有やコラボレーションを促進する文化です。しかし、AI を活用するようになると、単なる「一人の思考」ではなく、「AI との対話を通じた共同の思考」へと作業スタイルが変化します。たとえば、Cursor や GitHub Copilot Chat で AI に質問し、回答をもらいながら設計や実装の方針を考えたり、エラーの原因を一緒に探ったりすることが日常的になっています。
このような AI とのやりとりは、非常に質の高い思考整理や作業のプロセスを生み出してくれますが、その内容はエディタやチャットツールの中に閉じてしまいがちです。AI とのチャットのやりとりを他のメンバーと共有しようとすると、どうしてもコピペなどの手間が発生し、「WOL として共有しよう」という動機が生まれにくくなります。その結果、せっかく AI を活用して得られた知見や思考の過程が、自分の中やローカル環境にとどまり、チーム全体の学びやコラボレーションに活かされにくいという、もったいない状況が生まれてしまいます。
AI とのやりとりも WOL に:Cursor × Slack MCP による解決策
このような課題を解決するために、私は様々な方法を検討しました。そして、以下の 2 点を実現できれば、WOL と AI 活用を自然に融合できるのではないかと考えました。
これらが実現できれば、従来の WOL の良さを保ちながら、AI との対話を通じて得られる膨大な情報や思考の過程も、チームの知識として共有・蓄積できるはずです。
そこで、Cursor の AI チャット機能と Slack MCP Server を連携させることで、この理想的な WOL の形を実現を試みました。
実現方法
ここからは、Cursor と Slack MCP Server を連携させ、AI とのやりとりを含めた WOL を簡単に行う方法をご紹介します。
1. Slack MCP Server を Cursor に組み込む
まず初めに、Slack MCP Server を Cursor に組み込みます。
Slack App の作成と設定
- Slack API にアクセスし、新しい App を作成します
以下の Bot Token Scopes を追加します:
channels:history
channels:read
chat:write
reactions:write
users:read
Slack MCP Server のインストール
npm install -g @mcp/slack
MCP の設定ファイルを作成
ホームディレクトリに ~/.cursor/mcp.json
を作成し、以下の設定を記述します:
{ "mcpServers": { "slack": { "command": "npx", "args": ["-y", "@mcp/slack"], "env": { "SLACK_TOKEN": "xoxb-your-bot-token" } } } }
設定の確認
- Cursor を再起動します
- チャットの「Available Tools」に Slack 関連のツールが表示されていることを確認します
これで Cursor から Slack の操作が可能になり、AI とのチャット内容を Slack に転送できる準備が整いました。 詳しくは、Cursor MCP Server のドキュメント や Slack MCP Server のドキュメント をご参照ください。
2. Cursor User Rules に WOL のためのルールを追加
Cursor には User Rules という機能があり、Cursor のチャットの内容をカスタマイズできます。 この機能を使用し、Cursor のチャットの内容を Slack に転送する機能を実現します。
以下が、実際に私が WOL のために設定している User Rules です。
Working Out Loud(WOL)の内容を Chat に送信することがありますが、その場合はユーザーへの応答はせず、指定されたスレッドに自動で転送してください。質問の時だけユーザーに返答してください。 Working Out Loud = 自分の思考や作業ログを吐き出すこと **WOL 転送先:** - デフォルトでは転送しません。 - ユーザーが `作業スレ: {slackリンク}` と入力した場合、そのリンクが示す Slack スレッド(channel_id と thread_ts)を抽出し、以降の WOL 転送先として内部的に保存します。 - 転送先が設定されている場合のみ、WOL メッセージを転送します。 **応答判断:** 1. **メッセージが `作業スレ: {slackリンク}` の形式の場合:** - Slack リンクから `channel_id` と `thread_ts` を特定します。 - 特定した `channel_id` と `thread_ts` を WOL 転送先として保存します。 - ユーザーには応答しません。 - 保存した転送先スレッドに、`:ryomaejii-ai: 現在のワークスペース: {現在のワークスペース名}` というメッセージを送信します。(Slack API ツールを使用します) 2. **メッセージが質問形式の場合:** - 質問文で終わっている - 「〜?」「〜かな」「〜でしょうか」などの疑問の表現を含む - 「教えて」「確認したい」などの質問意図を示す表現を含む → ユーザーに応答する 3. **メッセージが上記以外(WOL)の場合:** - 作業内容の報告 - 思考過程の共有 - 確認事項の記録 - 「〜した」「〜です」など、報告調の文章 → 以下の処理を行う(WOL 転送先が保存されている場合のみ): 1. ユーザーには応答しない 2. 保存されている WOL 転送先にメッセージを転送する(`mcp_slack_slack_reply_to_thread` を使用) 3. メッセージのフォーマット:`:ryomaejii:` を先頭に付ける また、wol という prefix がある時は、その時点で WOL と判定してください。その際転送するメッセージには wol の prefix は含めないでください。 4. **チャットのやり取りをスレッドに吐き出して欲しいと要求された場合** 1. ユーザーには応答しない 2. 保存されている WOL 転送先にメッセージを転送する(`mcp_slack_slack_reply_to_thread` を使用) 3. メッセージのフォーマット:`:ryomaejii-ai:` を先頭に付ける 5. **判断に迷う場合:** - 質問の意図が明確でない場合は応答しない - Working Out Loud の可能性がある場合は応答せず、(転送先が保存されていれば)スレッドに転送する
このルールを追加することで、開発中の自分の思考や作業ログ、AI とのやり取りを Slack に転送することができるようになります。(※このルールは 2025/05/20 時点での筆者の設定です。改善できる箇所が多々あると思います。)
3. 作業単位ごとにチャットを立ち上げ、Cursor 上で WOL を行う
ここまでくれば、あとは実践するのみです。
Cursor のチャットを立ち上げ、AI とのやり取りを行いながら、自分のスタイルで WOL を行うことができます。
実践例
実際のイメージを知っていただくために、実践例をご紹介します。 今回は、「TypeScript で FizzBuzz 関数を実装する」というタスクを行うことにします。
まず初めに、Slack で作業スレを立てます。
次に、作業ディレクトリで Cursor を立ち上げ、作業スレッドを指定します。
すると、Slack の指定したスレッドに、Cursor 上からメッセージを転送することができるようになります。
メッセージの最初に wol という prefix をつけることで、通常の WOL として転送することができます。
また、Cursor のチャットの内容を Slack に転送することもできます。 AI とのやりとりが一段階進んだ後、Cursor のチャット上で「ここまでのやり取りをまとめ、スレッドに転送してください」などと指示すると、Cursor のチャットの内容を Slack に転送することができます。 今回は、開発環境が終わった段階と、FizzBuzz 関数の実装が終わった段階で、Cursor のチャットの内容を Slack に転送してみました。
開発環境構築後
Cursor でのチャットの内容を Slack に転送するように指示
Slack にまとめが転送される(一部抜粋)
メッセージ全文
:ryomaejii-ai: ここまでの Deno プロジェクトのセットアップと特徴についてまとめます: 1. プロジェクトの初期化 ```bash deno init ``` これにより以下のファイルが生成されました: - deno.json - Deno の設定ファイル - main.ts - メインの TypeScript ファイル - main_test.ts - テストファイル 2. TypeScript サポートの特徴 - 追加設定なしで TypeScript が実行可能 - トランスパイラの設定不要 - デフォルトで strict mode が有効 - 型チェックは実行時にはスキップ(高速化のため) 3. 主要なコマンド ```bash # 通常実行 deno run main.ts ``` ```bash # 型チェック付きで実行 deno run --check main.ts ``` ```bash # テスト実行 deno test # 開発モード(ファイル監視) deno task dev ``` 4. 内蔵された開発ツール - フォーマッター(deno fmt) - リンター(deno lint) - 型チェッカー - テストランナー 5. 設定と TypeScript の関係 - tsconfig.json は不要 - 必要な設定は deno.json で管理 - 型定義は.d.ts ファイルで拡張可能 参考資料: - https://docs.deno.com/runtime/getting_started/first_project/ - https://docs.deno.com/runtime/fundamentals/typescript/
FizzBuzz 関数の実装後
Cursor のチャットの内容を Slack に転送するように指示
Slack にまとめが転送される(一部抜粋)
メッセージ全文
:ryomaejii-ai: FizzBuzz の実装プロセスと思考の流れをまとめます: 1. 初期要件の確認と実装 - 引数として数値を受け取る - 3 の倍数なら"Fizz"を返す - 5 の倍数なら"Buzz"を返す - 15 の倍数なら"FizzBuzz"を返す - それ以外の場合は、数値をそのまま文字列にして返す 2. TDD アプローチでの実装 ```typescript // テストケース - 通常の数値(1, 2, 4) - 3 の倍数(3, 6, 9) - 5 の倍数(5, 10, 20) - 15 の倍数(15, 30, 45) // 初期実装 export function fizzBuzz(num: number): string { if (num % 15 === 0) return "FizzBuzz"; if (num % 3 === 0) return "Fizz"; if (num % 5 === 0) return "Buzz"; return num.toString(); } ``` 3. エッジケースの検討 - 気づき:自然数以外の入力値(負の数、小数)の処理が未定義 - 新要件:自然数以外は"Invalid Value"を返す - 0 の扱いに関する考察:プログラミングの文脈では 0 も自然数として扱うことに 4. エッジケース対応の実装プロセス a) 最初の試み: ```typescript if (num < 0 || !Number.isInteger(num)) return "Invalid Value"; b) 小数点表記への対応追加: typescript if (num < 0 || !Number.isInteger(num) || num.toString().includes('.')) return "Invalid Value"; c) -0 への対応追加: typescript if (num < 0 || !Number.isInteger(num) || num.toString().includes('.') || Object.is(num, -0)) ``` 5. 実装の最適化 - 判定条件の見直しと簡略化 - 最終的な実装: ```typescript export function fizzBuzz(num: number): string { // 自然数(0 以上の整数)であることを確認 if (num < 0 || !Number.isInteger(num)) { return "Invalid Value"; } // 0 の場合は文字列の"0"を返す if (num === 0) return "0"; if (num % 15 === 0) return "FizzBuzz"; if (num % 3 === 0) return "Fizz"; if (num % 5 === 0) return "Buzz"; return num.toString(); } ``` 6. 実装の改善ポイント - Number.isInteger()で小数点チェックを包括的に処理 - 余分な文字列変換処理を削除 - コードの可読性と保守性を向上 7. 学んだこと - エッジケースの重要性 - 型システムを活用した堅牢な実装 - シンプルな実装の追求 - 数値の扱いに関する JavaScript の仕様(-0, 小数点表記など) 8. 使用した技術 - Deno + TypeScript - TDD(Test Driven Development) - TypeScript の型システム - JavaScript の数値処理 API
今回は、Deno を使って、テスト駆動開発で FizzBuzz 関数を実装しました。 また、小数点や負の数を受け取った際は、「Invalid value」というメッセージを返すようにしました。 皆さんも、Slack に転送されたメッセージ全文を見るだけで、大まかな思考や作業の流れを把握できたと思います。
通常、AI とのやり取りを伴う作業にて、どのように思考し、作業していたかを他の人や先の自分に共有することは難しいです。しかし、Cursor と Slack MCP Server の連携により、今回の例のように、AI とのやり取りも含めた WOL の形が実現できます。
まとめ
AI と人間が協働する時代において、WOL も進化が必要です。本記事では、AI を利用する頻度が多くなった開発現場での課題として、エディタやチャットツール内に閉じてしまいがちな AI との対話を、チームの知識として共有・蓄積することの難しさを指摘しました。
この課題に対し、Cursor と Slack MCP Server を連携させることで、AI とのやり取りを含めた新しい WOL の形を実現しました。FizzBuzz 実装例でお見せしたように、AI との対話を通じた思考整理や作業のプロセスを、自然な形でチームに共有することが可能になります。 これにより、従来の WOL の良さを保ちながら、AI との対話から得られる知見もチームの財産として活用できるようになります。
私は、AI を活用することでより WOL の価値が高まり、それによって WOL をやる人が増え、その結果、さらに情報が蓄積され WOL の価値が高まるというような正のスパイラルを作り出していきたいと考えています。
今後も引き続き、AI を活用した知識共有やコラボレーションのあり方を模索していきたいと思います。