MCPとは? から、LLMの能力を最大限引き出す共通ルールMCPをPythonで自作し、Elasticsearchと連携させる具体的な手順を説明します。
LLMは万能じゃない?AI導入の壁
ChatGPTやClaudeなどの大規模言語モデル(LLM)は、私たちの日常業務に革命をもたらしています。しかし、こんな疑問を持ったことはありませんか?
- 「メールを送って」と頼んだのに、実際には送ってくれない
 - 検索はできるけど、社内データベースとのやり取りは難しい
 
そう、LLMはテキストを“予測”するだけのモデルに過ぎず、自分で“行動”する力は持っていません。
MCP(Model Context Protocol)とは?
MCPとは、LLMがさまざまな外部ツールと統一的に連携できるようにするための「共通ルール(プロトコル)」です。
ソフトウェア開発の世界では「標準」がとても重要です。たとえば「REST API」は、異なるシステム同士がスムーズに連携できる共通ルールとして広く使われています。
この“標準の考え方”をLLMにも適用したものが、MCPになります。
なぜ今、MCPが注目されているのか?
従来のようにツールごとにバラバラのAPIを組み合わせる必要はなく、MCPを通すことで統一された方法でさまざまなタスクを実行できます。
- ツール連携の仕組みがシンプルになり、開発効率が向上
 - コードの再利用性や保守性がアップ
 - セキュリティの統一管理が可能
 - LLMを実際の業務・サービスに活用しやすくなる
 
MCPエコシステム
MCPエコシステムは以下の要素で構成されています。
- MCPクライアント/ホスト:
- 例:Claude, Wind surf, Cursorなど。
 - 機能:エコシステムのクライアント側、すなわちLLM側となる部分です。
 
 - プロトコル:
- 機能:クライアントとサーバー間の双方向の接続を担います。
 - 場所:MCPクライアントとMCPサーバーの間に存在します。
 
 - MCPサーバー:
- 機能:外部サービス(その機能や実行可能なこと)をクライアントに翻訳して伝えます。
 
 - サービス:
- 機能:LLMがアクセスしたい外部の機能やデータソースそのものです(例:Elastic)。
 
 
MCPの仕組みを理解する:シーケンス図で見る処理の流れ
MCPを利用したLLMと外部サービスの連携は、以下のシーケンス図のように進行します。

【実践】MCPサーバーをPythonで構築しElasticsearchと連携する手順
ここからは、実際にMCPサーバーを構築し、Elasticsearchと連携する手順を示します。これにより、自然言語を用いたElasticsearchの操作が可能になります。
前提条件
- Macbook Pro M4チップ
 - VS code、Python が使える好みのIDE
 - Claude Desktop アプリをインストール済み(今回は無料枠)
 - Elasticsearch 環境
 
※MCPサーバー作成の公式クイックスタートを参考に進みますhttps://github.com/modelcontextprotocol/python-sdk
ステップ1: UVパッケージマネージャーの導入
uvをインストール。
※https://docs.astral.sh/uv/getting-started/installation/
curl -LsSf https://astral.sh/uv/install.sh | shインストール後、以下で確認:
which uv/usr/local/bin/uv などが出れば成功です。
ステップ2: MCPサーバーの作成
ターミナルを開き、以下のコマンドで新しいプロジェクト(mcp-elasticsearch)を初期化します。
uv init mcp-elasticsearch作成されたプロジェクトディレクトリに移動します。
cd mcp-elasticsearchMCP の CLI パッケージをプロジェクトの依存関係に追加します。
uv add "mcp[cli]"これで、MCP Python SDK がインストールされ、プロジェクトの準備が完了しました。
ステップ3: MCPサーバーのコードを書く
Python用のMCP公式SDKを参考に進みます。https://github.com/modelcontextprotocol/python-sdk
- mcp-elasticsearch プロジェクトを Visual Studio Code で開きます。
 - env ファイルを作成し、以下を記述。
 
ELASTIC_USER=elastic
ELASTIC_PASSWORD=****
ELASTIC_CLOUD_URL=https://your-deployment.aws.found.io- プロジェクトフォルダにあるmain.py の中身を以下のコードに置き換えます。
 
