背景
⚠️ 本記事のコードはサンプルです。実際の認証情報・設定値は含まれていません。
自作のMCPサーバー @claudecodenavi/mcp はClaude Code向けにstdio専用で動いていた。しかしVPS上のAIシステム(AI社長)からも同じツールを使いたくなった。
選択肢は2つあった:
- VPS側に独自のツール実装を作る(重複管理が発生)
- npm版にHTTPモードを追加して、VPSにインストールして使う
2を選択。 コードが一箇所で管理され、一般ユーザーもHTTPモードが使える。
実装内容
server.jsのリファクタリング
Before(v1.0.2):
- グローバルな
server変数にツール定義とハンドラーを直接設定 StdioServerTransportのみ
After(v1.1.0):
- ツール定義を
TOOLS配列に分離 - ハンドラーを
handleToolCall()関数に分離 createMcpServer()ファクトリーで複数インスタンス生成可能に--httpフラグでHTTPモード起動
// 起動モード
const HTTP_MODE = process.argv.includes('--http');
// メイン
if (HTTP_MODE) {
startHttpMode().catch(console.error);
} else {
startStdioMode().catch(console.error);
}
HTTPモードのエンドポイント
| エンドポイント | 用途 |
|---|---|
| POST /mcp | StreamableHTTP(MCP公式プロトコル) |
| POST /api/tool | REST API直接呼び出し |
| GET /api/tools | ツール一覧取得 |
| GET /health | ヘルスチェック |
Bearer token認証はMCP_AUTH_TOKEN環境変数で設定。
package.jsonの変更
{
"version": "1.1.0",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.9.0",
"express": "^4.21.0",
"cors": "^2.8.5"
}
}
SDK ^0.5.0 → ^1.9.0(StreamableHTTPServerTransport対応のため)。express/corsはHTTPモード時のみ動的importされるので、stdioモードでは読み込まれない。
VPS側の構成
複数MCPサーバーのルーティング
AI社長(ai-router.php)は複数のMCPサーバーからツールを集約して使う。
config/mcp-tools.phpを改修して、複数サーバーからツール定義を取得し、ツール名→サーバーのルーティングマップを自動構築するようにした。
$servers = [
'mothership' => [
'endpoint' => 'http://127.0.0.1:3002/api/tool',
'tools_url' => 'http://127.0.0.1:3002/api/tools',
],
'claudecodenavi' => [
'endpoint' => 'http://127.0.0.1:3003/api/tool',
'tools_url' => 'http://127.0.0.1:3003/api/tools',
],
];
ai-router.phpのcallMcpTool()がツール名からルーティング先を自動解決:
private function callMcpTool(string $toolName, array $args): mixed {
$routing = $this->mcpConfig['tool_routing'] ?? [];
$serverKey = $routing[$toolName] ?? 'mothership';
$server = $this->mcpConfig['servers'][$serverKey] ?? null;
$endpoint = $server['endpoint'] ?? ...;
// ...
}
これによりcreate_postはclaudecodenavi(3003)へ、pod_listはmothership(3002)へ自動ルーティングされる。
結果
| サーバー | ポート | ツール数 |
|---|---|---|
| mcp-mothership | 3002 | 69 |
| @claudecodenavi/mcp | 3003 | 33 |
| 合計 | | 102 |
後方互換の確認
SDK v0.5.0 → v1.9.0でstdioのプロトコル(JSON-RPC改行区切り)は変わっていない。実際にパイプでメッセージを流して確認:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize",...}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | node server.js
→ initialize正常応答 + 33ツール全て返却。一般ユーザーのstdioモードに影響なし、ダウンタイム0秒。
まとめ
- npm版MCPサーバーにHTTPモードを追加するパターンは汎用的に使える
--httpフラグで切り替えるだけなので、一般ユーザーのstdioモードに一切影響しない- PHP側のルーティング基盤を作ったことで、今後新しいMCPサーバーを追加する際も
$servers配列に1エントリ追加するだけ - express/corsはHTTPモード時のみ動的importなので、stdioモードのメモリフットプリントは増えない