概要

Model Context Protocol (MCP) は、Large Language Model (LLM) とツール、データソースを統一的に接続するための標準プロトコルである。
MCPサーバは、AIアシスタントがアクセス可能なツール、リソース、プロンプトを提供するインターフェースとして機能する。

MCPサーバの実装において、Linuxディストリビューションに必要なプログラム言語とSDKをインストールする。
一般的な選択肢としては、Node.jsのMCP SDK、PythonのFastMCP、TypeScriptによる実装等がある。

MCPは2つの主要なトランスポート方式をサポートしている。

  • Standard I/O (STDIO) トランスポート
    ローカル環境でのプロセス間通信に使用され、Claude Desktopなどのクライアントとの統合に最適である。
  • HTTP/SSE (Server-Sent Events) トランスポート
    リモートサーバ上でMCPサーバを運用して、ネットワーク経由でアクセスする場合に使用される。


以下の例では、Node.jsのMCP SDKを使用したMCPサーバを構築している。

 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 import { z } from "zod";
 
 const server = new McpServer({
   name: "sample-mcp-server",
   version: "1.0.0",
 });
 
 // ツールの定義
 server.tool(
   "calculate-sum",
   "2つの数値を加算する",
   {
     a: z.number().describe("1つ目の数値"),
     b: z.number().describe("2つ目の数値"),
   },
   async ({ a, b }) => ({
     content: [{ type: "text", text: `結果: ${a + b}` }],
   })
 );
 
 // サーバの起動
 const transport = new StdioServerTransport();
 await server.connect(transport);


セキュリティでは、認証トークンの実装、HTTPS通信の設定、入力データのバリデーション、適切なエラーハンドリング等を行う必要がある。
特にHTTPトランスポートを使用する場合は、APIキーやOAuth2.0による認証・認可の実装も検討する必要がある。

パフォーマンスとスケーラビリティでは、ツールの実行時間の最適化、接続プールの管理、ログ記録の効率化が重要である。
また、コンテナ化 (Podman等) や オーケストレーション (Kubernetes) の導入により、複数のMCPサーバインスタンスを管理することも検討する。

MCPサーバのドキュメント化も重要な要素となる。
ツールの説明、パラメータの型定義、使用例を明確に記述することにより、AIモデルおよび開発者がサーバの機能を正確に理解することができる。
MCP Inspectorを使用することで、対話的にツールをテストおよび検証することが可能である。

モニタリングとログ収集の設定において、標準エラー出力 (stderr) を使用したログ記録、PrometheusやGrafanaによるメトリクス収集を行うことにより、
MCPサーバの健全性およびパフォーマンスを監視することができる。

MCPサーバを構築・運用する場合は、以下に示す事柄に注意する。

  • STDIOトランスポートでは標準出力 (stdout) にログを出力しない。(JSON-RPC通信が破損する)
  • ツールの実行時間を適切に管理して、タイムアウトを設定する。
  • エラーハンドリングを適切に実装して、詳細なエラーメッセージを返す。
  • セキュリティアップデートの適用
  • アクセスログの監視とレート制限の実装
  • ツールのバージョン管理とドキュメントの維持
  • 定期的なバックアップの実施



サーバ環境のインストール

まず、システムの更新を行う。

# RHEL
sudo dnf update

# SUSE
sudo zypper update

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo apt update
sudo apt upgrade


次に、Linuxサーバに必要な基本環境をインストールする。

# RHEL
sudo dnf install curl wget git gcc-c++ make

# SUSE
sudo zypper install curl wget git gcc-c++ make

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo apt install curl wget git build-essential



開発言語とフレームワークの選択

Node.js + MCP SDKを使用する場合

まず、Node.jsをインストールする。
RHEL / SUSEの場合、Node Version Manager (nvm) を使用することを推奨する。

# nvmのインストール (全ディストリビューション共通)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v<バージョン  例 : 0.43.0>/install.sh | bash

# Node.js LTS版のインストール
nvm install --lts
nvm use --lts


Banana Pi F3 (RISC-V) の場合は、パッケージ管理システムからインストールする。

# Banana Pi F3 (Armbian)
sudo apt install nodejs npm

# バージョン確認
node --version
npm --version


もし、公式バイナリが存在しない場合は、ソースコードからビルドを行う。

# ソースからのビルド (RISC-V環境で必要な場合)
git clone https://github.com/nodejs/node.git
cd node