# =========================================
# Elasticsearch MCP Server for Claude
# =========================================
from mcp.server.fastmcp import FastMCP
from elasticsearch import Elasticsearch
from dotenv import load_dotenv
import os
# ----------------------
# 🔧 Setup
# ----------------------
print("Loading environment variables...")
load_dotenv()
# Start MCP server
mcp = FastMCP("ElasticsearchMCP")
# Connect to Elasticsearch
try:
    es = Elasticsearch(
        os.getenv("ELASTIC_CLOUD_URL"),
        basic_auth=(os.getenv("ELASTIC_USER"), os.getenv("ELASTIC_PASSWORD"))
    )
    print("🔗 Connected to Elasticsearch!")
except Exception as e:
    print("❌ Failed to connect:", e)
    es = None
# ----------------------
# 🛠️ MCP Tools
# ----------------------
@mcp.tool()
def list_indices() -> list:
    """Get all index names in the cluster."""
    # ES Query: GET /_cat/indices
    return list(es.indices.get_alias().keys()) if es else ["ES not initialized"]
@mcp.tool()
def get_cluster_health() -> dict:
    """Check the health status of the Elasticsearch cluster."""
    # ES Query: GET /_cluster/health
    return es.cluster.health() if es else {"error": "ES not initialized"}
@mcp.tool()
def count_documents(index_name: str) -> int:
    """Count how many documents exist in a given index."""
    # ES Query: GET /{index}/_count
    try:
        return es.count(index=index_name)['count'] if es else -1
    except Exception as e:
        return f"Error: {str(e)}"
@mcp.tool()
def search_index(index_name: str, keyword: str) -> list:
    """Search for documents in an index using a keyword."""
    # ES Query:
    # POST /{index}/_search
    # {
    #   "query": { "match": { "_all": "keyword" } }
    # }
    try:
        result = es.search(
            index=index_name,
            query={"match": {"_all": keyword}},
            size=5
        )
        return [doc["_source"] for doc in result["hits"]["hits"]]
    except Exception as e:
        return [f"Error: {str(e)}"]
@mcp.tool()
def get_index_mapping(index_name: str) -> dict:
    """Retrieve the field definitions (mappings) of an index."""
    # ES Query: GET /{index}/_mapping
    try:
        return es.indices.get_mapping(index=index_name)[index_name]["mappings"]
    except Exception as e:
        return {"error": str(e)}
@mcp.tool()
def delete_index(index_name: str) -> str:
    """Delete an entire index (irreversible)."""
    # ES Query: DELETE /{index}
    try:
        es.indices.delete(index=index_name)
        return f"✅ Deleted index '{index_name}'"
    except Exception as e:
        return f"❌ Error: {str(e)}"
@mcp.tool()
def get_recent_docs(index_name: str, size: int = 5) -> list:
    """Fetch the most recent documents from an index."""
    # ES Query:
    # POST /{index}/_search
    # {
    #   "size": 5,
    #   "sort": [{ "@timestamp": "desc" }]
    # }
    try:
        result = es.search(
            index=index_name,
            size=size,
            sort=[{"@timestamp": "desc"}]
        )
        return [doc["_source"] for doc in result["hits"]["hits"]]
    except Exception as e:
        return [f"Error: {str(e)}"]
# ----------------------
# Start MCP Server
# ----------------------
if __name__ == "__main__":
    print("🚀 Starting MCP server...")
    if os.environ.get("RUN_MCP_MANUALLY"):
        print("Running in manual mode (not Claude Desktop).")
    else:
        print("Waiting for Claude Desktop input (stdio mode).")
    try:
        mcp.run()
        print("✅ MCP server exited cleanly.")
    except Exception as e:
        print("❌ MCP server crashed:", e)
このコードでは、
- fast_mcp モジュールから FastMCP クラスをインポートしています。
 - ElasticsearchMCP という名前で MCP サーバーのインスタンスを作成しています。
 - @mcp.tool() デコレータを使って、七つの ツールを定義しています。ツールの説明(”””ここに入っている文章 “””)は AI がツールの中身を理解するために重要です。
 
