MCPサーバー構築とローカルLLM連携の概要
Model Context Protocol (MCP) は、大規模言語モデル(LLM)が外部のツールやデータソースに安全かつ構造化された方法でアクセスするためのプロトコルです。特に、ChatGPTやClaudeなどのAIアシスタントに、ローカル環境で動作するLLMやデータベース、APIの機能を拡張するために利用されます。本記事では、MCPサーバーを構築し、ローカルで実行しているLLM(例:Llama 3, Gemma, Mixtral)と連携させる方法を、実際に発生する可能性のあるエラーとその解決策を含めて詳しく解説します。
よくある問題とエラーメッセージ
MCPサーバー構築とローカルLLM連携において、初心者から中級者が直面する主な課題は以下の通りです。
1. サーバー起動時の接続エラー
MCPサーバーは起動したが、クライアント(Claude Desktop等)から接続できないという問題です。具体的なエラーメッセージは以下のようなものです。
Error: Failed to connect to MCP server at 'http://localhost:8080'
Error: Connection refused
Error: Invalid MCP server response. The server did not advertise any tools.
2. ローカルLLM APIとの通信エラー
MCPサーバーは正常に起動しても、内部で呼び出しているローカルLLM(例:Ollama, llama.cppサーバー)との通信に失敗することがあります。
Error calling local LLM API: Post "http://localhost:11434/api/generate": context deadline exceeded
Error: Local model 'llama3:8b' not found. Please pull the model first.
MCP Server Error: 500 Internal Server Error - LLM generation failed.
3. プロトコル準拠や設定ファイルのエラー
MCPサーバーの実装がプロトコル仕様に完全に準拠していない場合や、クライアント側の設定ファイル(例:Claude Desktopの`claude_desktop_config.json`)の記述ミスによるエラーです。
Error: MCP protocol violation: Missing required field 'name' in tool definition.
Error: Unsupported configuration file format. Expected JSON.
Error in config: The specified MCP server script does not exist or is not executable.
エラーの原因と詳細解説
接続エラーの原因
「Connection refused」エラーは、最も一般的な原因として、MCPサーバープロセスが実際には起動していない、または異なるポートでリッスンしていることが挙げられます。サーバースクリプトに実行権限がない、必要なPython/Node.jsの依存パッケージがインストールされていない場合も発生します。「Invalid response」エラーは、サーバーが起動しているものの、MCPプロトコルで要求される初期化シーケンス(`initialize` → `tools/list`)を正しく実装・応答していないことを示しています。
ローカルLLM通信エラーの原因
「context deadline exceeded」は、MCPサーバーからローカルLLMサーバー(Ollama等)へのリクエストがタイムアウトしたことを意味します。これは、LLMサーバーが起動していない、モデルの読み込みに時間がかかりすぎている、またはネットワーク設定の問題が原因です。「model not found」エラーは、Ollamaなどで指定したモデル名が間違っている、または`ollama pull`コマンドでモデルを事前にダウンロードしていない場合に発生します。
設定ファイルエラーの原因
MCPプロトコルは、ツール定義やメッセージ形式について厳格なJSONスキーマを要求します。フィールド名のタイポや、必須フィールドの欠落は即座にエラーとなります。また、Claude Desktopなどのクライアントは、設定ファイルのパスや形式が決まっており、それに従わないとサーバーを認識できません。
ステップバイステップ解決方法
ステップ1: 環境構築と基本MCPサーバーの作成
まず、Python環境を整え、MCPの基本ライブラリをインストールします。ここではPythonの`mcp`ライブラリを使用した簡単なサーバー例を示します。
# 1. 仮想環境の作成と有効化
python -m venv mcp-venv
source mcp-venv/bin/activate # Linux/macOS
# mcp-venvScriptsactivate # Windows
# 2. 必要なパッケージのインストール
pip install mcp
pip install requests # ローカルLLM API呼び出し用
# 3. 簡単なMCPサーバー(server.py)の作成
import asyncio
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import requests
import json
server = Server("local-llm-server")
# ローカルLLM(Ollama)を呼び出すツールを定義
@server.list_tools()
async def handle_list_tools():
return [
{
"name": "ask_local_llama",
"description": "ローカルのLlamaモデルに質問を投げます。",
"inputSchema": {
"type": "object",
"properties": {
"prompt": {"type": "string", "description": "LLMへのプロンプト"}
},
"required": ["prompt"]
}
}
]
@server.call_tool()
async def handle_call_tool(name: str, arguments: dict):
if name == "ask_local_llama":
prompt = arguments.get("prompt", "")
# Ollama APIを呼び出す
try:
response = requests.post(
'http://localhost:11434/api/generate',
json={
"model": "llama3:8b", # 使用するモデル名
"prompt": prompt,
"stream": False
},
timeout=60 # タイムアウトを60秒に設定
)
response.raise_for_status()
result = response.json()
return [{"type": "text", "text": result.get("response", "No response generated.")}]
except requests.exceptions.ConnectionError:
return [{"type": "text", "text": "エラー: ローカルLLMサーバー(Ollama)に接続できません。起動しているか確認してください。"}]
except requests.exceptions.Timeout:
return [{"type": "text", "text": "エラー: ローカルLLMからの応答がタイムアウトしました。"}]
except Exception as e:
return [{"type": "text", "text": f"エラー: {str(e)}"}]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="local-llm-server",
server_version="0.1.0"
),
NotificationOptions(),
)
if __name__ == "__main__":
asyncio.run(main())
ステップ2: ローカルLLMサーバーの準備と起動
MCPサーバーが連携する対象であるローカルLLMサーバー(ここではOllamaを例にします)を起動します。
# 1. Ollamaのインストール(公式サイトから)
# 2. モデルのダウンロード(必須ステップ)
ollama pull llama3:8b
# 3. Ollamaサーバーの起動(通常、サービスとして自動起動します)
# 起動確認
curl http://localhost:11434/api/tags
# 応答例: {"models":[{"name":"llama3:8b","modified_at":"2024-...", ...}]}
ステップ3: MCPサーバーの起動と動作確認
作成したMCPサーバーを直接テストするには、`mcp` CLIや簡単なテストクライアントを使用します。
# まず、MCPサーバーを起動します(別ターミナルで)。
python server.py
# このサーバーはstdioを介して通信するため、ターミナルには何も表示されないことが正常です。
# 別のターミナルで、mcp CLIを使ってツール一覧を取得してみます(インストールが必要な場合あり)。
# または、以下のような簡単なテストスクリプト(test_client.py)を作成します。
import asyncio
import json
import sys
import subprocess
async def test_mcp_server():
# server.py プロセスを起動
proc = await asyncio.create_subprocess_exec(
sys.executable, 'server.py',
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
# ここでプロトコルに沿った初期化メッセージを送信・受信する処理が必要です。
# 実際には、mcpライブラリのクライアント機能を使うか、Claude Desktopでテストするのが現実的です。
print("MCPサーバープロセスを起動しました。Claude Desktopの設定に進みます。")
asyncio.run(test_mcp_server())
ステップ4: Claude Desktopへの統合(設定ファイル)
Claude DesktopアプリケーションでローカルのMCPサーバーを使用するには、設定ファイルを編集します。
// 設定ファイルの場所
// macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
// Windows: %APPDATA%Claudeclaude_desktop_config.json
// claude_desktop_config.json の内容例
{
"mcpServers": {
"local-llama": {
"command": "/absolute/path/to/mcp-venv/bin/python",
"args": [
"/absolute/path/to/your/project/server.py"
]
}
}
}
注意点: `command`と`args`のパスは絶対パスで指定する必要があります。相対パスでは動作しないことがほとんどです。設定ファイルを変更したら、Claude Desktopアプリを完全に再起動してください。
実践的なコード例:ファイル読み込みツールの追加
MCPサーバーは複数のツールを提供できます。ローカルLLMにファイルの内容をコンテキストとして提供するツールを追加してみましょう。
# server.py に以下のツール定義とハンドラを追加
@server.list_tools()
async def handle_list_tools():
return [
{
"name": "ask_local_llama",
"description": "ローカルのLlamaモデルに質問を投げます。",
"inputSchema": {
"type": "object",
"properties": {
"prompt": {"type": "string", "description": "LLMへのプロンプト"}
},
"required": ["prompt"]
}
},
{ # 新しいツールを追加
"name": "read_file_content",
"description": "指定したテキストファイルの内容を読み込み、その内容を返します。",
"inputSchema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "読み込むファイルへの絶対パス"}
},
"required": ["file_path"]
}
}
]
@server.call_tool()
async def handle_call_tool(name: str, arguments: dict):
if name == "ask_local_llama":
# ... 前述の実装 ...
elif name == "read_file_content": # 新しいツールの処理
file_path = arguments.get("file_path", "")
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
return [{"type": "text", "text": f"ファイルの内容:n{content}"}]
except FileNotFoundError:
return [{"type": "text", "text": f"エラー: ファイル '{file_path}' が見つかりません。"}]
except Exception as e:
return [{"type": "text", "text": f"エラー: {str(e)}"}]
raise ValueError(f"Unknown tool: {name}")
まとめと補足情報
MCPサーバーを構築してローカルLLMと連携させることで、ClaudeやChatGPTなどのAIアシスタントに、プライベートなデータや特定の機能を安全に拡張することが可能になります。成功のための重要なポイントは以下の通りです。
- 絶対パスの使用: 設定ファイル内の`command`と`args`、およびファイル操作ツール内のパスは、必ず絶対パスを使用してください。
- 段階的なデバッグ: 1) Ollama単体のAPI動作確認、2) MCPサーバー単体のテスト(簡単なツールで)、3) Claude Desktop連携、の順で問題を切り分けます。
- タイムアウト設定: ローカルLLMの応答は時間がかかる場合があるため、`requests`や`aiohttp`のタイムアウト値を多めに設定しましょう。
- プロトコル仕様の確認: エラーが解決しない場合は、MCPの公式ドキュメントを参照し、メッセージ形式が正しいか確認します。
この仕組みを応用すれば、データベース照会ツール、グラフ生成ツール、社内API呼び出しツールなど、様々なカスタムツールをAIアシスタントに追加できるようになります。ローカル環境の強力なLLMとクラウド型AIアシスタントの長所を組み合わせる、非常に強力な開発パターンと言えるでしょう。