git checkout v<バージョン  例 : 22.x>

mkdir -p build && cd build
../configure --prefix=<任意のインストールディレクトリ> \
             --dest-cpu=riscv64
make -j $(nproc)
make install


次に、プロジェクトディレクトリを作成して、MCPサーバのプロジェクトを初期化する。

mkdir <プロジェクトディレクトリ>
cd <プロジェクトディレクトリ>

npm init -y


必要なJavaScriptライブラリをインストールする。

npm install @modelcontextprotocol/sdk zod


package.jsonファイルを編集して、ES Modulesを有効化する。

  • typeキー
    typeキーを省略する場合は、commonjs (CommonJS形式) となる。
    • moduleを指定する場合
    ES Modules形式 (import/export構文) を使用する場合に設定する。
    • この場合、.jsファイルがESM形式として扱われ、importやexport文を使用できる。
    • commonjsを指定する場合
      CommonJS形式 (require() / module.exports構文) を使用する場合に設定する。
      Node.jsの従来の方式である。


 {
   "name": "<任意の名前  例 : mcp-server>",
   "version": "<任意のバージョン  例 : 1.0.0>",
   "type": "module",
   "scripts": {
     "start": "node server.js",
     "inspector": "npx @modelcontextprotocol/inspector node server.js"
   },
   "dependencies": {
     "@modelcontextprotocol/sdk": "latest",
     "zod": "^3.22.0"
   }
 }


Python + FastMCPを使用する場合

Pythonと仮想環境をインストールする。

# RHEL
sudo dnf install python3 python3-pip python3-virtualenv

# SUSE
sudo zypper install python3 python3-pip python3-virtualenv

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo apt install python3 python3-pip python3-venv


プロジェクトディレクトリを作成して、Python仮想環境を設定する。

mkdir -p <プロジェクトディレクトリ>
cd <プロジェクトディレクトリ>

python3 -m venv venv


Pythonの仮想環境をアクティベートする。

# Bash / Zshの場合
source venv/bin/activate

# Fishの場合
source venv/bin/activate.fish


必要なPythonライブラリをインストールする。

pip install fastmcp httpx


※注意
venv/bin/activateスクリプトの設定を~/.profileファイル等に記述することは非推奨である。

activateスクリプトは相対パスで動作するため、フルパスで指定する必要がある。
これは、常に特定の仮想環境がアクティブになってしまうため、他のプロジェクトで異なる仮想環境を使用する場合に問題が発生するためである。

また、シェルのセッションごとに自動的にアクティベートすると、どの環境で作業しているのか理解し難い。
代替案として、以下に示すような方法が推奨される。
これは、必要な時のみ仮想環境をアクティベートすることが可能なため、柔軟な運用が可能になる。

特に、複数のプロジェクトを扱う場合は、"方法 2" や "方法 3" が便利である。

  • エイリアスを設定する方法
    ~/.profileファイル等に記述する。
 # 方法 1
 
 alias activate-mcpserver="source <activateスクリプトのパス>"


  • プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法
    ~/.profileファイル または ~/.zprofileファイル等に記述する。
 # 方法 2 (a) : Bash / Zsh
 
 function check_venv()
 {
    if [ -d "venv" ]; then
       if [ -z "$VIRTUAL_ENV" ]; then
          source venv/bin/activate
       elif [ "$VIRTUAL_ENV" != "$PWD/venv" ]; then
          deactivate
          source venv/bin/activate
       fi
    elif [ -n "$VIRTUAL_ENV" ]; then
       deactivate
    fi
 }
 
 function cd()
 {
    builtin cd "$@"
    check_venv
 }
 
 # プロンプト表示前に毎回チェック
 PROMPT_COMMAND="check_venv${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
 
 # シェル起動時にもチェック
 check_venv


  • プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法
    Fishを使用している場合は、以下に示す2つのファイルに設定を追加する。
    ~/.config/fish/config.fish
    ~/.config/fish/functions/check_venv.fish
 # 方法 2 (b) : Fish
 
 # ~/.config/fish/config.fishファイル
 check_venv
 
 # ~/.config/fish/functions/check_venv.fishファイル
 function check_venv --on-variable PWD --on-event fish_prompt
    if test -d "venv"
       if test -z "$VIRTUAL_ENV"
          source venv/bin/activate.fish
       else if test "$VIRTUAL_ENV" != "$PWD/venv"
          deactivate
          source venv/bin/activate.fish
       end
    else if test -n "$VIRTUAL_ENV"
       deactivate
    end
 end
 
 function cd
    builtin cd $argv
 end


  • direnvのような専用ツールを使用する方法
    プロジェクトディレクトリの.envrcファイルに記述する。
 # 方法 3
 
 layout python3  # Pythonの仮想環境を自動的に管理



