MCPを使って機械学習してみた！

# はじめに
こんにちは。株式会社船井総合研究所でデータ分析を担当している平尾です。
今回は、MCPを用いて機械学習による計算を行ってみました。

scikit-learnのdiabetesデータを用いてXGBoostにより
予測モデルを作成し、それをMCPに組み込みました。

# モデルの準備
前回の記事([グリッドサーチとOptunaを実際に使って比較してみた！](https://qiita.com/hirao_takamichi/items/c0495133335afec9578b))と同じようにデータフレームを作成します。「データの準備」をご覧ください。


続いて、同記事の「パラメータ比較」の「Optuna」列に記載の通りに
モデルのパラメータを設定し、モデルを作成します。

```
import xgboost as xgb

model = xgb.XGBRegressor(
    objective='reg:squarederror',
    booster="gbtree",                               
    n_estimators=100,                                                                 
    learning_rate=0.068,
    max_depth=2,
    min_child_weight=4,
    subsample=0.22,
    colsample_bytree=0.85,
    reg_lambda=7,
)
model.fit(X,y)
pickle.dump(model, open(f"model.pickle", "wb"))
```

# MCP実行環境の準備
```
# Pythonプロジェクト作成
uv init hellomcp
cd hellomcp

# 仮装環境作成
uv venv
source .venv/bin/activate

# MCPサーバーのPython SDKをインストール
uv add "mcp[cli]"
```
※[MCPサーバーのテストツール"MCP Inspector"の使い方](https://www.bioerrorlog.work/entry/how-to-use-mcp-inspector)より引用
uvのインストールに関しては、[uvの使い方: Pythonパッケージ&プロジェクトマネージャー](https://www.bioerrorlog.work/entry/how-to-use-uv-python-manager)をご覧ください。

# MCPサーバーの構築
```
# simple_calculator_server.py
from mcp.server.fastmcp import FastMCP
import logging
import pickle
import pandas as pd
import xgboost

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# サーバーインスタンスの作成
mcp = FastMCP(
    name="calculator",
    description="糖尿病進行度を予測します",
)

@mcp.tool()
async def calculate(age: int, sex: str, bmi:float, bp:float, tc:int, ldl:float, hdl:float, tch:float, ltg:float, glu:int) -> float:
    
    # 男性の場合は1、女性の場合は2に変換
    if sex == '男性':
        sex = 1
    else:
        sex = 2

    # データの正規化
    age = (age-48.5180995)/13.1090278 
    sex = (sex-1.4683258)/0.4995612
    bmi = (bmi-26.3757919)/4.4181216
    bp = (bp-94.6470136)/13.8312834
    tc = (tc-189.1402715)/34.6080517
    ldl = (ldl-115.4391403)/30.4130810
    hdl = (hdl-49.7884615)/12.9342022
    tch = (tch-4.0702489)/1.2904499
    ltg = (ltg-4.6414109)/0.5223906
    glu = (glu-91.2601810)/11.4963347

    # 列名とデータ型を指定
    data = {
        "age":age,
        "sex": sex,
        "bmi": bmi,
        "bp": bp,
        "tc": tc,
        "ldl": ldl,
        "hdl":hdl,
        "tch":tch,
        "ltg":ltg,
        "glu":glu
    }

    # データフレームを作成
    index = [0]  
    df = pd.DataFrame(data, index=index)
    
    loaded_model = pickle.load(open("model.pickle", "rb"))
    prediction = loaded_model.predict(df)
    return prediction

if __name__ == "__main__":
    logger.info("糖尿病進行度を予測します")
    mcp.run()
```

コーディングは以下の記事を参考にしています。
[Part1 : Azure AI Foundry で MCPを使ってみた【深掘りと最新動向調査】](https://zenn.dev/chips0711/articles/e71b088f26f56a)
(Stdioサーバー実装例 (Python FastMCP))

また、データの正規化に用いる平均値、標準偏差に関しては、
[Diabetes data](https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html)の「Proc Means and Proc Print Output」のデータを用いています。

性別に関しては、元データが１,２というように表されており、それぞれの数字がどちらの性別を示すかは調べてもわかりませんでした。
そのため、ここでは男性を1、女性を2として設定しました。

toolの中身を説明します。
説明変数を入力するとそれぞれ標準化され、
説明変数のデータフレームが作成されます。

そのデータフレームがモデルに読み込まれ、
目的変数である糖尿病進行度が出力されます。

# MCPサーバーをMCP Inspectorで動かしてみる
まず、MCPサーバーを以下のように起動します。
```
mcp dev diabetes.py
```
そうすると、以下の画面がでてきます。
その中のhttpで始まるURLをクリックします。

![スクリーンショット 2025-05-20 135654.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/7b28ced2-01d8-4524-9633-d00e2c44fcfe.png)

ブラウザに以下の画面が開きます。
黒いボタン「Connect」をクリックします。
![スクリーンショット 2025-05-20 135815.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/62dbaa4d-a5bc-4400-8055-65246be2a9a0.png)

Toolsの赤丸のボタンをクリックします。

![スクリーンショット 2025-05-20 135954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/699d2120-e14c-43db-a3e9-8b7ba78b17a2.png)

赤丸の「calculate」をクリックします。
![スクリーンショット 2025-05-20 140155.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/5608803d-d7d1-40fe-a48a-481cb6a75692.png)

右側に説明変数を入力する画面が出てきます。
![スクリーンショット 2025-05-28 185527.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/fd4e9293-e913-4c5b-905d-959e9d7cf1a2.png)


項目を全て入力しRun Toolを押すと、計算結果が出力されます。
（ここでは、以下のように値を与えています。
age: 24, sex: 男性 ,bmi:30,bp:90.4, tc:200, ldl:120.9, hdl:78.3, tch:5.6, ltg:3.2, glu:80 ）

![スクリーンショット 2025-05-28 164026.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/00f8b0fc-bd51-432e-b4f7-f7ec564c7ae7.png)


# MCPサーバーをClaudeに接続
それでは、実際にMCPサーバーをClaudeに接続してみます。
Claudeに各説明変数を与えて、会話ベースで目的変数を出してもらうようにします。

まず、Pythonファイルの一部を書き換えます。
(参考：[リモートで動作する MCP Server を実装し、Claude アプリから呼び出してみる](https://engineering.mobalab.net/2025/04/21/connect-to-remote-mcp-server-from-claude-desktop-app-ja/))

書き換え前
```
if __name__ == "__main__":
    logger.info("糖尿病進行度を予測します")
    mcp.run()
```
書き換え後
```
def main():
    mcp.run(transport='sse')

if __name__ == "__main__":
    logger.info("糖尿病進行度を予測します")
    main()
```
MCPサーバーを以下のように起動します。(ファイル名を変更しました。)
```
python diabetes_sse.py  
```


Claudeを開き、左上の三本線のマークから「設定」をクリックします。
![スクリーンショット 2025-05-28 162149.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/34127858-9119-46f6-bfad-80e533c1527d.png)



「構成を編集」をクリックします。
![スクリーンショット 2025-05-28 161719.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/24082e04-c5f0-489e-b451-958694cc1118.png)

claude_desktop_config_jsonを開き、以下のように設定します。
(参考：[リモートで動作する MCP Server を実装し、Claude アプリから呼び出してみる](https://engineering.mobalab.net/2025/04/21/connect-to-remote-mcp-server-from-claude-desktop-app-ja/))
```
{
    "mcpServers": {
        "predict": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "http://localhost:8000/sse"
            ]
        }
    }
}
```
左上の三本線のマークから「終了」をクリックします。
![スクリーンショット 2025-05-28 162214.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/01535b72-7315-4227-a61d-91a7a27ab954.png)


再度Claudeを立ち上げると、設定画面が以下のようになっています。
![スクリーンショット 2025-05-28 163012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/3328de21-d3eb-4297-8c7c-a10bad9f2b4d.png)

さて、ようやく用意ができました。

以下のように指示を与えてみます。
(MCPInspectorで与えた値と同じです。)
「age: 24, sex: 男性 ,bmi:30,bp:90.4, tc:200, ldl:120.9, hdl:78.3, tch:5.6, ltg:3.2, glu:80 これらの値で予測値を出してください」
![スクリーンショット 2025-05-29 100825.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/e68093e3-3607-48e4-ad88-57b07652f5c5.png)


許可をしてください。
![スクリーンショット 2025-05-29 100912.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/fc1a1d83-3609-4e4d-9119-5b8c34c1d78a.png)


すると以下のように予測値を返してくれます。
![スクリーンショット 2025-05-29 100957.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/98d3e8c5-a687-4eb3-aa53-3e498f526aaf.png)

予測値は、先ほどのMCP Inspectorでの結果と一致しています。


では、「では40歳女性の場合はどうですか？」と追加で聞いてみます。
![スクリーンショット 2025-05-29 101212.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3992991/f5976525-1041-4e60-8f7c-dc777c62667e.png)
先ほど与えた値のうち、年齢と性別だけ変更して予測値を出してくれました。


# おわりに
実は、ClaudeとMCPを繋げる段階で非常に苦労しました。
claude_desktop_config.jsonをどう書き換えても、Claudeを再インストールしてもずっと繋がりませんでした。

実は、「[リモートで動作する MCP Server を実装し、Claude アプリから呼び出してみる](https://engineering.mobalab.net/2025/04/21/connect-to-remote-mcp-server-from-claude-desktop-app-ja/)」にある通り、今までの方法ではClaudeを MCP接続できなくなったようです。

そのため、上記サイトのclaude_desktop_config.jsonの書き方を試すまでは
繋がりませんでした。

(しかし、私の場合今でもチャット入力欄にトンカチマークはでてきません。MCP接続できるとトンカチマークが出てくるようです。)