🐕

Opensearch MCPを動かしてみる

に公開

AIにコンテンツを作成させる際、リソース(いわゆる背景画像やicon、SEなど)の作成までAIにお願いできたらな~と怠惰が極まってきました。生成AIを使用して直接画像や音声を生成することもできますが、今回は既に存在する大量の素材からAI自身て適した素材を選択する手法をとりたいと思います。
Opensearchに素材データ(カテゴリや、素材自体の説明)を格納しておき、AIに自由に検索できるようにすればいい感じになるんじゃないの?というきっかけで、Opensearch MCPを試してみました。

Opensearch MCP

Introducing MCP in OpenSearchによると、Opensearch MCPにはBuilt-in MCP server型とStandalone OpenSearch MCP server型の2種類があります。今回はBuilt-in MCP server型を構築してみました。

  • Built-in MCP server型

  • Standalone OpenSearch MCP server型

環境

Windows 10のDockerコンテナ上にOpensearchサーバを構築します。
MCPの動作確認用のクライアントとしてClaude Desktopを使用します。

手順

Docker Desktopをインストールします。記事の本題とずれるため手順は省略します。

1. Opensearchの起動

Docker image opensearchproject/opensearch を使用してOpensesarchサーバーを起動します。

$ dcoker pull opensearchproject/opensearch

$ docker run -d \
  --name opensearch-node \
  -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e "plugins.security.disabled=true" \
  -e 'OPENSEARCH_INITIAL_ADMIN_PASSWORD=myStrongPassword123@456' \
  opensearchproject/opensearch:latest

パスワードは小大英字+数字+記号を含む必要があります。条件を満たさないとコンテナが起動しません。
また、今回はローカルで試すだけなのでHTTPプロトコルを許容するためplugins.security.disabled=trueを設定しています。

下記のような応答があれば起動完了です。

$ curl -X GET "http://localhost:9200"
{
  "name" : "4b7589d56f0a",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "9eWNe5L4TVK_OH62rV3mRg",
  "version" : {
    "distribution" : "opensearch",
    "number" : "3.1.0",
    "build_type" : "tar",
    "build_hash" : "8ff7c6ee924a49f0f59f80a6e1c73073c8904214",
    "build_date" : "2025-06-21T08:05:43.345081313Z",
    "build_snapshot" : false,
    "lucene_version" : "10.2.1",
    "minimum_wire_compatibility_version" : "2.19.0",
    "minimum_index_compatibility_version" : "2.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

2. データの投入

検索対象のサンプルデータを投入します。

sample-logsデータの投入
  OPENSEARCH_URL="http://localhost:9200"
  AUTH_USER="admin"
  AUTH_PASS="myStrongPassword123@456"

  # 1. インデックスの作成
  curl $CURL_OPTS -X PUT "$OPENSEARCH_URL/sample-logs" \
    -u "$AUTH_USER:$AUTH_PASS" \
    -H "Content-Type: application/json" \
    -d '{
      "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
      },
      "mappings": {
        "properties": {
          "timestamp": {
            "type": "date"
          },
          "level": {
            "type": "keyword"
          },
          "message": {
            "type": "text"
          },
          "service": {
            "type": "keyword"
          },
          "user_id": {
            "type": "integer"
          },
          "ip_address": {
            "type": "ip"
          }
        }
      }
    }'

  # 2. サンプルドキュメントの投入
  DOCUMENTS=(
    '{"timestamp":"2024-01-15T10:00:00Z","level":"INFO","message":"User login successful","service":"auth","user_id":1001,"ip_address":"192.168.1.100"}'
    '{"timestamp":"2024-01-15T10:05:00Z","level":"ERROR","message":"Database connection failed","service":"api","user_id":1002,"ip_address":"192.168.1.101"}'
    '{"timestamp":"2024-01-15T10:10:00Z","level":"WARN","message":"High memory usage detected","service":"worker","user_id":1003,"ip_address":"192.168.1.102"}'
    '{"timestamp":"2024-01-15T10:15:00Z","level":"INFO","message":"Cache cleared successfully","service":"cache","user_id":1001,"ip_address":"192.168.1.100"}'
    '{"timestamp":"2024-01-15T10:20:00Z","level":"DEBUG","message":"Processing user request","service":"api","user_id":1004,"ip_address":"192.168.1.103"}'
    '{"timestamp":"2024-01-15T10:25:00Z","level":"ERROR","message":"Payment processing failed","service":"payment","user_id":1005,"ip_address":"192.168.1.104"}'
    '{"timestamp":"2024-01-15T10:30:00Z","level":"INFO","message":"Backup completed","service":"backup","user_id":null,"ip_address":"192.168.1.200"}'
    '{"timestamp":"2024-01-15T10:35:00Z","level":"WARN","message":"Slow query detected","service":"database","user_id":1002,"ip_address":"192.168.1.101"}'
  )

  # 各ドキュメントを投入
  for i in "${!DOCUMENTS[@]}"; do
    echo "ドキュメント $((i+1)) を投入中..."
    curl $CURL_OPTS -X POST "$OPENSEARCH_URL/sample-logs/_doc" \
      -u "$AUTH_USER:$AUTH_PASS" \
      -H "Content-Type: application/json" \
      -d "${DOCUMENTS[$i]}"
    echo ""
  done
sample-productsデータの投入
  OPENSEARCH_URL="http://localhost:9200"
  AUTH_USER="admin"
  AUTH_PASS="myStrongPassword123@456"

