はじめに
この記事は、QiitaのModel Context Protocol(以下、MCP)解説シリーズの第25回です。
今回は、MCPのTools機能をさらに応用し、複数の外部サービスを統合する実践例を解説します。具体的には、GitHubとSlackを連携させるMCPサーバーを構築し、LLMがこれらのサービスを横断的に操作する様子を見てみましょう。
💡 なぜ複数サービス統合が重要なのか?
単一のサービスをMCPに統合するだけでも便利ですが、複数のサービスを組み合わせることで、LLMはより複雑で実用的なタスクを解決できるようになります。
例:開発チームのサポート
- 「最新のバグ修正をSlackの#devチャンネルに通知して。」
- 「GitHubで特定のコミットを探し、関連するSlackのディスカッションを要約して。」
- 「プルリクエストのレビュー結果をSlackに自動投稿して。」
このようなタスクは、LLMがGitHubとSlackのToolsを組み合わせて使うことで初めて可能になります。
🛠️ ステップ1:プロジェクトのセットアップ
今回は、TypeScriptと、GitHubとSlackのAPIを扱うためのライブラリを使用します。
mkdir mcp-multi-service
cd mcp-multi-service
npm init -y
npm install typescript ts-node @modelcontextprotocol/sdk @octokit/rest @slack/web-api zod dotenv
npm install -D @types/node
npx tsc --init
tsconfig.jsonの設定
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
APIトークンの取得
GitHubトークンの取得手順:
- GitHub > Settings > Developer settings > Personal access tokens > Tokens (classic)
- 「Generate new token」をクリック
- 必要なスコープを選択:
repo
,user:email
Slackトークンの取得手順:
- Slack APIでアプリを作成
- OAuth & Permissions で以下のスコープを追加:
chat:write
channels:read
groups:read
- Bot User OAuth Tokenをコピー
.env
ファイルの作成
# GitHubのPersonal Access Token
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# SlackのBot Token
SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# GitHubリポジトリ情報
GITHUB_OWNER=your_github_username
GITHUB_REPO=your_repo_name
📝 ステップ2:Toolsの実装
server.ts
ファイルを作成し、GitHubとSlackのToolsを定義します。
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { Octokit } from '@octokit/rest';
import { WebClient } from '@slack/web-api';
import { z } from 'zod';
import 'dotenv/config';
// 環境変数の検証
const requiredEnvVars = ['GITHUB_TOKEN', 'SLACK_BOT_TOKEN', 'GITHUB_OWNER', 'GITHUB_REPO'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`環境変数 ${envVar} が設定されていません`);
}
}
// APIクライアントの初期化
const github = new Octokit({ auth: process.env.GITHUB_TOKEN });
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
const githubRepo = {
owner: process.env.GITHUB_OWNER!,
repo: process.env.GITHUB_REPO!
};
// 入力スキーマの定義
const GetLatestCommitsSchema = z.object({
count: z.number().int().min(1).max(10).describe('取得するコミットの数(1-10)'),
});
const PostSlackMessageSchema = z.object({
channel: z.string().describe('メッセージを投稿するSlackチャンネル名(例:#general)'),
message: z.string().describe('投稿するメッセージの本文'),
});
const GetPullRequestsSchema = z.object({
state: z.enum(['open', 'closed', 'all']).default('open').describe('プルリクエストの状態'),
count: z.number().int().min(1).max(10).default(5).describe('取得するプルリクエストの数'),
});
// サーバーの作成
const server = new Server(
{
name: 'mcp-multi-service',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// ツールの一覧を返すハンドラー
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'get_latest_commits',
description: 'GitHubリポジトリの最新のコミット情報を取得します',
inputSchema: GetLatestCommitsSchema,
},
{
name: 'post_slack_message',
description: '指定されたSlackチャンネルにメッセージを投稿します',
inputSchema: PostSlackMessageSchema,
},
{
name: 'get_pull_requests',
description: 'GitHubリポジトリのプルリクエスト一覧を取得します',
inputSchema: GetPullRequestsSchema,
},
],
};
});
// ツールの実行ハンドラー
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'get_latest_commits': {
const input = GetLatestCommitsSchema.parse(args);
const { data } = await github.repos.listCommits({
...githubRepo,
per_page: input.count,
});
const commits = data.map(commit => ({
sha: commit.sha.substring(0, 8),
message: commit.commit.message.split('\n')[0], // 最初の行のみ
author: commit.commit.author?.name || 'Unknown',
date: commit.commit.author?.date,
url: commit.html_url,
}));
return {
content: [
{
type: 'text',
text: JSON.stringify(commits, null, 2),
},
],
};
}
case 'post_slack_message': {
const input = PostSlackMessageSchema.parse(args);
const result = await slack.chat.postMessage({
channel: input.channel,
text: input.message,
});
if (!result.ok) {
throw new Error(`Slackへの投稿に失敗: ${result.error}`);
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
status: 'success',
message: 'メッセージを正常に投稿しました',
timestamp: result.ts,
channel: result.channel,
}, null, 2),
},
],
};
}
case 'get_pull_requests': {
const input = GetPullRequestsSchema.parse(args);
const { data } = await github.pulls.list({
...githubRepo,
state: input.state,
per_page: input.count,
});
const pullRequests = data.map(pr => ({
number: pr.number,
title: pr.title,
author: pr.user?.login || 'Unknown',
state: pr.state,
created_at: pr.created_at,
url: pr.html_url,
}));
return {
content: [
{
type: 'text',
text: JSON.stringify(pullRequests, null, 2),
},
],
};
}
default:
throw new Error(`未知のツール: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '不明なエラーが発生しました';
return {
content: [
{
type: 'text',
text: `エラー: ${errorMessage}`,
},
],
isError: true,
};
}
});
// サーバーの起動
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP multi-service server started');
}
if (require.main === module) {
main().catch((error) => {
console.error('サーバー起動エラー:', error);
process.exit(1);
});
}
🔧 ステップ3:Claude Desktop設定
Claude Desktopで新しいMCPサーバーを使用するため、設定ファイルを更新します。
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%/Claude/claude_desktop_config.json
{
"mcpServers": {
"multi-service": {
"command": "npx",
"args": ["ts-node", "/path/to/your/mcp-multi-service/server.ts"],
"env": {
"GITHUB_TOKEN": "your_github_token",
"SLACK_BOT_TOKEN": "your_slack_token",
"GITHUB_OWNER": "your_github_username",
"GITHUB_REPO": "your_repo_name"
}
}
}
}
🚀 ステップ4:動作確認とテスト例
Claude Desktopを再起動後、以下のような指示でテストしてみてください。
単一ツールのテスト
例1: GitHubコミット取得
「GitHubリポジトリの最新のコミットを3つ教えて。」
例2: Slackメッセージ投稿
「Slackの#generalチャンネルに「テスト投稿です」というメッセージを送って。」
複数ツールの連携テスト
例3: コミット情報をSlackに通知
「GitHubの最新コミット情報を取得して、その内容を要約してSlackの#devチャンネルに投稿して。」
期待される動作:
-
get_latest_commits
ツールで最新コミット情報を取得 - Claudeがコミット情報を分析・要約
-
post_slack_message
ツールで要約をSlackに投稿
例4: プルリクエストステータス報告
「現在オープンなプルリクエストをチェックして、その状況をSlackの#teamチャンネルに報告して。」
🎯 セキュリティとベストプラクティス
セキュリティ考慮事項
- 環境変数の管理: APIトークンは必ず環境変数で管理し、コードに直接記載しない
- スコープ制限: 必要最小限のAPIスコープのみを付与する
- エラーハンドリング: 機密情報がエラーメッセージに含まれないよう注意
パフォーマンス最適化
- レート制限対応: GitHub APIとSlack APIのレート制限を考慮した実装
- バッチ処理: 複数の操作をまとめて実行できる場合は効率化を図る
- キャッシュ機能: 頻繁にアクセスする情報はキャッシュを検討
🔄 応用例とカスタマイズ
追加できる機能例
- Issue管理: GitHubのIssueとSlackを連携
- デプロイ通知: CI/CDパイプラインの結果をSlackに通知
- レビュー依頼: プルリクエストのレビュー依頼をSlackで自動送信
他サービスとの統合
このパターンは以下のサービスにも適用可能です:
- プロジェクト管理: Jira、Asana、Trello
- コミュニケーション: Discord、Microsoft Teams
- クラウドサービス: AWS、Google Cloud、Azure
- CRM: Salesforce、HubSpot
📚 まとめ
本記事では、MCPのTools機能を活用してGitHubとSlackを統合する実例を紹介しました。
重要なポイント:
- 複数サービス統合の威力: LLMが異なるサービス間でデータを連携し、複雑なワークフローを自動化
- 自律的な判断力: ユーザーの指示から適切なツールの組み合わせを選択
- 安全な設計: 認証情報の適切な管理とエラーハンドリング
- 拡張性: 新しいサービスやツールを簡単に追加可能
このパターンをマスターすることで、LLMを活用した業務自動化の可能性が大きく広がります。
次回は、開発効率化をテーマに、テスト自動化とCI/CDパイプライン構築について解説します。お楽しみに!