はじめに
こんにちは!ラクスルのコーポレートアプリ開発グループ に所属している高橋です。
最近、AIツールの社内活用を推進する中で、セキュアなアクセストークン管理を実現するSlack MCPサーバーを実装しました。
Model Context Protocol(MCP)は、AIモデルと外部サービスを安全に連携させるための標準規格ですが、既存の実装例には「個人のAPIトークンを環境変数に設定する」など、セキュリティや利便性に課題がありました。
また標準的なHTTPプロトコルを活用し、特定のクラウドサービスに依存しない設計としたため、様々な環境に導入可能です。
この記事では、MCPサーバーの実装アプローチと主要コンポーネントを解説し、実際の使用例も紹介します。 AIの組織的活用を検討されている方々の参考になれば幸いです。 詳細な実装記事に関しては別で詳しく解説します。
対象読者
- MCPサーバーの開発に興味がある方
- Slackとの統合を検討している開発者
- MCPの基本概念を理解していて実装に進みたい方
- 特定のクラウドサービスに依存しないMCPサーバの構築をしたい方
取り組むにあたっての背景と導入経緯
当社では全社的なAI活用推進の一環として、MCPサーバーを利用することが決定しました。
AIツールと社内システムの連携によって業務効率化を図る取り組みの1つとして位置付けられています。
導入検討に際して、既存のMCP実装例を調査した結果いくつかの課題が上がりました。
- 認証方式の課題:従来のstdioベースの実装例では、環境変数に個人のAPIトークンを設定する方式が主流でした。しかし、これはエンジニア以外のユーザーにとって大きなハードルとなり、全社展開の妨げになる可能性がありました。
- セキュリティリスク:個人のBotトークン(xoxbから始まるトークン)を各ユーザーが管理する方式では、トークンの漏洩リスクが高まります。特に、社内の機密情報にアクセスできる強い権限を持つトークンの場合、その影響は深刻です。
- 権限管理の複雑さ:特定のSlackチャンネルのみにアクセスを制限するなど、Slackの既存権限体系に準拠したセキュリティモデルを維持したい場合、各ユーザーが適切な権限のトークンを自分で取得・管理することが現実的ではありませんでした。
これらの課題を解決するため、リモートMCPサーバーでトークンを一元管理する方式を採用しました。
この方式ではユーザーは通常のOAuth認証フローに従ってブラウザから認証するだけで良く、技術的知識がなくても利用可能です。
またトークンはサーバー側で安全に管理され、漏洩リスクを最小化しています。
ユーザーごとの権限はSlackの既存権限体系に準拠し、自動的に適切なアクセス制御が適用されます。
このアプローチにより、高度なセキュリティを確保しながら、社内の全スタッフがブラウザからの簡単な認証だけでSlack APIを活用したAIサービスとの連携が可能になる環境を目指しています。
アーキテクチャ概要
以下の図は、システム全体におけるトークン管理の流れを示しています。
MCPクライアント(Claude AIなど)はMCPアクセストークンのみを保持し、Slackの実際のAPIトークンには一切アクセスできません。
すべてのSlackトークンはMCPサーバー側で管理され、KVSを使用して暗号化保存されています。
MCP認証フロー
当サーバーでは、MCP公式仕様書に記載されている標準的なOAuth認証フローを実装しています。
Authorization - Model Context Protocol
この認証フローにより、ユーザーはブラウザから簡単にSlackアカウントで認証でき、MCPサーバーは各ユーザーの適切な権限を持つSlackトークンを安全に管理できます。
ユーザーはMCPアクセストークンのみを使用してクライアントアプリケーションから操作するため、Slackトークンが直接露出することはありません。
実際に動かしてみた
早速ですがClaudeのWeb版で実際に動かしてみます。
検索とツールのボタンを押すとintegrationのリストが表示されるので「連携」を押します。
するとSlackの認証画面にリダイレクトします。
「許可する」を押すとClaudeのWebサイトにリダイレクトされ、Toolが認識された状態になります。
次に、Slackのチャンネルに今日の天気を投稿するようにClaudeに依頼してみます。
これが実際にSlackに投稿されました。
このように、MCPサーバーを通じてClaudeからSlackへのアクセスが簡単に実現でき、ユーザーは複雑な設定なしでAIとSlackの連携機能を活用できます。
実装時にハマったポイント:接続ごとのMCPサーバーインスタンス生成
MCPサーバーを実装していく中で、特に悩まされたのが複数クライアントの同時接続問題でした。
最初は何が起きているのかさえ把握できない状態でした。
シンプルに以下のようなコードで実装していました。
const server = new McpServer({ name: "test-server", version: "1.0.0" }); app.get("/sse", async (req, res) => { const transport = new SSEServerTransport("/messages", res); await server.connect(transport); // ここで問題発生! });
しかし複数人で動かしてみると、何人かの接続が途中で切れてしまいます。「なぜ接続が切断されるのだろう?」と頭を抱えました。
原因がクライアント側にあるのか、サーバー側にあるのか切り分けるのも一苦労でした。
「SSEトランスポートに問題があるのでは?」とドラフト段階だったStreamable HTTP形式に変更してみたり、Claudeのリモートサーバー対応を検証したりしましたが同じ結果でした。
これによりサーバー側に問題があると確信しました。
答えは意外とシンプルでした。
GitHub の issue を探していたところ、typescript-sdk の issue #204 で同様の問題が議論されていました。
そこでわかったのは、McpServer インスタンスは 1 つのトランスポートにしか接続できないということでした。
server.connect(transport)
を呼び出すと、以前のトランスポート接続は上書きされてしまいます。
この問題への対処法は次のようなコードでした:
const transports = new Map<string, SSEServerTransport>(); app.get("/sse", async (req, res) => { const transport = new SSEServerTransport("/messages", res); const sessionId = transport.sessionId; transports.set(sessionId, transport); const server = new McpServer({ name: "test-server", version: "1.0.0" }); await server.connect(transport); }); app.get("/message", async (req, res) => { const transport = transports.get(sessionId); await transport.handlePostMessage(req, res, req.body); });
当時はドキュメントに明示されていなかったこの「暗黙の前提」に気づくまでに時間がかかりました。
現在のSDKドキュメントには下記のように記載されているので、同じ問題に直面する方は少なくなるかと思います。
In stateless mode, create a new instance of transport and server for each request to ensure complete isolation. A single instance would cause request ID collisions when multiple clients connect concurrently.
今後の展望
Streamable HTTPトランスポートへの対応
現在のMCPサーバー実装では、HTTP+SSEトランスポートを使用していますが、これは2025-03-26仕様では非推奨(deprecated)となりました。
この変更に対応するため、同仕様で新たに標準化された「Streamable HTTP」トランスポートへの移行を計画しています。
Streamable HTTPトランスポートは、従来のHTTP+SSEトランスポートと比較して以下のメリットがあります
- 単一エンドポイントでの通信:リクエストとレスポンスが単一のエンドポイントを通じて行われるため、実装がシンプルになり管理も容易になります。
- 双方向通信の改善:サーバーからクライアントへの通知やリクエストが同じ接続上で行えるため、より対話的な機能を実現できます。
- スケーラビリティの向上:接続を長時間維持する必要がなく、リソース使用の効率化につながります。
執筆時点では主要なMCPクライアントがまだStreamable HTTPトランスポートを採用していないため、HTTP+SSEトランスポートを主軸にしています。
クライアント側のサポートが広がり次第、MCPサーバーにもStreamable HTTPトランスポートのサポートを追加する予定です。
独自MCPクライアントの開発
Slack MCPサーバーをより幅広く活用するため、今後は独自のMCPクライアント開発も計画しています。
現在、ClaudeのようなサービスでリモートMCPサーバーを利用するには有料プランへの加入が必要です。
社内での広範な利用を想定すると、コスト面で大きな課題となります。
また独自クライアントの開発には、コスト削減だけでなくカスタマイズの自由度という大きなメリットもあります。
例えば認証トークンの期限切れを自動検知して再認証を促す仕組みや、社内ワークフローに特化した機能など、組織特有のニーズに合わせた実装が可能になります。
この取り組みによりコスト制約を気にすることなく、組織内でのAI活用をさらに促進していきたいと考えています。
まとめ
本記事では、SlackとModel Context Protocolを統合したMCPサーバーの構築について解説しました。
実装過程では複数クライアントの同時接続問題など技術的な課題にも直面しましたが、これらを解決することでより堅牢なシステムを構築できました。
今後はStreamable HTTPトランスポートへの対応や独自MCPクライアントの開発を進め、より効率的なAI活用基盤を整備していく予定です。
AIをより身近かつ安全に活用できる環境づくりへ、この記事が貢献できれば幸いです。