はじめに
⚠️ 本記事のコードはサンプルです。実際の認証情報・設定値は含まれていません。
複数の自社サービス(6つのPod)を運営していると、Claude Codeから「あのサービスのDB確認して」「エラーログ見て」「デプロイして」が日常になります。
最初はその都度SSHしたりMCPツールをバラバラに使っていましたが、統合MCPサーバー「mcp-mothership」を作って育てた結果、69ツール・27ファイル・4,629行の規模になりました。
この記事では、設計判断・セキュリティ対策・運用で学んだことを共有します。
全体構成
mcp-mothership/
├── server.js # デュアルモード(stdio / HTTP)
├── config/pods.json # Pod別接続情報
├── lib/
│ ├── config.js # 環境変数・設定管理
│ ├── ssh.js # SSH接続ヘルパー
│ ├── db/ # MySQL / SQLite / Mothership専用
│ ├── tools/ # ツール定義(11カテゴリ)
│ └── handlers/ # ハンドラー実装(tools/と1:1対応)
ポイント: tools/ と handlers/ を1:1で分離。ツール定義(名前・説明・パラメータ)とビジネスロジックを完全に分けることで、ツール追加が楽になります。
69ツールの内訳
| カテゴリ | ツール数 | 例 |
|---------|---------|-----|
| Pod管理 | 5 | pod_list, pod_detail, pod_compare |
| コード分析 | 8 | code_tree, code_search, code_functions |
| KPI・収益 | 8 | kpi_summary, kpi_trend, revenue_detail |
| DBクエリ | 5 | db_query, db_tables, db_execute |
| ログ分析 | 5 | logs_error, logs_access, logs_search |
| デプロイ | 7 | deploy_file, deploy_verify, vps_exec |
| AI社長 | 6 | ceo_decisions, ceo_analyze, ceo_report |
| ヘルスチェック | 4 | health_all, health_ssl, health_dns |
| ファイル操作 | 3 | file_create, file_edit |
| スキル管理 | 3 | skill_list, skill_create, skill_execute |
| Google Workspace | 15 | gmail, calendar, drive, search console |
最初は10ツール程度でしたが、「これもMCPから触れたら便利」を繰り返して69まで増えました。
設計で大事だったこと
1. デュアルモード(stdio / HTTP)
// server.js
if (process.argv.includes('--http')) {
// Express + Bearer Token認証
// VPSでPM2デーモンとして稼働
} else {
// stdio: Claude Codeのローカル接続用
}
ローカルではstdio、VPSではHTTPで同じコードが動きます。PHPのAIチャットパネルからはREST API (/api/tool) 経由で呼べるので、MCPクライアントがなくても使えます。
2. VPSローカルモード
VPS上で動作する時はSSHをスキップして直接コマンド実行します。
// ssh.js
function isLocalMode() {
return process.env.VPS_HOST === '127.0.0.1';
}
// ローカルなら bash -c で直接実行、リモートなら SSH
これで同じコードがローカル開発とVPS本番の両方で動きます。
3. ハンドラーの返却形式の統一
初期は返却形式がバラバラでした。
// pod.js - 生データを返す
return { pods: [...] };
// health.js - MCPフォーマット
return { content: [{ type: 'text', text: '...' }] };
server.js で isMcpFormat チェックを入れて両方に対応させました。REST APIは自動アンラップして生JSONを返します。最初から統一すべきでしたが、後方互換を保ちつつ対応できました。
セキュリティ: 15フェーズ修復の教訓
MCPサーバーは VPSにSSHしてコマンドを実行する という性質上、セキュリティが最重要です。45件の脆弱性を修正しました。
パスワード管理
// Before: pods.json にパスワード直書き 🚨
{ "password": "actual_password_here" }
// After: 環境変数名を指定
{ "password_env": "MOTHERSHIP_DB_PASSWORD" }
resolvePassword() で環境変数から解決する方式に変更。
SQLインジェクション防止
// validateTableName() - テーブル名のホワイトリスト検証
function validateTableName(name) {
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
throw new Error('Invalid table name');
}
}
// INTO OUTFILE / INTO DUMPFILE / LOAD_FILE をブロック
コマンドインジェクション防止
// shellEscape() - シングルクォートエスケープ
function shellEscape(str) {
return "'" + str.replace(/'/g, "'\\''") + "'";
}
// vps_exec - 許可パスの制限
const ALLOWED_PATHS = ['/var/www/', '/var/log/'];
const BLOCKED_PATHS = ['/etc/', '/root/', '~/.ssh/', ...];
セキュリティゲート(環境変数制御)
MOTHERSHIP_ALLOW_WRITE=false → DB書き込み系ツール無効
MOTHERSHIP_ALLOW_DEPLOY=false → デプロイ系ツール無効
デフォルトは全部 false。明示的に有効化しない限り危険な操作はできません。
Google Workspace統合(15ツール追加の実例)
直近で15ツールを一気に追加した流れ:
- CLIラッパー作成 — 既存のPHP Google APIクライアントを
php google-api-cli.php <service> <action>で呼べるように - MCPハンドラー — SSH経由でCLIラッパーを呼び出し、JSON返却をパース
- ツール定義 — 15ツール分のスキーマ定義
MCPハンドラー → SSH → php google-api-cli.php gmail summary → 既存APIクライアント → JSON
既存資産(PHPのGoogle APIクライアント)を捨てずにMCPから呼べるようにしたのがポイントです。全部Node.jsで書き直す必要はありません。
運用で学んだこと
sshExec() の返却値に注意
// sshExec() は stdout文字列を直接返す
const result = await sshExec('ls /var/www/');
// result は文字列。result.stdout ではない!
mysqlQuery() は常に同じ形式
const result = await mysqlQuery('mothership_db', sql);
// 常に { columns: [], rows: [], count: 0 }
// getRows(result) で安全に取得
PM2のrootとdeploy問題
sudo pm2 と pm2 は 別のPM2インスタンス です。同名プロセスが両方に存在するとポート競合(EADDRINUSE)が発生します。本番はdeployユーザー側に統一しました。
まとめ
- 小さく始めて育てる: 10ツールから69ツールへ。必要になったら追加
- セキュリティはデフォルト安全: 環境変数ゲートで書き込み・デプロイを制御
- 既存資産を活かす: PHPのAPIクライアントをCLIラッパー経由でMCPに統合
- tools/ と handlers/ の分離: ツール追加のコストを最小化
MCPサーバーは一度作ると、Claude Codeでの開発体験が劇的に変わります。「SSHしてコマンド打って結果をコピペして…」がなくなり、自然言語で全部できるようになります。