MCPサーバの構築

Node.js + STDIOトランスポートを使用する場合

STDIOトランスポートは、ローカル環境でのプロセス間通信に使用される。

これは、Claude DesktopやCursor等のMCPクライアントと統合する場合に最適である。

 // server.jsファイル
 
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 import { z } from "zod";
 import os from "os";
 
 // MCPサーバインスタンスの作成
 const server = new McpServer({
   name: "linux-system-mcp-server",
   version: "1.0.0",
 });
 
 // ツール 1 : 数値計算
 server.tool(
   "add-numbers",
   "2つの数値を加算する",
   {
     number1: z.number().describe("1つ目の数値"),
     number2: z.number().describe("2つ目の数値"),
   },
   async ({ number1, number2 }) => ({
     content: [
       {
         type: "text",
         text: `計算結果: ${number1} + ${number2} = ${number1 + number2}`,
       },
     ],
   })
 );
 
 // ツール 2 : システム情報取得
 server.tool(
   "get-system-info",
   "Linuxシステムの情報を取得する",
   {},
   async () => {
     const info = {
       platform: os.platform(),
       arch: os.arch(),
       hostname: os.hostname(),
       cpus: os.cpus().length,
       totalMemory: `${Math.round(os.totalmem() / 1024 / 1024)} MB`,
       freeMemory: `${Math.round(os.freemem() / 1024 / 1024)} MB`,
       uptime: `${Math.round(os.uptime() / 3600)} hours`,
     };
 
     return {
       content: [
         {
           type: "text",
           text: JSON.stringify(info, null, 2),
         },
       ],
     };
   }
 );
 
 // ツール 3 : ファイル一覧取得
 server.tool(
   "list-files",
   "指定されたディレクトリのファイル一覧を取得する",
   {
     path: z.string().describe("ディレクトリのパス"),
   },
   async ({ path }) => {
     const fs = await import("fs/promises");
     try {
       const files = await fs.readdir(path);
       return {
         content: [
           {
             type: "text",
             text: `ファイル一覧:\n${files.join("\n")}`,
           },
         ],
       };
     } catch (error) {
       return {
         content: [
           {
             type: "text",
             text: `エラー: ${error.message}`,
           },
         ],
         isError: true,
       };
     }
   }
 );
 
 // サーバの起動
 async function main() {
   const transport = new StdioServerTransport();
   await server.connect(transport);
   
   // STDIOトランスポートではstderrに出力する (stdoutは使用禁止)
   console.error("MCPサーバが起動しました");
 }
 
 main().catch((error) => {
   console.error("サーバ起動エラー:", error);
   process.exit(1);
 });


Node.js + HTTPトランスポートを使用する場合

HTTPトランスポートは、リモートサーバ上でMCPサーバを運用し、ネットワーク経由でアクセスする場合に使用される。

まず、HTTP関連のライブラリをインストールする。

npm install express cors


HTTPサーバ対応の実装を行う。

 // http-server.jsファイル
 
 import express from "express";
 import cors from "cors";
 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
 import { z } from "zod";
 
 const app = express();
 app.use(cors());
 app.use(express.json());
 
 const server = new McpServer({
   name: "linux-system-mcp-server",
   version: "1.0.0",
 });
 
 // ツールの定義 (STDIOトランスポートと同様)
 server.tool(
   "get-system-info",
   "Linuxシステムの情報を取得する",
   {},
   async () => {
     // 実装内容は省略
   }
 );
 
 // SSEエンドポイントの設定
 app.post("/sse", async (req, res) => {
   const transport = new SSEServerTransport("/messages", res);
   await server.connect(transport);
 });
 
 // ヘルスチェックエンドポイント
 app.get("/health", (req, res) => {
   res.json({ status: "ok", timestamp: new Date().toISOString() });
 });
 
 const PORT = process.env.PORT || 3000;
 const HOST = process.env.HOST || "0.0.0.0";
 
 app.listen(PORT, HOST, () => {
   console.log(`MCPサーバがポート${PORT}で起動しました`);
   console.log(`ホスト: ${HOST}`);
 });