list_indices()インデックス一覧の取得get_cluster_health()クラスタの状態確認count_documents(index)ドキュメント件数のカウント- search_index(index, keyword) キーワード検索
 get_index_mapping(index)マッピング取得delete_index(index)インデックス削除- get_recent_docs(index, size) 最新のドキュメント取得
 
ステップ4: Claude Desktop アプリのインストール
まだインストールしていない場合は、Claude の公式サイトから Claude Desktop アプリをダウンロードしてインストールしてください。
※Claude Desktop Download: https://claude.ai/download
MCPサーバーと連携させる前の画面↓
ステップ5: MCP サーバーの起動
- ターミナルで、mcp-elasticsearch ディレクトリにいることを確認します。
 - 以下のコマンドを実行して、MCP サーバーを起動します。
 
uv run mcp install main.pyサーバー追加されたメッセージを確認できます。
Claudeに設定項目が追加されました。Claude Desktopアプリを開いている場合は、一度終了し、20秒後に再度起動するとMCPサーバーが画面に表示されるようになります。問題が生じた際は、トラブルシューティングのセッションをご確認ください。
ステップ6: Claude Desktopとの連携設定
Claude Desktop アプリで、MCP サーバーが正しく認識されているか確認します。
Claudeの設定
- Claude Desktop アプリを開きます。
 - 「Claude」>「設定」>「開発者」に進みます。
 - 「構成を編集」をクリックし、設定ファイル
 
- ディレクトリが開きます。(claude-desktop-configuration.json) を好きなIDEで開きます。
 
- 設定ファイル内に、以下のような MCP サーバーの設定が記述されているか確認します。
 
{
  "mcpServers": {
    "ElasticsearchMCP": {
      "command": "/Users/sam-koyama/.local/bin/uv",
      "args": [
        "--directory",
        "/Users/sam-koyama/MCP/second/mcp-elasticsearch",
        "run",
        "main.py"
      ],
      "env": {
        "ELASTIC_USER": "elastic",
        "ELASTIC_PASSWORD": "3fik3ua3kXnbOlp0ThX41be1",
        "ELASTIC_CLOUD_URL": "https://my-deployment-a17327.es.ap-northeast-1.aws.found.io"
      }
    }
  }
}違う場合、トラブルシューティングの項を参照してください。
ツールの実行テスト
作成したツールを実際に Claude から実行してみましょう。
- Claude Desktop アプリを開始します。
 - チャットウィンドウ下部に表示されているハンマーアイコン(利用可能な MCP ツールを示します)をクリックします。
 
- 先ほど作成したMCPから添付ツール(プラグのアイコン)を選択すると、「連携サービス選択」で追加したプロジェクトが確認できます。
 
ステップ7: Claudeからの動作確認
Claudeのチャットウィンドウで下記の様なクエリーを投げます。
「blogsインデックスにあるドキュメントの数を教えて」
初回利用時には、アクセス許可を求められることがあるので、「Allow for this chat」を選択します。
その前に正解をKibanaのコンソールで確認しましょう。
ということで、応答は「7つ」であれば、MCP サーバーと Claude の連携は成功です!
トラブルシューティング:困ったときのヒント
もし MCP サーバーが Claude Desktop に表示されない、またはエラーが表示された場合は、以下の点を確認してみてください。
- 設定ファイルのパス確認: claude-desktop-configuration.json 内の command フィールドが、UV の絶対パス(例: /usr/local/bin/uv)になっているか確認してください。修正後はファイルを保存し、Claude Desktop を再起動します。
 - Claude Desktop の再起動: アプリを一度完全に終了し、再起動してみてください。
 - デスクトップフォルダへのアクセス許可: Claude Desktop がデスクトップフォルダへのアクセスを求めてきた場合は、必ず許可してください。
 
最後に
今回は、MCPの基本動作確認のため、Elasticsearch連携のシンプルなコードを使用しました。
現状のMCPには、サーバーセットアップの手作業やローカル設定の煩雑さといった技術的課題が見られますが、新しい技術であるため今後の発展が期待されます。

 