curl $CURL_OPTS -X PUT "$OPENSEARCH_URL/sample-products" \
    -u "$AUTH_USER:$AUTH_PASS" \
    -H "Content-Type: application/json" \
    -d '{
      "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
      },
      "mappings": {
        "properties": {
          "name": {
            "type": "text",
            "analyzer": "standard"
          },
          "category": {
            "type": "keyword"
          },
          "price": {
            "type": "float"
          },
          "description": {
            "type": "text"
          },
          "in_stock": {
            "type": "boolean"
          },
          "tags": {
            "type": "keyword"
          },
          "created_at": {
            "type": "date"
          }
        }
      }
    }'

  PRODUCTS=(
    '{"name":"MacBook Pro","category":"electronics","price":2499.99,"description":"High-performance laptop for professionals","in_stock":true,"tags":["laptop","apple","professional"],"created_at":"2024-01-01T00:00:00Z"}'
    '{"name":"iPhone 15","category":"electronics","price":999.99,"description":"Latest smartphone with advanced features","in_stock":true,"tags":["smartphone","apple","mobile"],"created_at":"2024-01-02T00:00:00Z"}'
    '{"name":"Nike Air Max","category":"shoes","price":129.99,"description":"Comfortable running shoes","in_stock":false,"tags":["shoes","nike","running"],"created_at":"2024-01-03T00:00:00Z"}'
    '{"name":"Coffee Maker","category":"appliances","price":79.99,"description":"Automatic drip coffee maker","in_stock":true,"tags":["coffee","kitchen","appliance"],"created_at":"2024-01-04T00:00:00Z"}'
    '{"name":"Wireless Headphones","category":"electronics","price":199.99,"description":"Noise-cancelling wireless headphones","in_stock":true,"tags":["headphones","wireless","audio"],"created_at":"2024-01-05T00:00:00Z"}'
  )

  for i in "${!PRODUCTS[@]}"; do
    echo "商品 $((i+1)) を投入中..."
    curl $CURL_OPTS -X POST "$OPENSEARCH_URL/sample-products/_doc" \
      -u "$AUTH_USER:$AUTH_PASS" \
      -H "Content-Type: application/json" \
      -d "${PRODUCTS[$i]}"
    echo ""
  done

以下のコマンドで正常にデータが登録できているか確認します。

  curl $CURL_OPTS -X GET "$OPENSEARCH_URL/sample-logs/_count" \
    -u "$AUTH_USER:$AUTH_PASS"

  curl $CURL_OPTS -X GET "$OPENSEARCH_URL/sample-products/_count" \
    -u "$AUTH_USER:$AUTH_PASS"

3. MCP Toolの登録

MCPサーバーを有効にします。

curl -X PUT $OPENSEARCH_URL/_cluster/settings/ \
    -u "$AUTH_USER:$AUTH_PASS" \
    -H "Content-Type: application/json" \
    -d '{
        "persistent": {
            "plugins.ml_commons.mcp_server_enabled": "true"
        }
    }'

MCPツールを登録します。ツールは複数登録できます。今回はIndex内を検索してデータを取得して欲しいので、SearchIndexToolを登録します。

$ curl $CURL_OPTS -X POST "$OPENSEARCH_URL/_plugins/_ml/mcp/tools/_register" \
  -u "$AUTH_USER:$AUTH_PASS" \
  -H "Content-Type: application/json" \
  -d '{
  "tools": [
    {
          "name": "SearchIndexTool",
          "type": "SearchIndexTool",
          "description": "Use this tool to search an index by providing two parameters: 'index' for the index name, and 'query' for the OpenSearch DSL formatted query. Only use this tool when both index name and DSL query is available.",
          "attributes": {
            "input_schema": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "string"
                },
                "query": {
                  "type": "string"
                }
              },
              "additionalProperties": false
            }
          }
        }
  ]
}'

登録結果の確認

$ curl "$OPENSEARCH_URL/_plugins/_ml/mcp/tools/_list?pretty" -u "$AUTH_USER:$AUTH_PASS"

公式ドキュメントのRegister MCP toolsページのExample request: Zero-configuration toolsにツールのサンプルがあるので、気になった物を登録するとよいでしょう。

ちなみにIntroducing MCP in OpenSearchページに記載されている、以下のようなtype属性だけのツールは登録できませんでした。

POST /_plugins/_ml/mcp/tools/_register
{
    "tools": [
        {
            "type": "ListIndexTool"
        }
    ]
}

4. 動作確認

  • Claude Desktopの開発者モードを有効にします
  • %USER_HOME%\AppData\Roaming\Claude\claude_desktop_config.json を新規作成
  • claude_desktop_config.jsonを以下の通り編集してClaude Desktopを再起動
    claude_desktop_config.json
    {
        "mcpServers": {
            "opensearch-mcp-server": {
                "command": "uvx",
                "args": [
                    "test-opensearch-mcp"
                ],
                "env": {
                    "OPENSEARCH_URL": "http://localhost:9200",
                    "OPENSEARCH_USERNAME": "admin",
                    "OPENSEARCH_PASSWORD": "myStrongPassword123@456"
                }
            }
        }
    }
    

以下のようにMCPを使用してOpensesarchからデータを検索してくれるようになりました。



おわりに

今回はサンプルデータを投入しましたが、実際には素材データを投入し、たとえば「ピンボールゲームを作りたいので、ボールの素材に使えそうなものはありますか」と問いかけたら適切な素材を返してくれるようにしたいです。

Discussion