Python + FastMCPを使用する場合

FastMCPは、PythonでMCPサーバを構築するためのフレームワークである。
型ヒントとデコレータを使用して、ツールを定義することができる。

 # server.pyファイル
 
 from fastmcp import FastMCP
 import platform
 import os
 import subprocess
 
 mcp = FastMCP("Linux System MCP Server")
 
 @mcp.tool()
 def add_numbers(number1: float, number2: float) -> str:
    """2つの数値を加算する"""
    result = number1 + number2
    return f"計算結果: {number1} + {number2} = {result}"
 
 @mcp.tool()
 def get_system_info() -> dict:
    """Linuxシステムの情報を取得する"""
    return {
       "platform": platform.system(),
       "architecture": platform.machine(),
       "hostname": platform.node(),
       "python_version": platform.python_version(),
       "processor": platform.processor(),
    }
 
 @mcp.tool()
 def list_files(path: str) -> str:
    """指定されたディレクトリのファイル一覧を取得する"""
    try:
       files = os.listdir(path)
       return f"ファイル一覧:\n" + "\n".join(files)
    except Exception as e:
       return f"エラー: {str(e)}"
 
 @mcp.tool()
 def execute_command(command: str) -> str:
    """安全なシェルコマンドを実行する (制限付き)"""
    # セキュリティのため、許可されたコマンドのみ実行
    allowed_commands = ["ls", "pwd", "whoami", "date", "uptime"]
 
    cmd_parts = command.split()
    if not cmd_parts or cmd_parts[0] not in allowed_commands:
       return f"エラー: コマンド '{cmd_parts[0] if cmd_parts else ''}' は許可されていません"
 
    try:
       result = subprocess.run(
          command,
          shell=True,
          capture_output=True,
          text=True,
          timeout=5
       )
       return result.stdout if result.returncode == 0 else result.stderr
    except subprocess.TimeoutExpired:
       return "エラー: コマンドがタイムアウトしました"
    except Exception as e:
       return f"エラー: {str(e)}"
 
 if __name__ == "__main__":
    mcp.run()



MCP Inspectorによるテスト

WebMCP Inspectorは、Anthropic社が提供する対話的なデバッグツールである。
これにより、Webブラウザ上でMCPサーバのツールをテストおよび検証することができる。

MCP Inspectorをインストールする。

# Node.js版の場合
npm install @modelcontextprotocol/inspector --save-dev


package.jsonにスクリプトを追加する。

 {
   "scripts": {
     "inspector": "npx @modelcontextprotocol/inspector node server.js"
   }
 }


MCP Inspectorを起動する。

npm run inspector


Webブラウザが自動的に開き、インスペクターのインターフェースが表示される。
ツールの一覧、パラメータの入力、実行結果の確認が可能である。


クライアント接続設定

MCPサーバの設定ファイルの保存場所は、以下の通りである。

  • MacOS / Linuxの場合
    ~/.config/Claude/claude_desktop_config.json

  • Windowsの場合
    %APPDATA%\Claude\claude_desktop_config.json


Claude Desktopからの接続 (STDIO)

Claude Desktopの設定ファイルを編集する。

設定ファイルの内容を編集する。

 {
   "mcpServers": {
     "linux-system-server": {
       "command": "node",
       "args": ["/home/username/mcp-server/server.js"]
     }
   }
 }


Pythonサーバの場合は以下に示すように設定する。

 {
   "mcpServers": {
     "linux-system-python-server": {
       "command": "/home/<ユーザ名>/mcp-server-python/venv/bin/python",
       "args": ["/home/<ユーザ名>/mcp-server-python/server.py"]
     }
   }
 }


環境変数を設定する必要がある場合は、envフィールドを追加する。

 {
   "mcpServers": {
     "linux-system-server": {
       "command": "node",
       "args": ["/home/<ユーザ名>/mcp-server/server.js"],
       "env": {
         "API_KEY": "your-api-key-here",
         "LOG_LEVEL": "debug"
       }
     }
   }
 }


リモートサーバへのSSH接続 (STDIO)

SSHを経由してリモートサーバ上のMCPサーバに接続する場合は、以下に示すように設定する。

 {
   "mcpServers": {
     "remote-linux-server": {
       "command": "ssh",
       "args": [
         "<ユーザ名>@<リモートサーバのIPアドレス>",
         "cd ~/mcp-server && node server.js"
       ]
     }
   }
 }


また、以下に示すように、公開鍵認証を設定しておくことが推奨される。

  1. まず、ローカルPC上でRSA形式のSSH鍵ペアを生成する。
    現在推奨されるRSA鍵の長さは4096ビットである。
    ssh-keygen -t rsa -b 4096 -C "mcp-client"
  2. 次に、公開鍵をリモートPCに転送する。
    ssh-copy-id コマンドを実行することにより、簡単に設定することができる。
    このコマンドは、リモートPCのパスワード認証を使用して接続し、公開鍵を自動的に ~/.ssh/authorized_keys ファイルに追加するものである。
    ssh-copy-id -i ~/.ssh/id_rsa_mcp.pub <ユーザ名>@<IPアドレス または ホスト名>


環境によっては、ssh-copy-id コマンドが利用できない場合がある。
その場合は、以下に示すコマンドで手動で公開鍵を転送することができる。

cat ~/.ssh/id_rsa_mcp.pub | ssh <ユーザ名>@<IPアドレス または ホスト名> "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"


更に、リモートPC側で権限を設定する必要がある。

ssh <ユーザ名>@<IPアドレス または ホスト名> "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"



HTTPS対応

HTTPトランスポートを使用する場合、HTTPS通信を設定することが推奨される。

Let's Encryptを使用する場合

Certbotをインストールする。

# RHEL
sudo dnf install certbot python3-certbot-nginx

# SUSE
sudo zypper install python3-certbot python3-certbot-nginx

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo apt install certbot python3-certbot-nginx


SSL証明書を取得する。

sudo certbot --nginx -d example.com


証明書の自動更新を設定する。

sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timer



リバースプロキシの設定

HTTPトランスポートを使用する場合、Nginxをリバースプロキシとして設定することが推奨される。

Nginxを使用する場合

Nginxをインストールする。

# RHEL
sudo dnf install nginx

# SUSE
sudo zypper install nginx

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo apt install nginx


Nginxの設定ファイルを作成する。

 # /etc/nginx/sites-available/mcp-serverファイル
 
 server {
    listen 80;
    server_name yourdomain.com;
 
    # HTTPからHTTPSへリダイレクト
    return 301 https://$server_name$request_uri;
 }
 
 server {
    listen 443 ssl http2;
    server_name yourdomain.com;
 
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
 
    # SSL設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
 
    location / {
       proxy_pass http://localhost:3000;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection 'upgrade';
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_cache_bypass $http_upgrade;
    }
 
    # SSEエンドポイント用の設定
    location /sse {
       proxy_pass http://localhost:3000/sse;
       proxy_http_version 1.1;
       proxy_set_header Connection '';
       proxy_buffering off;
       proxy_cache off;
       proxy_read_timeout 86400s;
    }
 }


設定ファイルを有効化する。

# RHEL / SUSE
sudo ln -s /etc/nginx/sites-available/mcp-server /etc/nginx/sites-enabled/

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo ln -s /etc/nginx/sites-available/mcp-server /etc/nginx/sites-enabled/


Nginxの設定をテストして再起動する。

sudo nginx -t
sudo systemctl restart nginx



Systemdサービスファイルの作成

MCPサーバをシステムサービスとして常時起動させる場合、systemdユニットファイルを作成する。

Node.js版MCPサーバの場合

 # /etc/systemd/system/mcp-server.serviceファイル
 
 [Unit]
 Description=MCP Server for Linux System
 After=network.target
 
 [Service]
 Type=simple
 User=<実行する任意のユーザ名>
 WorkingDirectory=/home/<ユーザ名>/mcp-server
 ExecStart=/usr/bin/node server.js
 Restart=always
 RestartSec=10
 StandardOutput=journal
 StandardError=journal
 
 # 環境変数の設定 (必要に応じて)
 Environment="NODE_ENV=production"
 Environment="PORT=3000"
 
 [Install]
 WantedBy=multi-user.target


Python版MCPサーバの場合

 # /etc/systemd/system/mcp-server-python.serviceファイル
 
 [Unit]
 Description=MCP Server for Linux System (Python)
 After=network.target
 
 [Service]
 Type=simple
 User=<実行する任意のユーザ名>
 WorkingDirectory=/home/<ユーザ名>/mcp-server-python
 ExecStart=/home/<ユーザ名>/mcp-server-python/venv/bin/python server.py
 Restart=always
 RestartSec=10
 StandardOutput=journal
 StandardError=journal
 
 [Install]
 WantedBy=multi-user.target


サービスを有効化して起動する。

sudo systemctl daemon-reload
sudo systemctl enable mcp-server
sudo systemctl start mcp-server


サービスの状態を確認する。

sudo systemctl status mcp-server


ログを確認する。

sudo journalctl -u mcp-server -f



ファイアウォールの設定

HTTPトランスポートを使用する場合、適切なポートを開放する必要がある。

Firewalldの場合

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https

# カスタムポートを使用する場合
sudo firewall-cmd --permanent --add-port=3000/tcp

sudo firewall-cmd --reload


設定を確認する。

sudo firewall-cmd --list-all


UFWの場合

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# カスタムポートを使用する場合
sudo ufw allow 3000/tcp

sudo ufw enable


設定を確認する。

sudo ufw status verbose


特定のIPアドレスからのアクセスのみを許可する場合は、以下のように設定する。

sudo ufw allow from 192.168.1.0/24 to any port 3000



セキュリティ設定

認証の実装

HTTPトランスポートを使用する場合、APIキーによる認証を行うことが推奨される。

 // 認証ミドルウェアの実装例 (Node.js + Express)
 
 function authenticateRequest(req, res, next) {
   const apiKey = req.headers['x-api-key'];
   
   if (!apiKey || apiKey !== process.env.API_KEY) {
     return res.status(401).json({ error: 'Unauthorized' });
   }
   
   next();
 }
 
 app.use('/sse', authenticateRequest);


レート制限の実装

DoS攻撃を防ぐため、レート制限を行う。

npm install express-rate-limit


 import rateLimit from 'express-rate-limit';
 
 const limiter = rateLimit({
   windowMs: 15 * 60 * 1000, // 15分
   max: 100, // 最大100リクエスト
   message: 'リクエストが多すぎます。しばらくしてから再試行してください。'
 });
 
 app.use('/sse', limiter);


入力データのバリデーション

ツールのパラメータに対して、適切なバリデーションを行う。
Zodスキーマを使用することにより、型安全性が確保される。

 server.tool(
   "read-file",
   "ファイルの内容を読み取る",
   {
     path: z.string()
       .min(1)
       .max(500)
       .regex(/^[a-zA-Z0-9._\/-]+$/)
       .describe("ファイルのパス (英数字、ハイフン、スラッシュのみ)"),
   },
   async ({ path }) => {
     // パス トラバーサル攻撃を防ぐ
     if (path.includes('..')) {
       return {
         content: [{ type: "text", text: "エラー: 無効なパスです" }],
         isError: true,
       };
     }
     
     // 実装内容
   }
 );



監視とログ管理

ログの実装

MCPサーバでは、STDIOトランスポートを使用する場合、標準エラー出力 (stderr) にログを出力する必要がある。
標準出力 (stdout) にログを出力すると、JSON-RPC通信が破損する。

 // Node.jsでのログ実装例
 
 function log(level, message, data = {}) {
   const logEntry = {
     timestamp: new Date().toISOString(),
     level,
     message,
     ...data,
   };
 
   console.error(JSON.stringify(logEntry));
 }
 
 // 使用例
 log('info', 'ツールが呼び出されました', { tool: 'get-system-info' });
 log('error', 'エラーが発生しました', { error: error.message });


PrometheusとGrafanaによる監視

メトリクスを収集するため、Prometheus用のエクスポータを構築する。

npm install prom-client


 import { register, Counter, Histogram } from 'prom-client';
 
 // メトリクスの定義
 const toolCallCounter = new Counter({
   name: 'mcp_tool_calls_total',
   help: 'ツール呼び出しの総数',
   labelNames: ['tool_name', 'status'],
 });
 
 const toolCallDuration = new Histogram({
   name: 'mcp_tool_call_duration_seconds',
   help: 'ツール呼び出しの実行時間',
   labelNames: ['tool_name'],
 });
 
 // メトリクスエンドポイント
 app.get('/metrics', async (req, res) => {
   res.set('Content-Type', register.contentType);
   res.end(await register.metrics());
 });


Prometheusをインストールする。

# RHELの場合
sudo dnf install prometheus

# SUSEの場合
sudo zypper install prometheus

# Banana Pi F3 (Armbian) / Raspberry Piの場合
wget https://github.com/prometheus/prometheus/releases/download/v2.x.x/prometheus-2.x.x.linux-arm64.tar.gz
tar xf prometheus-2.x.x.linux-arm64.tar.gz


Prometheusの設定ファイルを編集する。

 # prometheus.ymlファイル
 
 global:
   scrape_interval: 15s
 
 scrape_configs:
   - job_name: 'mcp-server'
     static_configs:
       - targets: ['localhost:3000']


Grafanaをインストールする。

# RHEL
sudo dnf install grafana

# SUSE
sudo zypper install grafana

# Banana Pi F3 (Armbian) / Raspberry Pi
sudo apt install grafana


GrafanaとPrometheusを起動する。

sudo systemctl enable prometheus grafana-server
sudo systemctl start prometheus grafana-server



バックアップとリストア

設定ファイルのバックアップ

定期的にMCPサーバの設定ファイルおよびコードをバックアップする。

 #!/usr/bin/env sh
 # backup.sh
 
 BACKUP_DIR="/var/backups/mcp-server"
 DATE=$(date +%Y%m%d_%H%M%S)
 PROJECT_DIR="/home/username/mcp-server"
 
 # バックアップディレクトリの作成
 mkdir -p $BACKUP_DIR
 
 # プロジェクトディレクトリの圧縮
 tar -czf "$BACKUP_DIR/mcp-server_$DATE.tar.gz" -C $(dirname $PROJECT_DIR) $(basename $PROJECT_DIR)
 
 # 30日以上前のバックアップを削除
 find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete


バックアップスクリプトを実行可能にする。

chmod u+x backup.sh


cronジョブとして設定する。

crontab -e


# 毎日午前2時にバックアップを実行
0 2 * * * /path/to/backup.sh



トラブルシューティング

MCPサーバが起動しない

  • ログを確認する。
    sudo journalctl -u mcp-server -n 50
  • ポートが既に使用されていないか確認する。
    sudo lsof -i :3000
  • Node.js または Pythonのバージョンを確認する。


Claude Desktopから接続できない

  • 設定ファイルのパスが正しいか確認する。
  • ファイルの実行権限を確認する
    chmod u+x server.js
  • STDIOトランスポートの場合、標準出力にログを出力していないか確認する。


ツールの実行が遅い

  • ツールの実装を最適化する。
  • タイムアウト設定を確認する。
  • システムリソースを確認する
    topコマンド または htopコマンド


RISC-V環境でNode.jsが動作しない

  • ソースコードからビルドする必要がある場合がある。
  • バージョン互換性を確認する。
  • 依存関係を確認する
    ldd /path/to/node



パフォーマンス最適化

Node.jsの最適化

 # Node.jsのメモリ制限を増やす
 node --max-old-space-size=4096 server.js
 
 # クラスタリングによる負荷分散
 # cluster.jsファイルを作成


 import cluster from 'cluster';
 import os from 'os';
 
 if (cluster.isPrimary) {
   const numCPUs = os.cpus().length;
 
   for (let i = 0; i < numCPUs; i++) {
     cluster.fork();
   }
 
   cluster.on('exit', (worker, code, signal) => {
     console.error(`ワーカー ${worker.process.pid} が終了しました`);
     cluster.fork();
   });
 }
 else {
   // サーバの起動
   import('./server.js');
 }


キャッシングの実装

頻繁にアクセスされるデータをキャッシュすることにより、パフォーマンスを向上させる。

npm install node-cache


 import NodeCache from 'node-cache';
 
 const cache = new NodeCache({ stdTTL: 600 }); // 10分間キャッシュ
 
 server.tool(
   "get-cached-data",
   "キャッシュされたデータを取得する",
   {},
   async () => {
     const cacheKey = 'system-info';
     let data = cache.get(cacheKey);
 
     if (!data) {
       // データを取得してキャッシュに保存
       data = await fetchSystemInfo();
       cache.set(cacheKey, data);
     }
 
     return {
       content: [{ type: "text", text: JSON.stringify(data) }],
     };
   }
 );